diff options
Diffstat (limited to 'modules')
692 files changed, 28235 insertions, 11857 deletions
diff --git a/modules/SCsub b/modules/SCsub index 67f5893db4..36c2472c42 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -13,7 +13,7 @@ env.modules_sources = [ for x in env.module_list: if (x in env.disabled_modules): continue - env_modules.Append(CPPFLAGS=["-DMODULE_" + x.upper() + "_ENABLED"]) + env_modules.Append(CPPDEFINES=["MODULE_" + x.upper() + "_ENABLED"]) SConscript(x + "/SCsub") if env.split_modules: diff --git a/modules/arkit/SCsub b/modules/arkit/SCsub new file mode 100644 index 0000000000..b43d936768 --- /dev/null +++ b/modules/arkit/SCsub @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_arkit = env_modules.Clone() + +# Add source files +env_arkit.add_source_files(env.modules_sources, "*.cpp") +env_arkit.add_source_files(env.modules_sources, "*.mm") diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h new file mode 100644 index 0000000000..8129611287 --- /dev/null +++ b/modules/arkit/arkit_interface.h @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* arkit_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ARKIT_INTERFACE_H +#define ARKIT_INTERFACE_H + +#include "servers/arvr/arvr_interface.h" +#include "servers/arvr/arvr_positional_tracker.h" +#include "servers/camera/camera_feed.h" + +/** + @author Bastiaan Olij <mux213@gmail.com> + + ARKit interface between iPhone and Godot +*/ + +// forward declaration for some needed objects +class ARKitShader; + +class ARKitInterface : public ARVRInterface { + GDCLASS(ARKitInterface, ARVRInterface); + +private: + bool initialized; + bool session_was_started; + bool plane_detection_is_enabled; + bool light_estimation_is_enabled; + real_t ambient_intensity; + real_t ambient_color_temperature; + + Transform transform; + CameraMatrix projection; + float eye_height, z_near, z_far; + + Ref<CameraFeed> feed; + int image_width[2]; + int image_height[2]; + PoolVector<uint8_t> img_data[2]; + + struct anchor_map { + ARVRPositionalTracker *tracker; + unsigned char uuid[16]; + }; + + ///@TODO should use memory map object from Godot? + unsigned int num_anchors; + unsigned int max_anchors; + anchor_map *anchors; + ARVRPositionalTracker *get_anchor_for_uuid(const unsigned char *p_uuid); + void remove_anchor_for_uuid(const unsigned char *p_uuid); + void remove_all_anchors(); + +protected: + static void _bind_methods(); + +public: + void start_session(); + void stop_session(); + + bool get_anchor_detection_is_enabled() const; + void set_anchor_detection_is_enabled(bool p_enable); + virtual int get_camera_feed_id(); + + bool get_light_estimation_is_enabled() const; + void set_light_estimation_is_enabled(bool p_enable); + + real_t get_ambient_intensity() const; + real_t get_ambient_color_temperature() const; + + /* while Godot has its own raycast logic this takes ARKits camera into account and hits on any ARAnchor */ + Array raycast(Vector2 p_screen_coord); + + void notification(int p_what); + + virtual StringName get_name() const; + virtual int get_capabilities() const; + + virtual bool is_initialized() const; + virtual bool initialize(); + virtual void uninitialize(); + + virtual Size2 get_render_targetsize(); + virtual bool is_stereo(); + virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); + virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + + virtual void process(); + + // called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm) + void _add_or_update_anchor(void *p_anchor); + void _remove_anchor(void *p_anchor); + + ARKitInterface(); + ~ARKitInterface(); +}; + +#endif /* !ARKIT_INTERFACE_H */ diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm new file mode 100644 index 0000000000..de58f93276 --- /dev/null +++ b/modules/arkit/arkit_interface.mm @@ -0,0 +1,738 @@ +/*************************************************************************/ +/* arkit_interface.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_ios.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "scene/resources/surface_tool.h" +#include "servers/visual/visual_server_globals.h" + +#import <ARKit/ARKit.h> +#import <UIKit/UIKit.h> + +#include "arkit_interface.h" +#include "arkit_session_delegate.h" + +// just a dirty workaround for now, declare these as globals. I'll probably encapsulate ARSession and associated logic into an mm object and change ARKitInterface to a normal cpp object that consumes it. +ARSession *ar_session; +ARKitSessionDelegate *ar_delegate; +NSTimeInterval last_timestamp; + +/* this is called when we initialize or when we come back from having our app pushed to the background, just (re)start our session */ +void ARKitInterface::start_session() { + // We're active... + session_was_started = true; + + // Ignore this if we're not initialized... + if (initialized) { + print_line("Starting ARKit session"); + ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new]; + configuration.lightEstimationEnabled = light_estimation_is_enabled; + if (plane_detection_is_enabled) { + configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal; + } else { + configuration.planeDetection = 0; + } + + // make sure our camera is on + if (feed.is_valid()) { + feed->set_active(true); + } + + [ar_session runWithConfiguration:configuration]; + } +} + +void ARKitInterface::stop_session() { + session_was_started = false; + + // Ignore this if we're not initialized... + if (initialized) { + // make sure our camera is off + if (feed.is_valid()) { + feed->set_active(false); + } + + [ar_session pause]; + } +} + +void ARKitInterface::notification(int p_what) { + // TODO, this is not being called, need to find out why, possibly because this is not a node. + // in that case we need to find a way to get these notifications! + switch (p_what) { + case MainLoop::NOTIFICATION_WM_FOCUS_IN: { + print_line("Focus in"); + + start_session(); + }; break; + case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { + print_line("Focus out"); + + stop_session(); + }; break; + default: + break; + } +} + +bool ARKitInterface::get_anchor_detection_is_enabled() const { + return plane_detection_is_enabled; +} + +void ARKitInterface::set_anchor_detection_is_enabled(bool p_enable) { + if (plane_detection_is_enabled != p_enable) { + plane_detection_is_enabled = p_enable; + + // Restart our session (this will be ignore if we're not initialised) + if (session_was_started) { + start_session(); + } + } +} + +int ARKitInterface::get_camera_feed_id() { + if (feed.is_null()) { + return 0; + } else { + return feed->get_id(); + } +} + +bool ARKitInterface::get_light_estimation_is_enabled() const { + return light_estimation_is_enabled; +} + +void ARKitInterface::set_light_estimation_is_enabled(bool p_enable) { + if (light_estimation_is_enabled != p_enable) { + light_estimation_is_enabled = p_enable; + + // Restart our session (this will be ignore if we're not initialised) + if (session_was_started) { + start_session(); + } + } +} + +real_t ARKitInterface::get_ambient_intensity() const { + return ambient_intensity; +} + +real_t ARKitInterface::get_ambient_color_temperature() const { + return ambient_color_temperature; +} + +StringName ARKitInterface::get_name() const { + return "ARKit"; +} + +int ARKitInterface::get_capabilities() const { + return ARKitInterface::ARVR_MONO + ARKitInterface::ARVR_AR; +} + +Array ARKitInterface::raycast(Vector2 p_screen_coord) { + Array arr; + Size2 screen_size = OS::get_singleton()->get_window_size(); + CGPoint point; + point.x = p_screen_coord.x / screen_size.x; + point.y = p_screen_coord.y / screen_size.y; + + ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account + NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hittest:point types:ARHitTestResultTypeExistingPlaneUsingExtent]; + + for (ARHitTestResult *result in results) { + Transform transform; + + matrix_float4x4 m44 = result.worldTransform; + transform.basis.elements[0].x = m44.columns[0][0]; + transform.basis.elements[1].x = m44.columns[0][1]; + transform.basis.elements[2].x = m44.columns[0][2]; + transform.basis.elements[0].y = m44.columns[1][0]; + transform.basis.elements[1].y = m44.columns[1][1]; + transform.basis.elements[2].y = m44.columns[1][2]; + transform.basis.elements[0].z = m44.columns[2][0]; + transform.basis.elements[1].z = m44.columns[2][1]; + transform.basis.elements[2].z = m44.columns[2][2]; + transform.origin.x = m44.columns[3][0]; + transform.origin.y = m44.columns[3][1]; + transform.origin.z = m44.columns[3][2]; + + /* important, NOT scaled to world_scale !! */ + arr.push_back(transform); + } + + return arr; +} + +void ARKitInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("_notification", "what"), &ARKitInterface::_notification); + + ClassDB::bind_method(D_METHOD("set_light_estimation_is_enabled", "enable"), &ARKitInterface::set_light_estimation_is_enabled); + ClassDB::bind_method(D_METHOD("get_light_estimation_is_enabled"), &ARKitInterface::get_light_estimation_is_enabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_estimation"), "set_light_estimation_is_enabled", "get_light_estimation_is_enabled"); + + ClassDB::bind_method(D_METHOD("get_ambient_intensity"), &ARKitInterface::get_ambient_intensity); + ClassDB::bind_method(D_METHOD("get_ambient_color_temperature"), &ARKitInterface::get_ambient_color_temperature); + + ClassDB::bind_method(D_METHOD("raycast", "screen_coord"), &ARKitInterface::raycast); +} + +bool ARKitInterface::is_stereo() { + // this is a mono device... + return false; +} + +bool ARKitInterface::is_initialized() const { + return initialized; +} + +bool ARKitInterface::initialize() { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, false); + + if (!initialized) { + print_line("initializing ARKit"); + + // create our ar session and delegate + ar_session = [ARSession new]; + ar_delegate = [ARKitSessionDelegate new]; + ar_delegate.arkit_interface = this; + ar_session.delegate = ar_delegate; + + // reset our transform + transform = Transform(); + + // make this our primary interface + arvr_server->set_primary_interface(this); + + // make sure we have our feed setup + if (feed.is_null()) { + feed.instance(); + feed->set_name("ARKit"); + + CameraServer *cs = CameraServer::get_singleton(); + if (cs != NULL) { + cs->add_feed(feed); + } + } + feed->set_active(true); + + // yeah! + initialized = true; + + // Start our session... + start_session(); + } + + return true; +} + +void ARKitInterface::uninitialize() { + if (initialized) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if (arvr_server != NULL) { + // no longer our primary interface + arvr_server->clear_primary_interface_if(this); + } + + if (feed.is_valid()) { + CameraServer *cs = CameraServer::get_singleton(); + if ((cs != NULL)) { + cs->remove_feed(feed); + } + feed.unref(); + } + + remove_all_anchors(); + + [ar_session release]; + [ar_delegate release]; + ar_session = NULL; + ar_delegate = NULL; + initialized = false; + session_was_started = false; + } +} + +Size2 ARKitInterface::get_render_targetsize() { + _THREAD_SAFE_METHOD_ + + Size2 target_size = OS::get_singleton()->get_window_size(); + + return target_size; +} + +Transform ARKitInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { + _THREAD_SAFE_METHOD_ + + Transform transform_for_eye; + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, transform_for_eye); + + if (initialized) { + float world_scale = arvr_server->get_world_scale(); + + // just scale our origin point of our transform, note that we really shouldn't be using world_scale in ARKit but.... + transform_for_eye = transform; + transform_for_eye.origin *= world_scale; + + transform_for_eye = p_cam_transform * arvr_server->get_reference_frame() * transform_for_eye; + } else { + // huh? well just return what we got.... + transform_for_eye = p_cam_transform; + } + + return transform_for_eye; +} + +CameraMatrix ARKitInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + // Remember our near and far, it will be used in process when we obtain our projection from our ARKit session. + z_near = p_z_near; + z_far = p_z_far; + + return projection; +} + +void ARKitInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + _THREAD_SAFE_METHOD_ + + // We must have a valid render target + ERR_FAIL_COND(!p_render_target.is_valid()); + + // Because we are rendering to our device we must use our main viewport! + ERR_FAIL_COND(p_screen_rect == Rect2()); + + // get the size of our screen + Rect2 screen_rect = p_screen_rect; + + // screen_rect.position.x += screen_rect.size.x; + // screen_rect.size.x = -screen_rect.size.x; + // screen_rect.position.y += screen_rect.size.y; + // screen_rect.size.y = -screen_rect.size.y; + + VSG::rasterizer->set_current_render_target(RID()); + VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0); +} + +ARVRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) { + if (anchors == NULL) { + num_anchors = 0; + max_anchors = 10; + anchors = (anchor_map *)malloc(sizeof(anchor_map) * max_anchors); + } + + ERR_FAIL_NULL_V(anchors, NULL); + + for (unsigned int i = 0; i < num_anchors; i++) { + if (memcmp(anchors[i].uuid, p_uuid, 16) == 0) { + return anchors[i].tracker; + } + } + + if (num_anchors + 1 == max_anchors) { + max_anchors += 10; + anchors = (anchor_map *)realloc(anchors, sizeof(anchor_map) * max_anchors); + ERR_FAIL_NULL_V(anchors, NULL); + } + + ARVRPositionalTracker *new_tracker = memnew(ARVRPositionalTracker); + new_tracker->set_type(ARVRServer::TRACKER_ANCHOR); + + char tracker_name[256]; + sprintf(tracker_name, "Anchor %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", p_uuid[0], p_uuid[1], p_uuid[2], p_uuid[3], p_uuid[4], p_uuid[5], p_uuid[6], p_uuid[7], p_uuid[8], p_uuid[9], p_uuid[10], p_uuid[11], p_uuid[12], p_uuid[13], p_uuid[14], p_uuid[15]); + + String name = tracker_name; + print_line("Adding tracker " + name); + new_tracker->set_name(name); + + // add our tracker + ARVRServer::get_singleton()->add_tracker(new_tracker); + anchors[num_anchors].tracker = new_tracker; + memcpy(anchors[num_anchors].uuid, p_uuid, 16); + num_anchors++; + + return new_tracker; +} + +void ARKitInterface::remove_anchor_for_uuid(const unsigned char *p_uuid) { + if (anchors != NULL) { + for (unsigned int i = 0; i < num_anchors; i++) { + if (memcmp(anchors[i].uuid, p_uuid, 16) == 0) { + // remove our tracker + ARVRServer::get_singleton()->remove_tracker(anchors[i].tracker); + memdelete(anchors[i].tracker); + + // bring remaining forward + for (unsigned int j = i + 1; j < num_anchors; j++) { + anchors[j - 1] = anchors[j]; + }; + + // decrease count + num_anchors--; + return; + } + } + } +} + +void ARKitInterface::remove_all_anchors() { + if (anchors != NULL) { + for (unsigned int i = 0; i < num_anchors; i++) { + // remove our tracker + ARVRServer::get_singleton()->remove_tracker(anchors[i].tracker); + memdelete(anchors[i].tracker); + }; + + free(anchors); + anchors = NULL; + num_anchors = 0; + } +} + +void ARKitInterface::process() { + _THREAD_SAFE_METHOD_ + + if (@available(iOS 11.0, *)) { + if (initialized) { + // get our next ARFrame + ARFrame *current_frame = ar_session.currentFrame; + if (last_timestamp != current_frame.timestamp) { + // only process if we have a new frame + last_timestamp = current_frame.timestamp; + + // get some info about our screen and orientation + Size2 screen_size = OS::get_singleton()->get_window_size(); + UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + + // Grab our camera image for our backbuffer + CVPixelBufferRef pixelBuffer = current_frame.capturedImage; + if ((CVPixelBufferGetPlaneCount(pixelBuffer) == 2) && (feed != NULL)) { + // Plane 0 is our Y and Plane 1 is our CbCr buffer + + // ignored, we check each plane separately + // image_width = CVPixelBufferGetWidth(pixelBuffer); + // image_height = CVPixelBufferGetHeight(pixelBuffer); + + // printf("Pixel buffer %i - %i\n", image_width, image_height); + + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + + // get our buffers + unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + + if (dataY == NULL) { + print_line("Couldn't access Y pixel buffer data"); + } else if (dataCbCr == NULL) { + print_line("Couldn't access CbCr pixel buffer data"); + } else { + Ref<Image> img[2]; + size_t extraLeft, extraRight, extraTop, extraBottom; + + CVPixelBufferGetExtendedPixels(pixelBuffer, &extraLeft, &extraRight, &extraTop, &extraBottom); + + { + // do Y + int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + + if ((image_width[0] != new_width) || (image_height[0] != new_height)) { + printf("- Camera padding l:%lu r:%lu t:%lu b:%lu\n", extraLeft, extraRight, extraTop, extraBottom); + printf("- Camera Y plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row); + + image_width[0] = new_width; + image_height[0] = new_height; + img_data[0].resize(new_width * new_height); + } + + PoolVector<uint8_t>::Write w = img_data[0].write(); + if (new_width == bytes_per_row) { + memcpy(w.ptr(), dataY, new_width * new_height); + } else { + int offset_a = 0; + int offset_b = extraLeft + (extraTop * bytes_per_row); + for (int r = 0; r < new_height; r++) { + memcpy(w.ptr() + offset_a, dataY + offset_b, new_width); + offset_a += new_width; + offset_b += bytes_per_row; + } + } + + img[0].instance(); + img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); + } + + { + // do CbCr + int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); + int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); + int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + + if ((image_width[1] != new_width) || (image_height[1] != new_height)) { + printf("- Camera CbCr plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row); + + image_width[1] = new_width; + image_height[1] = new_height; + img_data[1].resize(2 * new_width * new_height); + } + + PoolVector<uint8_t>::Write w = img_data[1].write(); + if ((2 * new_width) == bytes_per_row) { + memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height); + } else { + int offset_a = 0; + int offset_b = extraLeft + (extraTop * bytes_per_row); + for (int r = 0; r < new_height; r++) { + memcpy(w.ptr() + offset_a, dataCbCr + offset_b, 2 * new_width); + offset_a += 2 * new_width; + offset_b += bytes_per_row; + } + } + + img[1].instance(); + img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); + } + + // set our texture... + feed->set_YCbCr_imgs(img[0], img[1]); + + // now build our transform to display this as a background image that matches our camera + CGAffineTransform affine_transform = [current_frame displayTransformForOrientation:orientation viewportSize:CGSizeMake(screen_size.width, screen_size.height)]; + + // we need to invert this, probably row v.s. column notation + affine_transform = CGAffineTransformInvert(affine_transform); + + if (orientation != UIDeviceOrientationPortrait) { + affine_transform.b = -affine_transform.b; + affine_transform.d = -affine_transform.d; + affine_transform.ty = 1.0 - affine_transform.ty; + } else { + affine_transform.c = -affine_transform.c; + affine_transform.a = -affine_transform.a; + affine_transform.tx = 1.0 - affine_transform.tx; + } + + Transform2D display_transform = Transform2D( + affine_transform.a, affine_transform.b, + affine_transform.c, affine_transform.d, + affine_transform.tx, affine_transform.ty); + + feed->set_transform(display_transform); + } + + // and unlock + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + } + + // Record light estimation to apply to our scene + if (light_estimation_is_enabled) { + ambient_intensity = current_frame.lightEstimate.ambientIntensity; + + ///@TODO it's there, but not there.. what to do with this... + // https://developer.apple.com/documentation/arkit/arlightestimate?language=objc + // ambient_color_temperature = current_frame.lightEstimate.ambientColorTemperature; + } + + // Process our camera + ARCamera *camera = current_frame.camera; + + // strangely enough we have to states, rolling them up into one + if (camera.trackingState == ARTrackingStateNotAvailable) { + // no tracking, would be good if we black out the screen or something... + tracking_state = ARVRInterface::ARVR_NOT_TRACKING; + } else { + if (camera.trackingState == ARTrackingStateNormal) { + tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + } else if (camera.trackingStateReason == ARTrackingStateReasonExcessiveMotion) { + tracking_state = ARVRInterface::ARVR_EXCESSIVE_MOTION; + } else if (camera.trackingStateReason == ARTrackingStateReasonInsufficientFeatures) { + tracking_state = ARVRInterface::ARVR_INSUFFICIENT_FEATURES; + } else { + tracking_state = ARVRInterface::ARVR_UNKNOWN_TRACKING; + } + + // copy our current frame transform + matrix_float4x4 m44 = camera.transform; + if (orientation == UIDeviceOrientationLandscapeLeft) { + transform.basis.elements[0].x = m44.columns[0][0]; + transform.basis.elements[1].x = m44.columns[0][1]; + transform.basis.elements[2].x = m44.columns[0][2]; + transform.basis.elements[0].y = m44.columns[1][0]; + transform.basis.elements[1].y = m44.columns[1][1]; + transform.basis.elements[2].y = m44.columns[1][2]; + } else if (orientation == UIDeviceOrientationPortrait) { + transform.basis.elements[0].x = m44.columns[1][0]; + transform.basis.elements[1].x = m44.columns[1][1]; + transform.basis.elements[2].x = m44.columns[1][2]; + transform.basis.elements[0].y = -m44.columns[0][0]; + transform.basis.elements[1].y = -m44.columns[0][1]; + transform.basis.elements[2].y = -m44.columns[0][2]; + } else if (orientation == UIDeviceOrientationLandscapeRight) { + transform.basis.elements[0].x = -m44.columns[0][0]; + transform.basis.elements[1].x = -m44.columns[0][1]; + transform.basis.elements[2].x = -m44.columns[0][2]; + transform.basis.elements[0].y = -m44.columns[1][0]; + transform.basis.elements[1].y = -m44.columns[1][1]; + transform.basis.elements[2].y = -m44.columns[1][2]; + } else if (orientation == UIDeviceOrientationPortraitUpsideDown) { + // this may not be correct + transform.basis.elements[0].x = m44.columns[1][0]; + transform.basis.elements[1].x = m44.columns[1][1]; + transform.basis.elements[2].x = m44.columns[1][2]; + transform.basis.elements[0].y = m44.columns[0][0]; + transform.basis.elements[1].y = m44.columns[0][1]; + transform.basis.elements[2].y = m44.columns[0][2]; + } + transform.basis.elements[0].z = m44.columns[2][0]; + transform.basis.elements[1].z = m44.columns[2][1]; + transform.basis.elements[2].z = m44.columns[2][2]; + transform.origin.x = m44.columns[3][0]; + transform.origin.y = m44.columns[3][1]; + transform.origin.z = m44.columns[3][2]; + + // copy our current frame projection, investigate using projectionMatrixWithViewportSize:orientation:zNear:zFar: so we can set our own near and far + m44 = [camera projectionMatrixForOrientation:orientation viewportSize:CGSizeMake(screen_size.width, screen_size.height) zNear:z_near zFar:z_far]; + projection.matrix[0][0] = m44.columns[0][0]; + projection.matrix[1][0] = m44.columns[1][0]; + projection.matrix[2][0] = m44.columns[2][0]; + projection.matrix[3][0] = m44.columns[3][0]; + projection.matrix[0][1] = m44.columns[0][1]; + projection.matrix[1][1] = m44.columns[1][1]; + projection.matrix[2][1] = m44.columns[2][1]; + projection.matrix[3][1] = m44.columns[3][1]; + projection.matrix[0][2] = m44.columns[0][2]; + projection.matrix[1][2] = m44.columns[1][2]; + projection.matrix[2][2] = m44.columns[2][2]; + projection.matrix[3][2] = m44.columns[3][2]; + projection.matrix[0][3] = m44.columns[0][3]; + projection.matrix[1][3] = m44.columns[1][3]; + projection.matrix[2][3] = m44.columns[2][3]; + projection.matrix[3][3] = m44.columns[3][3]; + } + } + } + } +} + +void ARKitInterface::_add_or_update_anchor(void *p_anchor) { + _THREAD_SAFE_METHOD_ + + ARAnchor *anchor = (ARAnchor *)p_anchor; + + unsigned char uuid[16]; + [anchor.identifier getUUIDBytes:uuid]; + + ARVRPositionalTracker *tracker = get_anchor_for_uuid(uuid); + if (tracker != NULL) { + // lets update our mesh! (using Arjens code as is for now) + // we should also probably limit how often we do this... + + // can we safely cast this? + ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor; + + if (planeAnchor.geometry.triangleCount > 0) { + Ref<SurfaceTool> surftool; + surftool.instance(); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) { + int16_t index = planeAnchor.geometry.triangleIndices[j]; + simd_float3 vrtx = planeAnchor.geometry.vertices[index]; + simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index]; + surftool->add_uv(Vector2(textcoord[0], textcoord[1])); + surftool->add_color(Color(0.8, 0.8, 0.8)); + surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2])); + } + + surftool->generate_normals(); + tracker->set_mesh(surftool->commit()); + } else { + Ref<Mesh> nomesh; + tracker->set_mesh(nomesh); + } + + // Note, this also contains a scale factor which gives us an idea of the size of the anchor + // We may extract that in our ARVRAnchor class + Basis b; + matrix_float4x4 m44 = anchor.transform; + b.elements[0].x = m44.columns[0][0]; + b.elements[1].x = m44.columns[0][1]; + b.elements[2].x = m44.columns[0][2]; + b.elements[0].y = m44.columns[1][0]; + b.elements[1].y = m44.columns[1][1]; + b.elements[2].y = m44.columns[1][2]; + b.elements[0].z = m44.columns[2][0]; + b.elements[1].z = m44.columns[2][1]; + b.elements[2].z = m44.columns[2][2]; + tracker->set_orientation(b); + tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2])); + } +} + +void ARKitInterface::_remove_anchor(void *p_anchor) { + _THREAD_SAFE_METHOD_ + + ARAnchor *anchor = (ARAnchor *)p_anchor; + + unsigned char uuid[16]; + [anchor.identifier getUUIDBytes:uuid]; + + remove_anchor_for_uuid(uuid); +} + +ARKitInterface::ARKitInterface() { + initialized = false; + session_was_started = false; + plane_detection_is_enabled = false; + light_estimation_is_enabled = false; + ar_session = NULL; + z_near = 0.01; + z_far = 1000.0; + projection.set_perspective(60.0, 1.0, z_near, z_far, false); + anchors = NULL; + num_anchors = 0; + ambient_intensity = 1.0; + ambient_color_temperature = 1.0; + image_width[0] = 0; + image_width[1] = 0; + image_height[0] = 0; + image_height[1] = 0; +} + +ARKitInterface::~ARKitInterface() { + remove_all_anchors(); + + // and make sure we cleanup if we haven't already + if (is_initialized()) { + uninitialize(); + } +} diff --git a/modules/arkit/arkit_session_delegate.h b/modules/arkit/arkit_session_delegate.h new file mode 100644 index 0000000000..afe093656b --- /dev/null +++ b/modules/arkit/arkit_session_delegate.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* arkit_session_delegate.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ARKIT_SESSION_DELEGATE_H +#define ARKIT_SESSION_DELEGATE_H + +#import <ARKit/ARKit.h> +#import <UIKit/UIKit.h> + +class ARKitInterface; + +@interface ARKitSessionDelegate : NSObject <ARSessionDelegate> { + ARKitInterface *arkit_interface; +} + +@property(nonatomic) ARKitInterface *arkit_interface; + +- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors; +- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors; +- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors; +@end + +#endif /* !ARKIT_SESSION_DELEGATE_H */ diff --git a/modules/arkit/arkit_session_delegate.mm b/modules/arkit/arkit_session_delegate.mm new file mode 100644 index 0000000000..56485c987c --- /dev/null +++ b/modules/arkit/arkit_session_delegate.mm @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* arkit_session_delegate.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "arkit_session_delegate.h" +#include "arkit_interface.h" + +@implementation ARKitSessionDelegate + +@synthesize arkit_interface; + +- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors { + for (ARAnchor *anchor in anchors) { + arkit_interface->_add_or_update_anchor(anchor); + } +} + +- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors { + for (ARAnchor *anchor in anchors) { + arkit_interface->_remove_anchor(anchor); + } +} + +- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors { + for (ARAnchor *anchor in anchors) { + arkit_interface->_add_or_update_anchor(anchor); + } +} + +@end
\ No newline at end of file diff --git a/modules/arkit/config.py b/modules/arkit/config.py new file mode 100644 index 0000000000..96e41826c5 --- /dev/null +++ b/modules/arkit/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return platform == 'iphone' + +def configure(env): + pass diff --git a/modules/arkit/register_types.cpp b/modules/arkit/register_types.cpp new file mode 100644 index 0000000000..af35828004 --- /dev/null +++ b/modules/arkit/register_types.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "arkit_interface.h" + +void register_arkit_types() { + // does it make sense to register the class? + + Ref<ARKitInterface> arkit_interface; + arkit_interface.instance(); + ARVRServer::get_singleton()->add_interface(arkit_interface); +} + +void unregister_arkit_types() { + // should clean itself up nicely :) +} diff --git a/modules/thekla_unwrap/register_types.h b/modules/arkit/register_types.h index 8a0eab9437..6ed2065de2 100644 --- a/modules/thekla_unwrap/register_types.h +++ b/modules/arkit/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,5 +28,5 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -void register_thekla_unwrap_types(); -void unregister_thekla_unwrap_types(); +void register_arkit_types(); +void unregister_arkit_types(); diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub new file mode 100644 index 0000000000..8a77e4f803 --- /dev/null +++ b/modules/assimp/SCsub @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_assimp = env_modules.Clone() +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/include']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code/Importer/IFC']) +env_assimp.Prepend(CPPPATH=['#thirdparty/misc']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/irrXML/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/unzip/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code/Importer/STEPParser']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/zlib/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/openddlparser/include']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/rapidjson/include']) +env_assimp.Prepend(CPPPATH=['.']) +#env_assimp.Append(CPPDEFINES=['ASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_BOOST_WORKAROUND']) +env_assimp.Append(CPPDEFINES=['OPENDDLPARSER_BUILD']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_OWN_ZLIB']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_EXPORT']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_X_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_AMF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_3DS_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MD3_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MD5_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MDL_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MD2_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_PLY_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_ASE_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_OBJ_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_HMP_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_SMD_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MDC_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MD5_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_STL_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_LWO_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_DXF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_NFF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_RAW_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_SIB_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_OFF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_AC_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_BVH_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_IRRMESH_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_IRR_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_Q3D_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_B3D_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_COLLADA_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_TERRAGEN_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_CSM_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_3D_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_LWS_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_OGRE_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_OPENGEX_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_MS3D_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_COB_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_BLEND_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_Q3BSP_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_NDO_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_STEP_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_IFC_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_XGL_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_ASSBIN_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_GLTF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_C4D_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_3MF_IMPORTER']) +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_NO_X3D_IMPORTER']) + +env_assimp.Append(CPPDEFINES=['ASSIMP_BUILD_SINGLETHREADED']) + +if (not env.msvc): + env_assimp.Append(CXXFLAGS=['-std=c++11']) +elif (env.msvc == False and env['platform'] == 'windows'): + env_assimp.Append(LDFLAGS=['-pthread']) + +if(env['platform'] == 'windows'): + env_assimp.Append(CPPDEFINES=['PLATFORM_WINDOWS']) + env_assimp.Append(CPPDEFINES=[('PLATFORM', 'WINDOWS')]) +elif(env['platform'] == 'x11'): + env_assimp.Append(CPPDEFINES=['PLATFORM_LINUX']) + env_assimp.Append(CPPDEFINES=[('PLATFORM', 'LINUX')]) +elif(env['platform'] == 'osx'): + env_assimp.Append(CPPDEFINES=['PLATFORM_DARWIN']) + env_assimp.Append(CPPDEFINES=[('PLATFORM', 'DARWIN')]) + +env_thirdparty = env_assimp.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, Glob('#thirdparty/assimp/code/*.cpp')) + +# Godot's own source files +env_assimp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/assimp/config.py b/modules/assimp/config.py new file mode 100644 index 0000000000..098f1eafa9 --- /dev/null +++ b/modules/assimp/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return env['tools'] + +def configure(env): + pass diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp new file mode 100644 index 0000000000..f23c66dbcf --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -0,0 +1,1764 @@ +/*************************************************************************/ +/* editor_scene_importer_assimp.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "assimp/DefaultLogger.hpp" +#include "assimp/Importer.hpp" +#include "assimp/LogStream.hpp" +#include "assimp/Logger.hpp" +#include "assimp/SceneCombiner.h" +#include "assimp/cexport.h" +#include "assimp/cimport.h" +#include "assimp/matrix4x4.h" +#include "assimp/pbrmaterial.h" +#include "assimp/postprocess.h" +#include "assimp/scene.h" + +#include "core/bind/core_bind.h" +#include "core/io/image_loader.h" +#include "editor/editor_file_system.h" +#include "editor/import/resource_importer_scene.h" +#include "editor_scene_importer_assimp.h" +#include "editor_settings.h" +#include "scene/3d/camera.h" +#include "scene/3d/light.h" +#include "scene/3d/mesh_instance.h" +#include "scene/animation/animation_player.h" +#include "scene/main/node.h" +#include "scene/resources/material.h" +#include "scene/resources/surface_tool.h" +#include "zutil.h" +#include <string> + +void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const { + + const String import_setting_string = "filesystem/import/open_asset_import/"; + + Map<String, ImportFormat> import_format; + { + Vector<String> exts; + exts.push_back("fbx"); + ImportFormat import = { exts, true }; + import_format.insert("fbx", import); + } + { + Vector<String> exts; + exts.push_back("pmx"); + ImportFormat import = { exts, true }; + import_format.insert("mmd", import); + } + for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) { + _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions, E->get().is_default); + } +} + +void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const { + const String use_generic = "use_" + generic; + _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); + if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { + for (int32_t i = 0; i < exts.size(); i++) { + r_extensions->push_back(exts[i]); + } + } +} + +uint32_t EditorSceneImporterAssimp::get_import_flags() const { + return IMPORT_SCENE; +} + +AssimpStream::AssimpStream() { + // empty +} + +AssimpStream::~AssimpStream() { + // empty +} + +void AssimpStream::write(const char *message) { + print_verbose(String("Open Asset Import: ") + String(message).strip_edges()); +} + +void EditorSceneImporterAssimp::_bind_methods() { +} + +Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { + Assimp::Importer importer; + std::wstring w_path = ProjectSettings::get_singleton()->globalize_path(p_path).c_str(); + std::string s_path(w_path.begin(), w_path.end()); + importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); + // Cannot remove pivot points because the static mesh will be in the wrong place + importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); + int32_t max_bone_weights = 4; + //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) { + // const int eight_bones = 8; + // importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones); + // max_bone_weights = eight_bones; + //} + + importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); + //importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f); + int32_t post_process_Steps = aiProcess_CalcTangentSpace | + //aiProcess_FlipUVs | + //aiProcess_FlipWindingOrder | + //aiProcess_DropNormals | + //aiProcess_GenSmoothNormals | + aiProcess_JoinIdenticalVertices | + aiProcess_ImproveCacheLocality | + aiProcess_LimitBoneWeights | + //aiProcess_RemoveRedundantMaterials | // Causes a crash + aiProcess_SplitLargeMeshes | + aiProcess_Triangulate | + aiProcess_GenUVCoords | + //aiProcess_FindDegenerates | + aiProcess_SortByPType | + aiProcess_FindInvalidData | + aiProcess_TransformUVCoords | + aiProcess_FindInstances | + //aiProcess_FixInfacingNormals | + //aiProcess_ValidateDataStructure | + aiProcess_OptimizeMeshes | + //aiProcess_OptimizeGraph | + //aiProcess_Debone | + aiProcess_EmbedTextures | + aiProcess_SplitByBoneCount | + 0; + const aiScene *scene = importer.ReadFile(s_path.c_str(), + post_process_Steps); + ERR_EXPLAIN(String("Open Asset Import failed to open: ") + String(importer.GetErrorString())); + ERR_FAIL_COND_V(scene == NULL, NULL); + return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights); +} + +template <class T> +struct EditorSceneImporterAssetImportInterpolate { + + T lerp(const T &a, const T &b, float c) const { + + return a + (b - a) * c; + } + + T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { + + float t2 = t * t; + float t3 = t2 * t; + + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + T bezier(T start, T control_1, T control_2, T end, float t) { + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = t * t; + real_t t3 = t2 * t; + + return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; + } +}; + +//thank you for existing, partial specialization +template <> +struct EditorSceneImporterAssetImportInterpolate<Quat> { + + Quat lerp(const Quat &a, const Quat &b, float c) const { + ERR_FAIL_COND_V(!a.is_normalized(), Quat()); + ERR_FAIL_COND_V(!b.is_normalized(), Quat()); + + return a.slerp(b, c).normalized(); + } + + Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { + ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); + ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); + + return p1.slerp(p2, c).normalized(); + } + + Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { + ERR_FAIL_COND_V(!start.is_normalized(), Quat()); + ERR_FAIL_COND_V(!end.is_normalized(), Quat()); + + return start.slerp(end, t).normalized(); + } +}; + +template <class T> +T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp) { + //could use binary search, worth it? + int idx = -1; + for (int i = 0; i < p_times.size(); i++) { + if (p_times[i] > p_time) + break; + idx++; + } + + EditorSceneImporterAssetImportInterpolate<T> interp; + + switch (p_interp) { + case AssetImportAnimation::INTERP_LINEAR: { + + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.lerp(p_values[idx], p_values[idx + 1], c); + + } break; + case AssetImportAnimation::INTERP_STEP: { + + if (idx == -1) { + return p_values[0]; + } else if (idx >= p_times.size() - 1) { + return p_values[p_times.size() - 1]; + } + + return p_values[idx]; + + } break; + case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { + + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[1 + p_times.size() - 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); + + } break; + case AssetImportAnimation::INTERP_CUBIC_SPLINE: { + + if (idx == -1) { + return p_values[1]; + } else if (idx >= p_times.size() - 1) { + return p_values[(p_times.size() - 1) * 3 + 1]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + T from = p_values[idx * 3 + 1]; + T c1 = from + p_values[idx * 3 + 2]; + T to = p_values[idx * 3 + 4]; + T c2 = to + p_values[idx * 3 + 3]; + + return interp.bezier(from, c1, c2, to, c); + + } break; + } + + ERR_FAIL_V(p_values[0]); +} + +void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms) { + + Transform mesh_offset = _get_global_assimp_node_transform(p_assimp_node); + //mesh_offset.basis = Basis(); + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + const aiMesh *mesh = state.assimp_scene->mMeshes[i]; + int owned_by = -1; + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + + if (ownership.has(name)) { + owned_by = ownership[name]; + break; + } + } + + if (owned_by == -1) { //no owned, create new unique id + owned_by = 1; + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + owned_by = MAX(E->get() + 1, owned_by); + } + } + + for (uint32_t j = 0; j < mesh->mNumBones; j++) { + const aiBone *bone = mesh->mBones[j]; + String name = _assimp_get_string(bone->mName); + ownership[name] = owned_by; + //store the actual full path for the bone transform + //when skeleton finds its place in the tree, it will be restored + bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix); + } + } + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_bone_groups(state, p_assimp_node->mChildren[i], ownership, bind_xforms); + } +} + +void EditorSceneImporterAssimp::_fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms) { + + String name = _assimp_get_string(p_assimp_node->mName); + if (name == String()) { + name = "AuxiliaryBone" + itos(holecount++); + } + + Transform pose = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (!ownership.has(name)) { + //not a bone, it's a hole + Vector<SkeletonHole> holes = p_holes; + SkeletonHole hole; //add a new one + hole.name = name; + hole.pose = pose; + hole.node = p_assimp_node; + hole.parent = p_parent_name; + holes.push_back(hole); + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, holes, bind_xforms); + } + + return; + } else if (ownership[name] != p_skeleton_id) { + //oh, it's from another skeleton? fine.. reparent all bones to this skeleton. + int prev_owner = ownership[name]; + ERR_EXPLAIN("A previous skeleton exists for bone '" + name + "', this type of skeleton layout is unsupported."); + ERR_FAIL_COND(skeleton_map.has(prev_owner)); + for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) { + if (E->get() == prev_owner) { + E->get() = p_skeleton_id; + } + } + } + + //valid bone, first fill holes if needed + for (int i = 0; i < p_holes.size(); i++) { + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(p_holes[i].name); + int parent_idx = p_skeleton->find_bone(p_holes[i].parent); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); + } + + Transform pose_transform = _get_global_assimp_node_transform(p_holes[i].node); + p_skeleton->set_bone_rest(bone_idx, pose_transform); + + state.bone_owners[p_holes[i].name] = skeleton_map[p_skeleton_id]; + } + + //finally fill bone + + int bone_idx = p_skeleton->get_bone_count(); + p_skeleton->add_bone(name); + int parent_idx = p_skeleton->find_bone(p_parent_name); + if (parent_idx >= 0) { + p_skeleton->set_bone_parent(bone_idx, parent_idx); + } + //p_skeleton->set_bone_pose(bone_idx, pose); + if (bind_xforms.has(name)) { + //for now this is the full path to the bone in rest pose + //when skeleton finds it's place in the tree, it will get fixed + p_skeleton->set_bone_rest(bone_idx, bind_xforms[name]); + } + state.bone_owners[name] = skeleton_map[p_skeleton_id]; + //go to children + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, Vector<SkeletonHole>(), bind_xforms); + } +} + +void EditorSceneImporterAssimp::_generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms) { + + //find skeletons at this level, there may be multiple root nodes for each + Map<int, List<aiNode *> > skeletons_found; + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + int skeleton = ownership[name]; + if (!skeletons_found.has(skeleton)) { + skeletons_found[skeleton] = List<aiNode *>(); + } + skeletons_found[skeleton].push_back(p_assimp_node->mChildren[i]); + } + } + + //go via the potential skeletons found and generate the actual skeleton + for (Map<int, List<aiNode *> >::Element *E = skeletons_found.front(); E; E = E->next()) { + ERR_CONTINUE(skeleton_map.has(E->key())); //skeleton already exists? this can't be.. skip + Skeleton *skeleton = memnew(Skeleton); + //this the only way to reliably use multiple meshes with one skeleton, at the cost of less precision + skeleton->set_use_bones_in_world_transform(true); + skeleton_map[E->key()] = state.skeletons.size(); + state.skeletons.push_back(skeleton); + int holecount = 1; + //fill the bones and their relationships + for (List<aiNode *>::Element *F = E->get().front(); F; F = F->next()) { + _fill_node_relationships(state, F->get(), ownership, skeleton_map, E->key(), skeleton, "", holecount, Vector<SkeletonHole>(), bind_xforms); + } + } + + //go to the children + for (uint32_t i = 0; i < p_assimp_node->mNumChildren; i++) { + String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName); + if (ownership.has(name)) { + continue; //a bone, so don't bother with this + } + _generate_skeletons(state, p_assimp_node->mChildren[i], ownership, skeleton_map, bind_xforms); + } +} + +Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) { + ERR_FAIL_COND_V(scene == NULL, NULL); + + ImportState state; + state.path = p_path; + state.assimp_scene = scene; + state.max_bone_weights = p_max_bone_weights; + state.root = memnew(Spatial); + state.fbx = false; + state.animation_player = NULL; + + real_t scale_factor = 1.0f; + { + //handle scale + String ext = p_path.get_file().get_extension().to_lower(); + if (ext == "fbx") { + if (scene->mMetaData != NULL) { + float factor = 1.0; + scene->mMetaData->Get("UnitScaleFactor", factor); + scale_factor = factor * 0.01f; + } + state.fbx = true; + } + } + + state.root->set_scale(Vector3(scale_factor, scale_factor, scale_factor)); + + //fill light map cache + for (size_t l = 0; l < scene->mNumLights; l++) { + + aiLight *ai_light = scene->mLights[l]; + ERR_CONTINUE(ai_light == NULL); + state.light_cache[_assimp_get_string(ai_light->mName)] = l; + } + + //fill camera cache + for (size_t c = 0; c < scene->mNumCameras; c++) { + aiCamera *ai_camera = scene->mCameras[c]; + ERR_CONTINUE(ai_camera == NULL); + state.camera_cache[_assimp_get_string(ai_camera->mName)] = c; + } + + if (scene->mRootNode) { + Map<String, Transform> bind_xforms; //temporary map to store bind transforms + //guess the skeletons, since assimp does not really support them directly + Map<String, int> ownership; //bone names to groups + //fill this map with bone names and which group where they detected to, going mesh by mesh + _generate_bone_groups(state, state.assimp_scene->mRootNode, ownership, bind_xforms); + Map<int, int> skeleton_map; //maps previously created groups to actual skeletons + //generates the skeletons when bones are found in the hierarchy, and follows them (including gaps/holes). + _generate_skeletons(state, state.assimp_scene->mRootNode, ownership, skeleton_map, bind_xforms); + + //generate nodes + for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) { + _generate_node(state, scene->mRootNode->mChildren[i], state.root); + } + + //assign skeletons to nodes + + for (Map<MeshInstance *, Skeleton *>::Element *E = state.mesh_skeletons.front(); E; E = E->next()) { + MeshInstance *mesh = E->key(); + Skeleton *skeleton = E->get(); + NodePath skeleton_path = mesh->get_path_to(skeleton); + mesh->set_skeleton_path(skeleton_path); + } + } + + if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) { + + state.animation_player = memnew(AnimationPlayer); + state.root->add_child(state.animation_player); + state.animation_player->set_owner(state.root); + + for (uint32_t i = 0; i < scene->mNumAnimations; i++) { + _import_animation(state, i, p_bake_fps); + } + } + + return state.root; +} + +void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name) { + + const aiNodeAnim *assimp_track = assimp_anim->mChannels[p_track]; + //make transform track + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_TRANSFORM); + animation->track_set_path(track_idx, p_path); + //first determine animation length + + float increment = 1.0 / float(p_bake_fps); + float time = 0.0; + + bool last = false; + + int skeleton_bone = -1; + + if (p_skeleton) { + skeleton_bone = p_skeleton->find_bone(p_name); + } + + Vector<Vector3> pos_values; + Vector<float> pos_times; + Vector<Vector3> scale_values; + Vector<float> scale_times; + Vector<Quat> rot_values; + Vector<float> rot_times; + + for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) { + aiVector3D pos = assimp_track->mPositionKeys[p].mValue; + pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); + pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second); + } + + for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) { + aiQuaternion quat = assimp_track->mRotationKeys[r].mValue; + rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); + rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second); + } + + for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) { + aiVector3D scale = assimp_track->mScalingKeys[sc].mValue; + scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); + scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second); + } + while (true) { + Vector3 pos; + Quat rot; + Vector3 scale(1, 1, 1); + + if (pos_values.size()) { + pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); + } + + if (rot_values.size()) { + rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized(); + } + + if (scale_values.size()) { + scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); + } + + if (skeleton_bone >= 0) { + Transform xform; + xform.basis.set_quat_scale(rot, scale); + xform.origin = pos; + + Transform rest_xform = p_skeleton->get_bone_rest(skeleton_bone); + xform = rest_xform.affine_inverse() * xform; + rot = xform.basis.get_rotation_quat(); + scale = xform.basis.get_scale(); + pos = xform.origin; + } + + rot.normalize(); + + animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); + animation->transform_track_insert_key(track_idx, time, pos, rot, scale); + + if (last) { //done this way so a key is always inserted past the end (for proper interpolation) + break; + } + time += increment; + if (time >= animation->get_length()) { + last = true; + } + } +} + +void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) { + + ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations); + + const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index]; + String name = _assimp_anim_string_to_string(anim->mName); + if (name == String()) { + name = "Animation " + itos(p_animation_index + 1); + } + + float ticks_per_second = anim->mTicksPerSecond; + + if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) { + int32_t time_mode = 0; + state.assimp_scene->mMetaData->Get("TimeMode", time_mode); + ticks_per_second = _get_fbx_fps(time_mode, state.assimp_scene); + } + + //? + //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) { + // ticks_per_second = 1000.0f; + //} + + if (Math::is_equal_approx(ticks_per_second, 0.0f)) { + ticks_per_second = 25.0f; + } + + Ref<Animation> animation; + animation.instance(); + animation->set_name(name); + animation->set_length(anim->mDuration / ticks_per_second); + + //regular tracks + + for (size_t i = 0; i < anim->mNumChannels; i++) { + const aiNodeAnim *track = anim->mChannels[i]; + String node_name = _assimp_get_string(track->mNodeName); + /* + if (node_name.find(ASSIMP_FBX_KEY) != -1) { + String p_track_type = node_name.get_slice(ASSIMP_FBX_KEY, 1); + if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") { + continue; + } + } +*/ + if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) { + continue; //do not bother + } + + bool is_bone = state.bone_owners.has(node_name); + NodePath node_path; + Skeleton *skeleton = NULL; + + if (is_bone) { + skeleton = state.skeletons[state.bone_owners[node_name]]; + String path = state.root->get_path_to(skeleton); + path += ":" + node_name; + node_path = path; + } else { + + ERR_CONTINUE(!state.node_map.has(node_name)); + Node *node = state.node_map[node_name]; + node_path = state.root->get_path_to(node); + } + + _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name); + } + + //blend shape tracks + + for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { + + const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; + + const String prop_name = _assimp_get_string(anim_mesh->mName); + const String mesh_name = prop_name.split("*")[0]; + + ERR_CONTINUE(prop_name.split("*").size() != 2); + + ERR_CONTINUE(!state.node_map.has(mesh_name)); + + const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(state.node_map[mesh_name]); + + ERR_CONTINUE(mesh_instance == NULL); + + String base_path = state.root->get_path_to(mesh_instance); + + Ref<Mesh> mesh = mesh_instance->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + + //add the tracks for this mesh + int base_track = animation->get_track_count(); + for (int j = 0; j < mesh->get_blend_shape_count(); j++) { + + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j)); + } + + for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { + for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { + + float t = anim_mesh->mKeys[k].mTime / ticks_per_second; + float w = anim_mesh->mKeys[k].mWeights[j]; + + animation->track_insert_key(base_track + j, t, w); + } + } + } + + if (animation->get_track_count()) { + state.animation_player->add_animation(name, animation); + } +} + +float EditorSceneImporterAssimp::_get_fbx_fps(int32_t time_mode, const aiScene *p_scene) { + switch (time_mode) { + case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack + case AssetImportFbx::TIME_MODE_120: return 120; + case AssetImportFbx::TIME_MODE_100: return 100; + case AssetImportFbx::TIME_MODE_60: return 60; + case AssetImportFbx::TIME_MODE_50: return 50; + case AssetImportFbx::TIME_MODE_48: return 48; + case AssetImportFbx::TIME_MODE_30: return 30; + case AssetImportFbx::TIME_MODE_30_DROP: return 30; + case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f; + case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f; + case AssetImportFbx::TIME_MODE_PAL: return 25; + case AssetImportFbx::TIME_MODE_CINEMA: return 24; + case AssetImportFbx::TIME_MODE_1000: return 1000; + case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f; + case AssetImportFbx::TIME_MODE_CUSTOM: + int32_t frame_rate; + p_scene->mMetaData->Get("FrameRate", frame_rate); + return frame_rate; + } + return 0; +} + +Transform EditorSceneImporterAssimp::_get_global_assimp_node_transform(const aiNode *p_current_node) { + aiNode const *current_node = p_current_node; + Transform xform; + while (current_node != NULL) { + xform = _assimp_matrix_transform(current_node->mTransformation) * xform; + current_node = current_node->mParent; + } + return xform; +} + +Ref<Texture> EditorSceneImporterAssimp::_load_texture(ImportState &state, String p_path) { + Vector<String> split_path = p_path.get_basename().split("*"); + if (split_path.size() == 2) { + size_t texture_idx = split_path[1].to_int(); + ERR_FAIL_COND_V(texture_idx >= state.assimp_scene->mNumTextures, Ref<Texture>()); + aiTexture *tex = state.assimp_scene->mTextures[texture_idx]; + String filename = _assimp_raw_string_to_string(tex->mFilename); + filename = filename.get_file(); + print_verbose("Open Asset Import: Loading embedded texture " + filename); + if (tex->mHeight == 0) { + if (tex->CheckFormat("png")) { + Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("jpg")) { + Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } else if (tex->CheckFormat("dds")) { + ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented"); + ERR_FAIL_COND_V(true, Ref<Texture>()); + //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth); + //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + //Ref<ImageTexture> t; + //t.instance(); + //t->create_from_image(img); + //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + //return t; + } + } else { + Ref<Image> img; + img.instance(); + PoolByteArray arr; + uint32_t size = tex->mWidth * tex->mHeight; + arr.resize(size); + memcpy(arr.write().ptr(), tex->pcData, size); + ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>()); + //ARGB8888 to RGBA8888 + for (int32_t i = 0; i < arr.size() / 4; i++) { + arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0]; + arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1]; + arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2]; + arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3]; + } + img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr); + ERR_FAIL_COND_V(img.is_null(), Ref<Texture>()); + + Ref<ImageTexture> t; + t.instance(); + t->create_from_image(img); + t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + return t; + } + return Ref<Texture>(); + } + Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture"); + return p_texture; +} + +Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportState &state, int p_index, bool p_double_sided) { + + ERR_FAIL_INDEX_V(p_index, (int)state.assimp_scene->mNumMaterials, Ref<Material>()); + + aiMaterial *ai_material = state.assimp_scene->mMaterials[p_index]; + Ref<SpatialMaterial> mat; + mat.instance(); + + int32_t mat_two_sided = 0; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) { + if (mat_two_sided > 0) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + } + + //const String mesh_name = _assimp_get_string(ai_mesh->mName); + aiString mat_name; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) { + mat->set_name(_assimp_get_string(mat_name)); + } + + aiTextureType tex_normal = aiTextureType_NORMALS; + { + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + + if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + + if (texture.is_valid()) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + { + aiString ai_filename = aiString(); + String filename = ""; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + aiTextureType tex_emissive = aiTextureType_EMISSIVE; + + if (ai_material->GetTextureCount(tex_emissive) > 0) { + + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + + if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture); + } + } + } + } + + aiTextureType tex_albedo = aiTextureType_DIFFUSE; + if (ai_material->GetTextureCount(tex_albedo) > 0) { + + aiString ai_filename = aiString(); + String filename = ""; + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) { + filename = _assimp_raw_string_to_string(ai_filename); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } + } else { + aiColor4D clr_diffuse; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) { + if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a)); + } + } + + aiString tex_gltf_base_color_path = aiString(); + aiTextureMapMode map_mode[2]; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) { + String filename = _assimp_raw_string_to_string(tex_gltf_base_color_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + _set_texture_mapping_mode(map_mode, texture); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) { + if (Math::is_equal_approx(pbr_base_color.a, 1.0f) == false) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) { + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } + } + + { + aiString tex_fbx_pbs_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_normal_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + if (p_double_sided) { + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + + { + aiString tex_fbx_stingray_normal_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_stingray_normal_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture); + } + } + } + } + + { + aiString tex_fbx_pbs_base_color_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_base_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) { + mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a)); + } + } + + aiUVTransform pbr_base_color_uv_xform; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) { + mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f)); + mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f)); + } + } + + { + aiString tex_fbx_pbs_emissive_path = aiString(); + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_emissive_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + _find_texture_path(state.path, path, found); + if (texture != NULL) { + if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + } + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture); + } + } + } else { + aiColor4D pbr_emmissive_color; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) { + mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a)); + } + } + + real_t pbr_emission_intensity; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) { + mat->set_emission_energy(pbr_emission_intensity); + } + } + + aiString tex_gltf_pbr_metallicroughness_path; + if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) { + String filename = _assimp_raw_string_to_string(tex_gltf_pbr_metallicroughness_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE); + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + } + } + } else { + float pbr_roughness = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + float pbr_metallic = 0.0f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_metallic = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_roughness = 0.04f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + } + } + + { + aiString tex_fbx_pbs_metallic_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture); + mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_metallic = 0.0f; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) { + mat->set_metallic(pbr_metallic); + } + } + + aiString tex_fbx_pbs_rough_path; + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) { + String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path); + String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/")); + bool found = false; + _find_texture_path(state.path, path, found); + if (found) { + Ref<Texture> texture = _load_texture(state, path); + if (texture != NULL) { + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture); + mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE); + } + } + } else { + float pbr_roughness = 0.04f; + + if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) { + mat->set_roughness(pbr_roughness); + } + } + } + + return mat; +} + +Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton, bool p_double_sided_material) { + + Ref<ArrayMesh> mesh; + mesh.instance(); + bool has_uvs = false; + + for (int i = 0; i < p_surface_indices.size(); i++) { + const unsigned int mesh_idx = p_surface_indices[i]; + const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; + + Map<uint32_t, Vector<BoneInfo> > vertex_weights; + + if (p_skeleton) { + for (size_t b = 0; b < ai_mesh->mNumBones; b++) { + aiBone *bone = ai_mesh->mBones[b]; + String bone_name = _assimp_get_string(bone->mName); + int bone_index = p_skeleton->find_bone(bone_name); + ERR_CONTINUE(bone_index == -1); //bone refers to an unexisting index, wtf. + + for (size_t w = 0; w < bone->mNumWeights; w++) { + + aiVertexWeight ai_weights = bone->mWeights[w]; + + BoneInfo bi; + + uint32_t vertex_index = ai_weights.mVertexId; + bi.bone = bone_index; + bi.weight = ai_weights.mWeight; + ; + + if (!vertex_weights.has(vertex_index)) { + vertex_weights[vertex_index] = Vector<BoneInfo>(); + } + + vertex_weights[vertex_index].push_back(bi); + } + } + } + + Ref<SurfaceTool> st; + st.instance(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + + for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { + if (ai_mesh->HasTextureCoords(0)) { + has_uvs = true; + st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y)); + } + if (ai_mesh->HasTextureCoords(1)) { + has_uvs = true; + st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y)); + } + if (ai_mesh->HasVertexColors(0)) { + Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a); + st->add_color(color); + } + if (ai_mesh->mNormals != NULL) { + const aiVector3D normals = ai_mesh->mNormals[j]; + const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); + st->add_normal(godot_normal); + if (ai_mesh->HasTangentsAndBitangents()) { + const aiVector3D tangents = ai_mesh->mTangents[j]; + const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); + const aiVector3D bitangent = ai_mesh->mBitangents[j]; + const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); + float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; + st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); + } + } + + if (vertex_weights.has(j)) { + + Vector<BoneInfo> bone_info = vertex_weights[j]; + Vector<int> bones; + bones.resize(bone_info.size()); + Vector<float> weights; + weights.resize(bone_info.size()); + for (int k = 0; k < bone_info.size(); k++) { + bones.write[k] = bone_info[k].bone; + weights.write[k] = bone_info[k].weight; + } + + st->add_bones(bones); + st->add_weights(weights); + } + + const aiVector3D pos = ai_mesh->mVertices[j]; + Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); + st->add_vertex(godot_pos); + } + + for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { + const aiFace face = ai_mesh->mFaces[j]; + ERR_CONTINUE(face.mNumIndices != 3); + Vector<size_t> order; + order.push_back(2); + order.push_back(1); + order.push_back(0); + for (int32_t k = 0; k < order.size(); k++) { + st->add_index(face.mIndices[order[k]]); + } + } + if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { + st->generate_tangents(); + } + + Ref<Material> material; + + if (!state.material_cache.has(ai_mesh->mMaterialIndex)) { + material = _generate_material_from_index(state, ai_mesh->mMaterialIndex, p_double_sided_material); + } + + Array array_mesh = st->commit_to_arrays(); + Array morphs; + morphs.resize(ai_mesh->mNumAnimMeshes); + Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; + Map<uint32_t, String> morph_mesh_idx_names; + for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { + + if (i == 0) { + //only do this the first time + String ai_anim_mesh_name = _assimp_get_string(ai_mesh->mAnimMeshes[j]->mName); + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + if (ai_anim_mesh_name.empty()) { + ai_anim_mesh_name = String("morph_") + itos(j); + } + mesh->add_blend_shape(ai_anim_mesh_name); + } + + Array array_copy; + array_copy.resize(VisualServer::ARRAY_MAX); + + for (int l = 0; l < VisualServer::ARRAY_MAX; l++) { + array_copy[l] = array_mesh[l].duplicate(true); + } + + const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices; + array_copy[Mesh::ARRAY_INDEX] = Variant(); + if (ai_mesh->mAnimMeshes[j]->HasPositions()) { + PoolVector3Array vertices; + vertices.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l]; + Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z); + vertices.write()[l] = position; + } + PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true); + + for (int32_t l = 0; l < vertices.size(); l++) { + PoolVector3Array::Write w = new_vertices.write(); + w[l] = vertices[l]; + } + ERR_CONTINUE(vertices.size() != new_vertices.size()); + array_copy[VisualServer::ARRAY_VERTEX] = new_vertices; + } + + int32_t color_set = 0; + if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) { + PoolColorArray colors; + colors.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l]; + Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a); + colors.write()[l] = color; + } + PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true); + + for (int32_t l = 0; l < colors.size(); l++) { + PoolColorArray::Write w = new_colors.write(); + w[l] = colors[l]; + } + array_copy[VisualServer::ARRAY_COLOR] = new_colors; + } + + if (ai_mesh->mAnimMeshes[j]->HasNormals()) { + PoolVector3Array normals; + normals.resize(num_vertices); + for (size_t l = 0; l < num_vertices; l++) { + const aiVector3D ai_normal = ai_mesh->mAnimMeshes[i]->mNormals[l]; + Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z); + normals.write()[l] = normal; + } + PoolVector3Array new_normals = array_copy[VisualServer::ARRAY_NORMAL].duplicate(true); + + for (int l = 0; l < normals.size(); l++) { + PoolVector3Array::Write w = new_normals.write(); + w[l] = normals[l]; + } + array_copy[VisualServer::ARRAY_NORMAL] = new_normals; + } + + if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) { + PoolColorArray tangents; + tangents.resize(num_vertices); + PoolColorArray::Write w = tangents.write(); + for (size_t l = 0; l < num_vertices; l++) { + _calc_tangent_from_mesh(ai_mesh, j, l, l, w); + } + PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true); + ERR_CONTINUE(new_tangents.size() != tangents.size() * 4); + for (int32_t l = 0; l < tangents.size(); l++) { + new_tangents.write()[l + 0] = tangents[l].r; + new_tangents.write()[l + 1] = tangents[l].g; + new_tangents.write()[l + 2] = tangents[l].b; + new_tangents.write()[l + 3] = tangents[l].a; + } + + array_copy[VisualServer::ARRAY_TANGENT] = new_tangents; + } + + morphs[j] = array_copy; + } + + mesh->add_surface_from_arrays(primitive, array_mesh, morphs); + mesh->surface_set_material(i, material); + mesh->surface_set_name(i, _assimp_get_string(ai_mesh->mName)); + } + + return mesh; +} + +void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent) { + + Spatial *new_node = NULL; + String node_name = _assimp_get_string(p_assimp_node->mName); + Transform node_transform = _assimp_matrix_transform(p_assimp_node->mTransformation); + + if (p_assimp_node->mNumMeshes > 0) { + /* MESH NODE */ + Ref<Mesh> mesh; + Skeleton *skeleton = NULL; + { + + //see if we have mesh cache for this. + Vector<int> surface_indices; + for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) { + int mesh_index = p_assimp_node->mMeshes[i]; + surface_indices.push_back(mesh_index); + + //take the chance and attempt to find the skeleton from the bones + if (!skeleton) { + aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]]; + for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) { + aiBone *bone = ai_mesh->mBones[j]; + String bone_name = _assimp_get_string(bone->mName); + if (state.bone_owners.has(bone_name)) { + skeleton = state.skeletons[state.bone_owners[bone_name]]; + break; + } + } + } + } + surface_indices.sort(); + String mesh_key; + for (int i = 0; i < surface_indices.size(); i++) { + if (i > 0) { + mesh_key += ":"; + } + mesh_key += itos(surface_indices[i]); + } + + if (!state.mesh_cache.has(mesh_key)) { + //adding cache + aiString cull_mode; //cull is on mesh, which is kind of stupid tbh + bool double_sided_material = false; + if (p_assimp_node->mMetaData) { + p_assimp_node->mMetaData->Get("Culling", cull_mode); + } + if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) { + double_sided_material = true; + } + + mesh = _generate_mesh_from_surface_indices(state, surface_indices, skeleton, double_sided_material); + state.mesh_cache[mesh_key] = mesh; + } + + mesh = state.mesh_cache[mesh_key]; + } + + MeshInstance *mesh_node = memnew(MeshInstance); + if (skeleton) { + state.mesh_skeletons[mesh_node] = skeleton; + } + mesh_node->set_mesh(mesh); + new_node = mesh_node; + + } else if (state.light_cache.has(node_name)) { + + Light *light = NULL; + aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[node_name]]; + ERR_FAIL_COND(!ai_light); + + if (ai_light->mType == aiLightSource_DIRECTIONAL) { + light = memnew(DirectionalLight); + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + + node_transform *= light_transform; + + } else if (ai_light->mType == aiLightSource_POINT) { + light = memnew(OmniLight); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Transform xform; + xform.origin = pos; + + node_transform *= xform; + + light->set_transform(xform); + + //light->set_param(Light::PARAM_ATTENUATION, 1); + } else if (ai_light->mType == aiLightSource_SPOT) { + light = memnew(SpotLight); + + Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z); + dir.normalize(); + Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z); + Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z); + up.normalize(); + + Transform light_transform; + light_transform.set_look_at(pos, pos + dir, up); + node_transform *= light_transform; + + //light->set_param(Light::PARAM_ATTENUATION, 0.0f); + } + ERR_FAIL_COND(light == NULL); + light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b)); + new_node = light; + } else if (state.camera_cache.has(node_name)) { + + aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[node_name]]; + ERR_FAIL_COND(!ai_camera); + + Camera *camera = memnew(Camera); + + float near = ai_camera->mClipPlaneNear; + if (Math::is_equal_approx(near, 0.0f)) { + near = 0.1f; + } + camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar); + + Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z); + Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized(); + Vector3 up = Vector3(ai_camera->mUp.x, ai_camera->mUp.y, ai_camera->mUp.z); + + Transform xform; + xform.set_look_at(pos, look_at, up); + + new_node = camera; + } else if (state.bone_owners.has(node_name)) { + + //have to actually put the skeleton somewhere, you know. + Skeleton *skeleton = state.skeletons[state.bone_owners[node_name]]; + if (skeleton->get_parent()) { + //a bone for a skeleton already added.. + //could go downwards here to add meshes children of skeleton bones + //but let's not support it for now. + return; + } + //restore rest poses to local, now that we know where the skeleton finally is + Transform skeleton_transform; + if (p_assimp_node->mParent) { + skeleton_transform = _get_global_assimp_node_transform(p_assimp_node->mParent); + } + for (int i = 0; i < skeleton->get_bone_count(); i++) { + Transform rest = skeleton_transform.affine_inverse() * skeleton->get_bone_rest(i); + skeleton->set_bone_rest(i, rest.affine_inverse()); + } + + skeleton->localize_rests(); + node_name = "Skeleton"; //don't use the bone root name + node_transform = Transform(); //don't transform + + new_node = skeleton; + } else { + //generic node + new_node = memnew(Spatial); + } + + { + + new_node->set_name(node_name); + new_node->set_transform(node_transform); + p_parent->add_child(new_node); + new_node->set_owner(state.root); + } + + state.node_map[node_name] = new_node; + + for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) { + _generate_node(state, p_assimp_node->mChildren[i], new_node); + } +} + +void EditorSceneImporterAssimp::_calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) { + const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index]; + const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); + const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index]; + const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z); + const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index]; + const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z); + float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f; + Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d); + w[index] = plane_tangent; +} + +void EditorSceneImporterAssimp::_set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture) { + ERR_FAIL_COND(map_mode == NULL); + aiTextureMapMode tex_mode = aiTextureMapMode::aiTextureMapMode_Wrap; + //for (size_t i = 0; i < 3; i++) { + tex_mode = map_mode[0]; + //} + int32_t flags = Texture::FLAGS_DEFAULT; + if (tex_mode == aiTextureMapMode_Wrap) { + //Default + } else if (tex_mode == aiTextureMapMode_Clamp) { + flags = flags & ~Texture::FLAG_REPEAT; + } else if (tex_mode == aiTextureMapMode_Mirror) { + flags = flags | Texture::FLAG_MIRRORED_REPEAT; + } + texture->set_flags(flags); +} + +void EditorSceneImporterAssimp::_find_texture_path(const String &r_p_path, String &r_path, bool &r_found) { + + _Directory dir; + + List<String> exts; + ImageLoader::get_recognized_extensions(&exts); + + Vector<String> split_path = r_path.get_basename().split("*"); + if (split_path.size() == 2) { + r_found = true; + return; + } + + if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) { + r_path = r_p_path.get_base_dir() + r_path.get_file(); + r_found = true; + return; + } + + for (int32_t i = 0; i < exts.size(); i++) { + if (r_found) { + return; + } + if (r_found == false) { + _find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]); + } + } +} + +void EditorSceneImporterAssimp::_find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) { + String name = path.get_basename() + extension; + if (dir.file_exists(name)) { + found = true; + path = name; + return; + } + String name_ignore_sub_directory = p_path.get_base_dir().plus_file(path.get_file().get_basename()) + extension; + if (dir.file_exists(name_ignore_sub_directory)) { + found = true; + path = name_ignore_sub_directory; + return; + } + + String name_find_texture_sub_directory = p_path.get_base_dir() + "/textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_sub_directory)) { + found = true; + path = name_find_texture_sub_directory; + return; + } + String name_find_texture_upper_sub_directory = p_path.get_base_dir() + "/Textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_upper_sub_directory)) { + found = true; + path = name_find_texture_upper_sub_directory; + return; + } + String name_find_texture_outside_sub_directory = p_path.get_base_dir() + "/../textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_texture_outside_sub_directory)) { + found = true; + path = name_find_texture_outside_sub_directory; + return; + } + + String name_find_upper_texture_outside_sub_directory = p_path.get_base_dir() + "/../Textures/" + path.get_file().get_basename() + extension; + if (dir.file_exists(name_find_upper_texture_outside_sub_directory)) { + found = true; + path = name_find_upper_texture_outside_sub_directory; + return; + } +} + +String EditorSceneImporterAssimp::_assimp_get_string(const aiString p_string) const { + //convert an assimp String to a Godot String + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + if (name.find(":") != -1) { + String replaced_name = name.split(":")[1]; + print_verbose("Replacing " + name + " containing : with " + replaced_name); + name = replaced_name; + } + + name = name.replace(".", ""); //can break things, specially bone names + + return name; +} + +String EditorSceneImporterAssimp::_assimp_anim_string_to_string(const aiString p_string) const { + + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + if (name.find(":") != -1) { + String replaced_name = name.split(":")[1]; + print_verbose("Replacing " + name + " containing : with " + replaced_name); + name = replaced_name; + } + return name; +} + +String EditorSceneImporterAssimp::_assimp_raw_string_to_string(const aiString p_string) const { + String name; + name.parse_utf8(p_string.C_Str() /*,p_string.length*/); + return name; +} + +Ref<Animation> EditorSceneImporterAssimp::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { + return Ref<Animation>(); +} + +const Transform EditorSceneImporterAssimp::_assimp_matrix_transform(const aiMatrix4x4 p_matrix) { + aiMatrix4x4 matrix = p_matrix; + Transform xform; + //xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4); + xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4); + return xform; +} diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h new file mode 100644 index 0000000000..598845236e --- /dev/null +++ b/modules/assimp/editor_scene_importer_assimp.h @@ -0,0 +1,234 @@ +/*************************************************************************/ +/* editor_scene_importer_assimp.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_SCENE_IMPORTER_ASSIMP_H +#define EDITOR_SCENE_IMPORTER_ASSIMP_H + +#ifdef TOOLS_ENABLED +#include "core/bind/core_bind.h" +#include "core/io/resource_importer.h" +#include "core/vector.h" +#include "editor/import/resource_importer_scene.h" +#include "editor/project_settings_editor.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/skeleton.h" +#include "scene/3d/spatial.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/animation.h" +#include "scene/resources/surface_tool.h" + +#include "assimp/DefaultLogger.hpp" +#include "assimp/LogStream.hpp" +#include "assimp/Logger.hpp" +#include "assimp/matrix4x4.h" +#include "assimp/scene.h" +#include "assimp/types.h" + +class AssimpStream : public Assimp::LogStream { +public: + // Constructor + AssimpStream(); + + // Destructor + ~AssimpStream(); + // Write something using your own functionality + void write(const char *message); +}; + +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor", 0, 0 +#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness", 0, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness", 0, 0 + +#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0 + +#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0 + +#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness", 0, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity", 0, 0 + +#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file", aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo", aiTextureType_UNKNOWN, 0 + +class EditorSceneImporterAssimp : public EditorSceneImporter { +private: + GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter); + const String ASSIMP_FBX_KEY = "_$AssimpFbx$"; + + struct AssetImportAnimation { + enum Interpolation { + INTERP_LINEAR, + INTERP_STEP, + INTERP_CATMULLROMSPLINE, + INTERP_CUBIC_SPLINE + }; + }; + + struct AssetImportFbx { + enum ETimeMode { + TIME_MODE_DEFAULT = 0, + TIME_MODE_120 = 1, + TIME_MODE_100 = 2, + TIME_MODE_60 = 3, + TIME_MODE_50 = 4, + TIME_MODE_48 = 5, + TIME_MODE_30 = 6, + TIME_MODE_30_DROP = 7, + TIME_MODE_NTSC_DROP_FRAME = 8, + TIME_MODE_NTSC_FULL_FRAME = 9, + TIME_MODE_PAL = 10, + TIME_MODE_CINEMA = 11, + TIME_MODE_1000 = 12, + TIME_MODE_CINEMA_ND = 13, + TIME_MODE_CUSTOM = 14, + TIME_MODE_TIME_MODE_COUNT = 15 + }; + enum UpAxis { + UP_VECTOR_AXIS_X = 1, + UP_VECTOR_AXIS_Y = 2, + UP_VECTOR_AXIS_Z = 3 + }; + enum FrontAxis { + FRONT_PARITY_EVEN = 1, + FRONT_PARITY_ODD = 2, + }; + + enum CoordAxis { + COORD_RIGHT = 0, + COORD_LEFT = 1 + }; + }; + + struct ImportState { + + String path; + const aiScene *assimp_scene; + uint32_t max_bone_weights; + Spatial *root; + Map<String, Ref<Mesh> > mesh_cache; + Map<int, Ref<Material> > material_cache; + Map<String, int> light_cache; + Map<String, int> camera_cache; + Vector<Skeleton *> skeletons; + Map<String, int> bone_owners; //maps bones to skeleton index owned by + Map<String, Node *> node_map; + Map<MeshInstance *, Skeleton *> mesh_skeletons; + bool fbx; //for some reason assimp does some things different for FBX + AnimationPlayer *animation_player; + }; + + struct BoneInfo { + uint32_t bone; + float weight; + }; + + struct SkeletonHole { //nodes may be part of the skeleton by used by vertex + String name; + String parent; + Transform pose; + const aiNode *node; + }; + + const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix); + String _assimp_get_string(const aiString p_string) const; + Transform _get_global_assimp_node_transform(const aiNode *p_current_node); + + void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w); + void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture); + void _find_texture_path(const String &p_path, String &path, bool &r_found); + void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension); + + Ref<Texture> _load_texture(ImportState &state, String p_path); + Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided); + Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false); + void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent); + void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms); + void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms); + void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms); + + void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name); + + void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps); + + Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights); + + String _assimp_anim_string_to_string(const aiString p_string) const; + String _assimp_raw_string_to_string(const aiString p_string) const; + float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene); + template <class T> + T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp); + void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; + + struct ImportFormat { + Vector<String> extensions; + bool is_default; + }; + +protected: + static void _bind_methods(); + +public: + EditorSceneImporterAssimp() { + Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE); + unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn; + Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity); + } + ~EditorSceneImporterAssimp() { + Assimp::DefaultLogger::kill(); + } + + virtual void get_extensions(List<String> *r_extensions) const; + virtual uint32_t get_import_flags() const; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); +}; +#endif +#endif diff --git a/modules/assimp/godot_update_assimp.sh b/modules/assimp/godot_update_assimp.sh new file mode 100644 index 0000000000..dcf1e6d4a2 --- /dev/null +++ b/modules/assimp/godot_update_assimp.sh @@ -0,0 +1,261 @@ +rm -rf ../../thirdparty/assimp +cd ../../thirdparty/ +git clone https://github.com/assimp/assimp.git +cd assimp +rm -rf code/3DSExporter.h +rm -rf code/3DSLoader.h +rm -rf code/3MFXmlTags.h +rm -rf code/ABCImporter.h +rm -rf code/ACLoader.h +rm -rf code/AMFImporter_Macro.hpp +rm -rf code/ASELoader.h +rm -rf code/assbin_chunks.h +rm -rf code/AssbinExporter.h +rm -rf code/AssbinLoader.h +rm -rf code/AssimpCExport.cpp +rm -rf code/AssxmlExporter.h +rm -rf code/B3DImporter.h +# rm -rf code/BaseProcess.cpp +# rm -rf code/BaseProcess.h +# rm -rf code/Bitmap.cpp +rm -rf code/BlenderBMesh.cpp +rm -rf code/BlenderBMesh.h +rm -rf code/BlenderCustomData.cpp +rm -rf code/BlenderCustomData.h +rm -rf code/BlenderIntermediate.h +rm -rf code/BlenderLoader.h +rm -rf code/BlenderModifier.h +rm -rf code/BlenderSceneGen.h +rm -rf code/BlenderTessellator.h +rm -rf code/BVHLoader.h +rm -rf code/C4DImporter.h +# rm -rf code/CalcTangentsProcess.h +# rm -rf code/CInterfaceIOWrapper.cpp +# rm -rf code/CInterfaceIOWrapper.h +rm -rf code/COBLoader.h +rm -rf code/COBScene.h +rm -rf code/ColladaExporter.h +rm -rf code/ColladaLoader.h +# rm -rf code/ComputeUVMappingProcess.h +# rm -rf code/ConvertToLHProcess.h +# rm -rf code/CreateAnimMesh.cpp +rm -rf code/CSMLoader.h +rm -rf code/D3MFExporter.h +rm -rf code/D3MFImporter.h +rm -rf code/D3MFOpcPackage.h +# rm -rf code/DeboneProcess.h +# rm -rf code/DefaultIOStream.cpp +# rm -rf code/DefaultIOSystem.cpp +# rm -rf code/DefaultProgressHandler.h +# rm -rf code/DropFaceNormalsProcess.cpp +# rm -rf code/DropFaceNormalsProcess.h +rm -rf code/DXFHelper.h +rm -rf code/DXFLoader.h +# rm -rf code/EmbedTexturesProcess.cpp +# rm -rf code/EmbedTexturesProcess.h +# rm -rf code/FBXCommon.h +# rm -rf code/FBXCompileConfig.h +# rm -rf code/FBXDeformer.cpp +# rm -rf code/FBXDocumentUtil.cpp +# rm -rf code/FBXDocumentUtil.h +# rm -rf code/FBXExporter.h +# rm -rf code/FBXExportNode.h +# rm -rf code/FBXExportProperty.h +# rm -rf code/FBXImporter.cpp +# rm -rf code/FBXImporter.h +# rm -rf code/FBXImportSettings.h +# rm -rf code/FBXMeshGeometry.h +# rm -rf code/FBXModel.cpp +# rm -rf code/FBXNodeAttribute.cpp +# rm -rf code/FBXParser.h +# rm -rf code/FBXProperties.cpp +# rm -rf code/FBXProperties.h +# rm -rf code/FBXTokenizer.cpp +# rm -rf code/FBXTokenizer.h +# rm -rf code/FBXUtil.cpp +# rm -rf code/FBXUtil.h +# rm -rf code/FileLogStream.h +# rm -rf code/FindDegenerates.h +# rm -rf code/FindInstancesProcess.h +# rm -rf code/FindInvalidDataProcess.h +rm -rf code/FIReader.hpp +# rm -rf code/FixNormalsStep.cpp +# rm -rf code/FixNormalsStep.h +# rm -rf code/GenFaceNormalsProcess.cpp +# rm -rf code/GenFaceNormalsProcess.h +# rm -rf code/GenVertexNormalsProcess.cpp +# rm -rf code/GenVertexNormalsProcess.h +rm -rf code/glTF2Asset.h +rm -rf code/glTF2Asset.inl +rm -rf code/glTF2AssetWriter.inl +rm -rf code/glTF2Exporter.cpp +rm -rf code/glTF2Importer.cpp +rm -rf code/glTF2AssetWriter.h +rm -rf code/glTFAsset.h +rm -rf code/glTFAsset.inl +rm -rf code/glTFAssetWriter.inl +rm -rf code/glTFExporter.cpp +rm -rf code/glTFImporter.cpp +rm -rf code/glTF2Exporter.h +rm -rf code/glTF2Importer.h +rm -rf code/glTFAssetWriter.h +rm -rf code/glTFExporter.h +rm -rf code/glTFImporter.h +rm -rf code/HalfLifeFileData.h +rm -rf code/HMPFileData.h +rm -rf code/HMPLoader.h +rm -rf code/HMPLoader.cpp +rm -rf code/IFF.h +# rm -rf code/Importer.h +# rm -rf code/ImproveCacheLocality.h +rm -rf code/IRRLoader.h +rm -rf code/IRRMeshLoader.h +rm -rf code/IRRShared.h +# rm -rf code/JoinVerticesProcess.h +# rm -rf code/LimitBoneWeightsProcess.cpp +# rm -rf code/LimitBoneWeightsProcess.h +rm -rf code/LWSLoader.h +rm -rf code/makefile.mingw +# rm -rf code/MakeVerboseFormat.cpp +# rm -rf code/MakeVerboseFormat.h +# rm -rf code/MaterialSystem.h +rm -rf code/MD2FileData.h +rm -rf code/MD2Loader.h +rm -rf code/MD2NormalTable.h +rm -rf code/MD3FileData.h +rm -rf code/MD3Loader.h +rm -rf code/MD4FileData.h +rm -rf code/MD5Loader.h +rm -rf code/MD5Parser.cpp +rm -rf code/MDCFileData.h +rm -rf code/MDCLoader.h +rm -rf code/MDLDefaultColorMap.h +# rm -rf code/MMDCpp14.h +# rm -rf code/MMDImporter.h +rm -rf code/MS3DLoader.h +rm -rf code/NDOLoader.h +rm -rf code/NFFLoader.h +rm -rf code/ObjExporter.h +rm -rf code/ObjFileImporter.h +rm -rf code/ObjFileMtlImporter.h +rm -rf code/ObjFileParser.h +rm -rf code/ObjTools.h +rm -rf code/ObjExporter.cpp +rm -rf code/ObjFileImporter.cpp +rm -rf code/ObjFileMtlImporter.cpp +rm -rf code/ObjFileParser.cpp +rm -rf code/OFFLoader.h +rm -rf code/OFFLoader.cpp +rm -rf code/OgreImporter.cpp +rm -rf code/OgreImporter.h +rm -rf code/OgreParsingUtils.h +rm -rf code/OgreXmlSerializer.h +rm -rf code/OgreXmlSerializer.cpp +rm -rf code/OgreBinarySerializer.cpp +rm -rf code/OpenGEXExporter.cpp +rm -rf code/OpenGEXExporter.h +rm -rf code/OpenGEXImporter.h +rm -rf code/OpenGEXStructs.h +rm -rf code/OpenGEXImporter.cpp +# rm -rf code/OptimizeGraph.h +# rm -rf code/OptimizeMeshes.cpp +# rm -rf code/OptimizeMeshes.h +rm -rf code/PlyExporter.h +rm -rf code/PlyLoader.h +# rm -rf code/PolyTools.h +# rm -rf code/PostStepRegistry.cpp +# rm -rf code/PretransformVertices.h +rm -rf code/Q3BSPFileData.h +rm -rf code/Q3BSPFileImporter.h +rm -rf code/Q3BSPFileParser.cpp +rm -rf code/Q3BSPFileParser.h +rm -rf code/Q3BSPZipArchive.cpp +rm -rf code/Q3BSPZipArchive.h +rm -rf code/Q3DLoader.h +rm -rf code/Q3DLoader.cpp +rm -rf code/Q3BSPFileImporter.cpp +rm -rf code/RawLoader.h +# rm -rf code/RemoveComments.cpp +# rm -rf code/RemoveRedundantMaterials.cpp +# rm -rf code/RemoveRedundantMaterials.h +# rm -rf code/RemoveVCProcess.h +# rm -rf code/ScaleProcess.cpp +# rm -rf code/ScaleProcess.h +# rm -rf code/scene.cpp +# rm -rf code/ScenePreprocessor.cpp +# rm -rf code/ScenePreprocessor.h +# rm -rf code/ScenePrivate.h +# rm -rf code/SGSpatialSort.cpp +rm -rf code/SIBImporter.h +rm -rf code/SMDLoader.cpp +# rm -rf code/simd.cpp +# rm -rf code/simd.h +# rm -rf code/SortByPTypeProcess.h +# rm -rf code/SplitByBoneCountProcess.h +# rm -rf code/SplitLargeMeshes.h +# rm -rf code/StdOStreamLogStream.h +rm -rf code/StepExporter.h +rm -rf code/StepExporter.cpp +rm -rf code/STLExporter.cpp +rm -rf code/STLExporter.h +rm -rf code/STLLoader.h +rm -rf code/STLLoader.cpp +# rm -rf code/TargetAnimation.cpp +# rm -rf code/TargetAnimation.h +rm -rf code/TerragenLoader.h +rm -rf code/TerragenLoader.cpp +# rm -rf code/TextureTransform.h +# rm -rf code/TriangulateProcess.h +rm -rf code/UnrealLoader.h +# rm -rf code/ValidateDataStructure.h +# rm -rf code/Version.cpp +# rm -rf code/VertexTriangleAdjacency.cpp +# rm -rf code/VertexTriangleAdjacency.h +# rm -rf code/Win32DebugLogStream.h +rm -rf code/X3DImporter_Macro.hpp +rm -rf code/X3DImporter_Metadata.cpp +rm -rf code/X3DImporter_Networking.cpp +rm -rf code/X3DImporter_Texturing.cpp +rm -rf code/X3DImporter_Shape.cpp +rm -rf code/X3DImporter_Rendering.cpp +rm -rf code/X3DImporter_Postprocess.cpp +rm -rf code/X3DImporter_Light.cpp +rm -rf code/X3DImporter_Group.cpp +rm -rf code/X3DImporter_Geometry3D.cpp +rm -rf code/X3DImporter_Geometry2D.cpp +rm -rf code/X3DImporter.cpp +rm -rf code/X3DExporter.cpp +rm -rf code/X3DVocabulary.cpp +rm -rf code/XFileExporter.h +rm -rf code/XFileExporter.cpp +rm -rf code/XFileHelper.h +rm -rf code/XFileHelper.cpp +rm -rf code/XFileImporter.h +rm -rf code/XFileImporter.cpp +rm -rf code/XFileParser.h +rm -rf code/XFileParser.cpp +rm -rf code/XGLLoader.h +rm -rf code/XGLLoader.cpp +rm -rf code/Importer +rm -rf .git +rm -rf cmake-modules +rm -rf doc +rm -rf packaging +rm -rf port +rm -rf samples +rm -rf scripts +rm -rf test +rm -rf tools +rm -rf contrib/zlib +rm -rf contrib/android-cmake +rm -rf contrib/gtest +rm -rf contrib/clipper +rm -rf contrib/irrXML +rm -rf contrib/Open3DGC +rm -rf contrib/openddlparser +rm -rf contrib/poly2tri +rm -rf contrib/rapidjson +rm -rf contrib/unzip +rm -rf contrib/zip +rm -rf contrib/stb_image +rm .travis* diff --git a/modules/assimp/register_types.cpp b/modules/assimp/register_types.cpp new file mode 100644 index 0000000000..2e8181372e --- /dev/null +++ b/modules/assimp/register_types.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "editor/editor_node.h" +#include "editor_scene_importer_assimp.h" + +#ifdef TOOLS_ENABLED +static void _editor_init() { + Ref<EditorSceneImporterAssimp> import_assimp; + import_assimp.instance(); + ResourceImporterScene::get_singleton()->add_importer(import_assimp); +} +#endif + +void register_assimp_types() { + +#ifdef TOOLS_ENABLED + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + ClassDB::register_class<EditorSceneImporterAssimp>(); + + ClassDB::set_current_api(prev_api); + + EditorNode::add_init_callback(_editor_init); +#endif +} + +void unregister_assimp_types() { +} diff --git a/modules/assimp/register_types.h b/modules/assimp/register_types.h new file mode 100644 index 0000000000..f841cd26b2 --- /dev/null +++ b/modules/assimp/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_assimp_types(); +void unregister_assimp_types(); diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 063508a25f..88732dff33 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const uint8_t *p_color_buffer, + const uint32_t color_table_size, const bmp_header_s &p_header) { Error err = OK; @@ -46,32 +47,84 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, size_t height = (size_t)p_header.bmp_info_header.bmp_height; size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count; - if (p_header.bmp_info_header.bmp_compression != 0) { - err = FAILED; - } + // Check whether we can load it - if (!(bits_per_pixel == 24 || bits_per_pixel == 32)) { - err = FAILED; - } + if (bits_per_pixel == 1) { + // Requires bit unpacking... + ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE); + + } else if (bits_per_pixel == 4) { + // Requires bit unpacking... + ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE); + } else if (bits_per_pixel == 16) { + + ERR_FAIL_V(ERR_UNAVAILABLE); + } if (err == OK) { - uint32_t line_width = ((p_header.bmp_info_header.bmp_width * - p_header.bmp_info_header.bmp_bit_count / 8) + - 3) & - ~3; + // Image data (might be indexed) + PoolVector<uint8_t> data; + int data_len = 0; + + if (bits_per_pixel <= 8) { // indexed + data_len = width * height; + } else { // color + data_len = width * height * 4; + } + ERR_FAIL_COND_V(data_len == 0, ERR_BUG); + err = data.resize(data_len); - PoolVector<uint8_t> image_data; - err = image_data.resize(width * height * 4); + PoolVector<uint8_t>::Write data_w = data.write(); + uint8_t *write_buffer = data_w.ptr(); - PoolVector<uint8_t>::Write image_data_w = image_data.write(); - uint8_t *write_buffer = image_data_w.ptr(); + const uint32_t width_bytes = width * bits_per_pixel / 8; + const uint32_t line_width = (width_bytes + 3) & ~3; + // The actual data traversal is determined by + // the data width in case of 8/4/1 bit images + const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); + for (unsigned int i = 0; i < height; i++) { const uint8_t *line_ptr = line; - for (unsigned int j = 0; j < width; j++) { + + for (unsigned int j = 0; j < w; j++) { switch (bits_per_pixel) { + case 1: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 7) & 1; + write_buffer[index + 1] = (color_index >> 6) & 1; + write_buffer[index + 2] = (color_index >> 5) & 1; + write_buffer[index + 3] = (color_index >> 4) & 1; + write_buffer[index + 4] = (color_index >> 3) & 1; + write_buffer[index + 5] = (color_index >> 2) & 1; + write_buffer[index + 6] = (color_index >> 1) & 1; + write_buffer[index + 7] = (color_index >> 0) & 1; + + index += 8; + line_ptr += 1; + } break; + case 4: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 4) & 0x0f; + write_buffer[index + 1] = color_index & 0x0f; + + index += 2; + line_ptr += 1; + } break; + case 8: { + uint8_t color_index = *line_ptr; + + write_buffer[index] = color_index; + + index += 1; + line_ptr += 1; + } break; case 24: { uint32_t color = *((uint32_t *)line_ptr); @@ -79,6 +132,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, write_buffer[index + 1] = (color >> 8) & 0xff; write_buffer[index + 0] = (color >> 16) & 0xff; write_buffer[index + 3] = 0xff; + index += 4; line_ptr += 3; } break; @@ -89,6 +143,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, write_buffer[index + 1] = (color >> 8) & 0xff; write_buffer[index + 0] = (color >> 16) & 0xff; write_buffer[index + 3] = color >> 24; + index += 4; line_ptr += 4; } break; @@ -96,7 +151,51 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, } line -= line_width; } - p_image->create(width, height, 0, Image::FORMAT_RGBA8, image_data); + + if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels + + p_image->create(width, height, 0, Image::FORMAT_RGBA8, data); + + } else { // data is in indexed format, extend it + + // Palette data + PoolVector<uint8_t> palette_data; + palette_data.resize(color_table_size * 4); + + PoolVector<uint8_t>::Write palette_data_w = palette_data.write(); + uint8_t *pal = palette_data_w.ptr(); + + const uint8_t *cb = p_color_buffer; + + for (unsigned int i = 0; i < color_table_size; ++i) { + uint32_t color = *((uint32_t *)cb); + + pal[i * 4 + 0] = (color >> 16) & 0xff; + pal[i * 4 + 1] = (color >> 8) & 0xff; + pal[i * 4 + 2] = (color)&0xff; + pal[i * 4 + 3] = 0xff; + + cb += 4; + } + // Extend palette to image + PoolVector<uint8_t> extended_data; + extended_data.resize(data.size() * 4); + + PoolVector<uint8_t>::Write ex_w = extended_data.write(); + uint8_t *dest = ex_w.ptr(); + + const int num_pixels = width * height; + + for (int i = 0; i < num_pixels; i++) { + dest[0] = pal[write_buffer[i] * 4 + 0]; + dest[1] = pal[write_buffer[i] * 4 + 1]; + dest[2] = pal[write_buffer[i] * 4 + 2]; + dest[3] = pal[write_buffer[i] * 4 + 3]; + + dest += 4; + } + p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data); + } } } return err; @@ -108,7 +207,9 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header_s bmp_header; Error err = ERR_INVALID_DATA; - if (f->get_len() > sizeof(bmp_header)) { + // A valid bmp file should always at least have a + // file header and a minimal info header + if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) { // File Header bmp_header.bmp_file_header.bmp_signature = f->get_16(); if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) { @@ -118,9 +219,14 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, // Info Header bmp_header.bmp_info_header.bmp_header_size = f->get_32(); + ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT); + bmp_header.bmp_info_header.bmp_width = f->get_32(); bmp_header.bmp_info_header.bmp_height = f->get_32(); + bmp_header.bmp_info_header.bmp_planes = f->get_16(); + ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT); + bmp_header.bmp_info_header.bmp_bit_count = f->get_16(); bmp_header.bmp_info_header.bmp_compression = f->get_32(); bmp_header.bmp_info_header.bmp_size_image = f->get_32(); @@ -129,37 +235,39 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header.bmp_info_header.bmp_colors_used = f->get_32(); bmp_header.bmp_info_header.bmp_important_colors = f->get_32(); - bmp_header.bmp_info_header.bmp_red_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_green_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_blue_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_alpha_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_cs_type = f->get_32(); - for (int i = 0; i < 9; i++) - bmp_header.bmp_info_header.bmp_endpoints[i] = f->get_32(); - - bmp_header.bmp_info_header.bmp_gamma_red = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_green = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_blue = f->get_32(); - - f->seek(sizeof(bmp_header.bmp_file_header) + - bmp_header.bmp_info_header.bmp_header_size); + switch (bmp_header.bmp_info_header.bmp_compression) { + case BI_RLE8: + case BI_RLE4: + case BI_CMYKRLE8: + case BI_CMYKRLE4: { + // Stop parsing + ERR_EXPLAIN("Compressed BMP files are not supported: " + f->get_path()); + f->close(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } break; + } + // Don't rely on sizeof(bmp_file_header) as structure padding + // adds 2 bytes offset leading to misaligned color table reading + uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE + + bmp_header.bmp_info_header.bmp_header_size; + f->seek(ct_offset); uint32_t color_table_size = 0; - if (bmp_header.bmp_info_header.bmp_bit_count == 1) - color_table_size = 2; - else if (bmp_header.bmp_info_header.bmp_bit_count == 4) - color_table_size = 16; - else if (bmp_header.bmp_info_header.bmp_bit_count == 8) - color_table_size = 256; - PoolVector<uint8_t> bmp_color_table; - if (color_table_size > 0) { - err = bmp_color_table.resize(color_table_size * 4); - PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write(); - f->get_buffer(bmp_color_table_w.ptr(), - bmp_header.bmp_info_header.bmp_colors_used * 4); + // bmp_colors_used may report 0 despite having a color table + // for 4 and 1 bit images, so don't rely on this information + if (bmp_header.bmp_info_header.bmp_bit_count <= 8) { + // Support 256 colors max + color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count; + ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG); } + PoolVector<uint8_t> bmp_color_table; + // Color table is usually 4 bytes per color -> [B][G][R][0] + bmp_color_table.resize(color_table_size * 4); + PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write(); + f->get_buffer(bmp_color_table_w.ptr(), color_table_size * 4); + f->seek(bmp_header.bmp_file_header.bmp_file_offset); uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size - @@ -174,7 +282,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, PoolVector<uint8_t>::Read bmp_buffer_r = bmp_buffer.read(); PoolVector<uint8_t>::Read bmp_color_table_r = bmp_color_table.read(); err = convert_to_image(p_image, bmp_buffer_r.ptr(), - bmp_color_table_r.ptr(), bmp_header); + bmp_color_table_r.ptr(), color_table_size, bmp_header); } f->close(); } diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index e0e50859fc..2debb19a1c 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,22 @@ class ImageLoaderBMP : public ImageFormatLoader { protected: static const unsigned BITMAP_SIGNATURE = 0x4d42; + static const unsigned BITMAP_FILE_HEADER_SIZE = 14; // bmp_file_header_s + static const unsigned BITMAP_INFO_HEADER_MIN_SIZE = 40; // bmp_info_header_s + + enum bmp_compression_s { + BI_RGB = 0x00, + BI_RLE8 = 0x01, // compressed + BI_RLE4 = 0x02, // compressed + BI_BITFIELDS = 0x03, + BI_JPEG = 0x04, + BI_PNG = 0x05, + BI_ALPHABITFIELDS = 0x06, + BI_CMYK = 0x0b, + BI_CMYKRLE8 = 0x0c, // compressed + BI_CMYKRLE4 = 0x0d // compressed + }; + struct bmp_header_s { struct bmp_file_header_s { uint16_t bmp_signature; @@ -57,21 +73,13 @@ protected: uint32_t bmp_pixels_per_meter_y; uint32_t bmp_colors_used; uint32_t bmp_important_colors; - uint32_t bmp_red_mask; - uint32_t bmp_green_mask; - uint32_t bmp_blue_mask; - uint32_t bmp_alpha_mask; - uint32_t bmp_cs_type; - uint32_t bmp_endpoints[9]; - uint32_t bmp_gamma_red; - uint32_t bmp_gamma_green; - uint32_t bmp_gamma_blue; } bmp_info_header; }; static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const uint8_t *p_color_buffer, + const uint32_t color_table_size, const bmp_header_s &p_header); public: diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp index 1f68a03e85..a1e3f386f4 100644 --- a/modules/bmp/register_types.cpp +++ b/modules/bmp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bmp/register_types.h b/modules/bmp/register_types.h index d8755db397..cd2a5c0570 100644 --- a/modules/bmp/register_types.h +++ b/modules/bmp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 11ce18449b..ecc8a9b481 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -186,7 +186,13 @@ if env['builtin_bullet']: thirdparty_sources = [thirdparty_dir + file for file in bullet2_src] - env_bullet.Append(CPPPATH=[thirdparty_dir]) + # Treat Bullet headers as system headers to avoid raising warnings. Not supported on MSVC. + if not env.msvc: + env_bullet.Append(CPPFLAGS=['-isystem', Dir(thirdparty_dir).path]) + else: + env_bullet.Prepend(CPPPATH=[thirdparty_dir]) + # if env['target'] == "debug" or env['target'] == "release_debug": + # env_bullet.Append(CPPDEFINES=['BT_DEBUG']) env_thirdparty = env_bullet.Clone() env_thirdparty.disable_warnings() diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index a0486443c2..a6872d81d7 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 1c5962cfe3..0942212b9e 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp index 6cc63d79ce..b60d6ba693 100644 --- a/modules/bullet/btRayShape.cpp +++ b/modules/bullet/btRayShape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,6 +54,11 @@ void btRayShape::setLength(btScalar p_length) { reload_cache(); } +void btRayShape::setMargin(btScalar margin) { + btConvexInternalShape::setMargin(margin); + reload_cache(); +} + void btRayShape::setSlipsOnSlope(bool p_slipsOnSlope) { slipsOnSlope = p_slipsOnSlope; @@ -77,10 +82,9 @@ void btRayShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVecto } void btRayShape::getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const { -#define MARGIN_BROADPHASE 0.1 btVector3 localAabbMin(0, 0, 0); - btVector3 localAabbMax(m_shapeAxis * m_length); - btTransformAabb(localAabbMin, localAabbMax, MARGIN_BROADPHASE, t, aabbMin, aabbMax); + btVector3 localAabbMax(m_shapeAxis * m_cacheScaledLength); + btTransformAabb(localAabbMin, localAabbMax, m_collisionMargin, t, aabbMin, aabbMax); } void btRayShape::calculateLocalInertia(btScalar mass, btVector3 &inertia) const { @@ -97,7 +101,7 @@ void btRayShape::getPreferredPenetrationDirection(int index, btVector3 &penetrat void btRayShape::reload_cache() { - m_cacheScaledLength = m_length * m_localScaling[2] + m_collisionMargin; + m_cacheScaledLength = m_length * m_localScaling[2]; m_cacheSupportPoint.setIdentity(); m_cacheSupportPoint.setOrigin(m_shapeAxis * m_cacheScaledLength); diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h index a44c30db4b..7f3229b3e8 100644 --- a/modules/bullet/btRayShape.h +++ b/modules/bullet/btRayShape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -60,6 +60,8 @@ public: void setLength(btScalar p_length); btScalar getLength() const { return m_length; } + virtual void setMargin(btScalar margin); + void setSlipsOnSlope(bool p_slipOnSlope); bool getSlipsOnSlope() const { return slipsOnSlope; } diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 7bc731e75e..038001996d 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -267,7 +267,7 @@ RID BulletPhysicsServer::area_get_space(RID p_area) const { void BulletPhysicsServer::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) { AreaBullet *area = area_owner.get(p_area); - ERR_FAIL_COND(!area) + ERR_FAIL_COND(!area); area->set_spOv_mode(p_mode); } @@ -279,14 +279,14 @@ PhysicsServer::AreaSpaceOverrideMode BulletPhysicsServer::area_get_space_overrid return area->get_spOv_mode(); } -void BulletPhysicsServer::area_add_shape(RID p_area, RID p_shape, const Transform &p_transform) { +void BulletPhysicsServer::area_add_shape(RID p_area, RID p_shape, const Transform &p_transform, bool p_disabled) { AreaBullet *area = area_owner.get(p_area); ERR_FAIL_COND(!area); ShapeBullet *shape = shape_owner.get(p_shape); ERR_FAIL_COND(!shape); - area->add_shape(shape, p_transform); + area->add_shape(shape, p_transform, p_disabled); } void BulletPhysicsServer::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) { @@ -348,13 +348,13 @@ void BulletPhysicsServer::area_set_shape_disabled(RID p_area, int p_shape_idx, b area->set_shape_disabled(p_shape_idx, p_disabled); } -void BulletPhysicsServer::area_attach_object_instance_id(RID p_area, ObjectID p_ID) { +void BulletPhysicsServer::area_attach_object_instance_id(RID p_area, ObjectID p_id) { if (space_owner.owns(p_area)) { return; } AreaBullet *area = area_owner.get(p_area); ERR_FAIL_COND(!area); - area->set_instance_id(p_ID); + area->set_instance_id(p_id); } ObjectID BulletPhysicsServer::area_get_object_instance_id(RID p_area) const { @@ -498,7 +498,7 @@ PhysicsServer::BodyMode BulletPhysicsServer::body_get_mode(RID p_body) const { return body->get_mode(); } -void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transform &p_transform) { +void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transform &p_transform, bool p_disabled) { RigidBodyBullet *body = rigid_body_owner.get(p_body); ERR_FAIL_COND(!body); @@ -506,7 +506,7 @@ void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transfor ShapeBullet *shape = shape_owner.get(p_shape); ERR_FAIL_COND(!shape); - body->add_shape(shape, p_transform); + body->add_shape(shape, p_transform, p_disabled); } void BulletPhysicsServer::body_set_shape(RID p_body, int p_shape_idx, RID p_shape) { @@ -569,11 +569,11 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) { body->remove_all_shapes(); } -void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) { +void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_id) { CollisionObjectBullet *body = get_collisin_object(p_body); ERR_FAIL_COND(!body); - body->set_instance_id(p_ID); + body->set_instance_id(p_id); } uint32_t BulletPhysicsServer::body_get_object_instance_id(RID p_body) const { diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 0cea3f5ba6..4c598c84f2 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,7 +45,7 @@ */ class BulletPhysicsServer : public PhysicsServer { - GDCLASS(BulletPhysicsServer, PhysicsServer) + GDCLASS(BulletPhysicsServer, PhysicsServer); friend class BulletPhysicsDirectSpaceState; @@ -133,7 +133,7 @@ public: virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode); virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const; - virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform()); + virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape); virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform); virtual int area_get_shape_count(RID p_area) const; @@ -142,7 +142,7 @@ public: virtual void area_remove_shape(RID p_area, int p_shape_idx); virtual void area_clear_shapes(RID p_area); virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled); - virtual void area_attach_object_instance_id(RID p_area, ObjectID p_ID); + virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id); virtual ObjectID area_get_object_instance_id(RID p_area) const; /// If you pass as p_area the SpaceBullet you can set some parameters as specified below @@ -174,7 +174,7 @@ public: virtual void body_set_mode(RID p_body, BodyMode p_mode); virtual BodyMode body_get_mode(RID p_body) const; - virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform()); + virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); // Not supported, Please remove and add new shape virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape); virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform); @@ -189,7 +189,7 @@ public: virtual void body_clear_shapes(RID p_body); // Used for Rigid and Soft Bodies - virtual void body_attach_object_instance_id(RID p_body, uint32_t p_ID); + virtual void body_attach_object_instance_id(RID p_body, uint32_t p_id); virtual uint32_t body_get_object_instance_id(RID p_body) const; virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable); diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp index f9b7126173..fc007169c0 100644 --- a/modules/bullet/bullet_types_converter.cpp +++ b/modules/bullet/bullet_types_converter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/bullet_types_converter.h b/modules/bullet/bullet_types_converter.h index 84321fe837..ba36331d07 100644 --- a/modules/bullet/bullet_types_converter.h +++ b/modules/bullet/bullet_types_converter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #ifndef BULLET_TYPES_CONVERTER_H #define BULLET_TYPES_CONVERTER_H -#include "core/math/matrix3.h" +#include "core/math/basis.h" #include "core/math/transform.h" #include "core/math/vector3.h" #include "core/typedefs.h" diff --git a/modules/bullet/bullet_utilities.h b/modules/bullet/bullet_utilities.h index 553c1d0384..01a11ca63c 100644 --- a/modules/bullet/bullet_utilities.h +++ b/modules/bullet/bullet_utilities.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 441fa7c8af..e1800fd3eb 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,9 @@ @author AndreaCatania */ -#define enableDynamicAabbTree false +// We enable dynamic AABB tree so that we can actually perform a broadphase on bodies with compound collision shapes. +// This is crucial for the performance of kinematic bodies and for bodies with transforming shapes. +#define enableDynamicAabbTree true CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {} @@ -57,6 +59,25 @@ void CollisionObjectBullet::ShapeWrapper::set_transform(const btTransform &p_tra transform = p_transform; } +btTransform CollisionObjectBullet::ShapeWrapper::get_adjusted_transform() const { + if (shape->get_type() == PhysicsServer::SHAPE_HEIGHTMAP) { + const HeightMapShapeBullet *hm_shape = (const HeightMapShapeBullet *)shape; // should be safe to cast now + btTransform adjusted_transform; + + // Bullet centers our heightmap: + // https://github.com/bulletphysics/bullet3/blob/master/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h#L33 + // This is really counter intuitive so we're adjusting for it + + adjusted_transform.setIdentity(); + adjusted_transform.setOrigin(btVector3(0.0, hm_shape->min_height + ((hm_shape->max_height - hm_shape->min_height) * 0.5), 0.0)); + adjusted_transform *= transform; + + return adjusted_transform; + } else { + return transform; + } +} + void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) { if (!bt_shape) { if (active) @@ -69,8 +90,12 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s CollisionObjectBullet::CollisionObjectBullet(Type p_type) : RIDBullet(), type(p_type), + instance_id(0), + collisionLayer(0), + collisionMask(0), collisionsEnabled(true), m_isStatic(false), + ray_pickable(false), bt_collision_object(NULL), body_scale(1., 1., 1.), force_shape_reset(false), @@ -212,8 +237,8 @@ RigidCollisionObjectBullet::~RigidCollisionObjectBullet() { } } -void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform) { - shapes.push_back(ShapeWrapper(p_shape, p_transform, true)); +void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform, bool p_disabled) { + shapes.push_back(ShapeWrapper(p_shape, p_transform, !p_disabled)); p_shape->add_owner(this); reload_shapes(); } @@ -280,8 +305,7 @@ void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transfor ERR_FAIL_INDEX(p_index, get_shape_count()); shapes.write[p_index].set_transform(p_transform); - // Note, enableDynamicAabbTree is false because on transform change compound is destroyed - reload_shapes(); + shape_changed(p_index); } const btTransform &RigidCollisionObjectBullet::get_bt_shape_transform(int p_index) const { @@ -295,6 +319,8 @@ Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const { } void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) { + if (shapes[p_index].active != p_disabled) + return; shapes.write[p_index].active = !p_disabled; shape_changed(p_index); } @@ -338,7 +364,8 @@ void RigidCollisionObjectBullet::reload_shapes() { // Try to optimize by not using compound if (1 == shape_count) { shpWrapper = &shapes.write[0]; - if (shpWrapper->transform.getOrigin().isZero() && shpWrapper->transform.getBasis() == shpWrapper->transform.getBasis().getIdentity()) { + btTransform transform = shpWrapper->get_adjusted_transform(); + if (transform.getOrigin().isZero() && transform.getBasis() == transform.getBasis().getIdentity()) { shpWrapper->claim_bt_shape(body_scale); mainShape = shpWrapper->bt_shape; main_shape_changed(); @@ -352,7 +379,7 @@ void RigidCollisionObjectBullet::reload_shapes() { for (int i(0); i < shape_count; ++i) { shpWrapper = &shapes.write[i]; shpWrapper->claim_bt_shape(body_scale); - btTransform scaled_shape_transform(shpWrapper->transform); + btTransform scaled_shape_transform(shpWrapper->get_adjusted_transform()); scaled_shape_transform.getOrigin() *= body_scale; compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape); } diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h index 4a0c805ce5..c9430bec18 100644 --- a/modules/bullet/collision_object_bullet.h +++ b/modules/bullet/collision_object_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -109,6 +109,7 @@ public: void set_transform(const Transform &p_transform); void set_transform(const btTransform &p_transform); + btTransform get_adjusted_transform() const; void claim_bt_shape(const btVector3 &body_scale); }; @@ -224,7 +225,7 @@ public: _FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; } - void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform()); + void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); void set_shape(int p_index, ShapeBullet *p_shape); int get_shape_count() const; diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp index ecacce0bee..bc7fd52cf6 100644 --- a/modules/bullet/cone_twist_joint_bullet.cpp +++ b/modules/bullet/cone_twist_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -84,7 +84,7 @@ void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param, break; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/cone_twist_joint_bullet.h b/modules/bullet/cone_twist_joint_bullet.h index d6040fd6ec..7450a2e166 100644 --- a/modules/bullet/cone_twist_joint_bullet.h +++ b/modules/bullet/cone_twist_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/constraint_bullet.cpp b/modules/bullet/constraint_bullet.cpp index d15fb8de01..f3f3907ada 100644 --- a/modules/bullet/constraint_bullet.cpp +++ b/modules/bullet/constraint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/constraint_bullet.h b/modules/bullet/constraint_bullet.h index ed3a318cbc..f6ac3e80c2 100644 --- a/modules/bullet/constraint_bullet.h +++ b/modules/bullet/constraint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index a4dc61d0bc..078bcc45a8 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.1"> +<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 1486936cf4..2a37f6af5e 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.1"> +<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index 812dcd2d56..0d2c46c579 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -175,7 +175,7 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO break; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } @@ -256,7 +256,7 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF break; default: ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h index 848c3a10cd..478a39f103 100644 --- a/modules/bullet/generic_6dof_joint_bullet.h +++ b/modules/bullet/generic_6dof_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp index 919c3152d7..a77cf4ebd1 100644 --- a/modules/bullet/godot_collision_configuration.cpp +++ b/modules/bullet/godot_collision_configuration.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h index 11012c5f6d..82fb74f121 100644 --- a/modules/bullet/godot_collision_configuration.h +++ b/modules/bullet/godot_collision_configuration.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_dispatcher.cpp b/modules/bullet/godot_collision_dispatcher.cpp index 1815f2152e..5cb50d685f 100644 --- a/modules/bullet/godot_collision_dispatcher.cpp +++ b/modules/bullet/godot_collision_dispatcher.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h index 1faaa68626..d5fb3526fe 100644 --- a/modules/bullet/godot_collision_dispatcher.h +++ b/modules/bullet/godot_collision_dispatcher.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h index 5844ef8bf3..188389b33c 100644 --- a/modules/bullet/godot_motion_state.h +++ b/modules/bullet/godot_motion_state.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 27ee44d1bd..2ba75b9a98 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,12 +35,13 @@ #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h> -#define RAY_STABILITY_MARGIN 0.1 - /** @author AndreaCatania */ +// Epsilon to account for floating point inaccuracies +#define RAY_PENETRATION_DEPTH_EPSILON 0.01 + GodotRayWorldAlgorithm::CreateFunc::CreateFunc(const btDiscreteDynamicsWorld *world) : m_world(world) {} @@ -102,8 +103,8 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); - if (depth >= -RAY_STABILITY_MARGIN) - depth = 0; + if (depth > -RAY_PENETRATION_DEPTH_EPSILON) + depth = 0.0; if (ray_shape->getSlipsOnSlope()) resultOut->addContactPoint(btResult.m_hitNormalWorld, btResult.m_hitPointWorld, depth); diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h index 7383dad2bf..3285d0c962 100644 --- a/modules/bullet/godot_ray_world_algorithm.h +++ b/modules/bullet/godot_ray_world_algorithm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 0117bb375f..360950c4c7 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -304,6 +304,7 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp colObj = static_cast<CollisionObjectBullet *>(colObj1Wrap->getCollisionObject()->getUserPointer()); m_result->shape = cp.m_index1; B_TO_G(cp.getPositionWorldOnB(), m_result->point); + B_TO_G(cp.m_normalWorldOnB, m_result->normal); m_rest_info_bt_point = cp.getPositionWorldOnB(); m_rest_info_collision_object = colObj1Wrap->getCollisionObject(); } else { @@ -332,14 +333,6 @@ void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 m_other_compound_shape_index = isSwapped ? m_index0 : m_index1; m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB; - const btCollisionObjectWrapper *bw0 = m_body0Wrap; - if (isSwapped) - bw0 = m_body1Wrap; - - if (bw0->getCollisionShape()->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) { - m_pointNormalWorld = bw0->m_worldTransform.getBasis().transpose() * btVector3(0, 0, 1); - } else { - m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; - } + m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; } } diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 73e1fc9627..f292b0a797 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp index 3a4459a581..b7e1e1a4c2 100644 --- a/modules/bullet/hinge_joint_bullet.cpp +++ b/modules/bullet/hinge_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -118,7 +118,7 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t break; default: ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/hinge_joint_bullet.h b/modules/bullet/hinge_joint_bullet.h index ca87c8dd8c..6555c23848 100644 --- a/modules/bullet/hinge_joint_bullet.h +++ b/modules/bullet/hinge_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/joint_bullet.cpp b/modules/bullet/joint_bullet.cpp index aaeb9f9ce7..a52b05f5c4 100644 --- a/modules/bullet/joint_bullet.cpp +++ b/modules/bullet/joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/joint_bullet.h b/modules/bullet/joint_bullet.h index 4a5333fb85..d2c2796367 100644 --- a/modules/bullet/joint_bullet.h +++ b/modules/bullet/joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp index 183a7e75b9..c9c4d1af7e 100644 --- a/modules/bullet/pin_joint_bullet.cpp +++ b/modules/bullet/pin_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -86,7 +86,7 @@ real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const { return p2pConstraint->m_setting.m_impulseClamp; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; return 0; } } diff --git a/modules/bullet/pin_joint_bullet.h b/modules/bullet/pin_joint_bullet.h index 648010bf78..7c0c5be329 100644 --- a/modules/bullet/pin_joint_bullet.h +++ b/modules/bullet/pin_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp index 31e5f6784e..03fb75cc82 100644 --- a/modules/bullet/register_types.cpp +++ b/modules/bullet/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/register_types.h b/modules/bullet/register_types.h index 226bcd9402..ebb875eb1f 100644 --- a/modules/bullet/register_types.h +++ b/modules/bullet/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/rid_bullet.h b/modules/bullet/rid_bullet.h index a9351d7728..91ad8164e6 100644 --- a/modules/bullet/rid_bullet.h +++ b/modules/bullet/rid_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 9dd04100ed..8d21b25b20 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -597,6 +597,8 @@ void RigidBodyBullet::set_state(PhysicsServer::BodyState p_state, const Variant if (!can_sleep) { // Can't sleep btBody->forceActivationState(DISABLE_DEACTIVATION); + } else { + btBody->forceActivationState(ACTIVE_TAG); } break; } @@ -726,12 +728,12 @@ bool RigidBodyBullet::is_axis_locked(PhysicsServer::BodyAxis p_axis) const { void RigidBodyBullet::reload_axis_lock() { - btBody->setLinearFactor(btVector3(!is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_X), !is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_Y), !is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_Z))); + btBody->setLinearFactor(btVector3(float(!is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_X)), float(!is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_Y)), float(!is_axis_locked(PhysicsServer::BODY_AXIS_LINEAR_Z)))); if (PhysicsServer::BODY_MODE_CHARACTER == mode) { /// When character angular is always locked btBody->setAngularFactor(btVector3(0., 0., 0.)); } else { - btBody->setAngularFactor(btVector3(!is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_X), !is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_Y), !is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_Z))); + btBody->setAngularFactor(btVector3(float(!is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_X)), float(!is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_Y)), float(!is_axis_locked(PhysicsServer::BODY_AXIS_ANGULAR_Z)))); } } @@ -739,22 +741,20 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) { if (p_enable) { // This threshold enable CCD if the object moves more than // 1 meter in one simulation frame - btBody->setCcdMotionThreshold(0.1); + btBody->setCcdMotionThreshold(1e-7); /// Calculate using the rule writte below the CCD swept sphere radius /// CCD works on an embedded sphere of radius, make sure this radius /// is embedded inside the convex objects, preferably smaller: /// for an object of dimensions 1 meter, try 0.2 - btScalar radius; + btScalar radius(1.0); if (btBody->getCollisionShape()) { btVector3 center; btBody->getCollisionShape()->getBoundingSphere(center, radius); - } else { - radius = 0; } btBody->setCcdSweptSphereRadius(radius * 0.2); } else { - btBody->setCcdMotionThreshold(0.); + btBody->setCcdMotionThreshold(10000.0); btBody->setCcdSweptSphereRadius(0.); } } @@ -797,6 +797,9 @@ void RigidBodyBullet::set_transform__bullet(const btTransform &p_global_transfor btBody->setLinearVelocity((p_global_transform.getOrigin() - btBody->getWorldTransform().getOrigin()) / space->get_delta_time()); // The kinematic use MotionState class godotMotionState->moveBody(p_global_transform); + } else { + // Is necesasry to avoid wrong location on the rendering side on the next frame + godotMotionState->setWorldTransform(p_global_transform); } CollisionObjectBullet::set_transform__bullet(p_global_transform); } @@ -822,13 +825,14 @@ void RigidBodyBullet::reload_shapes() { // shapes incorrectly do not set the vector in calculateLocalIntertia. // Arbitrary zero is preferable to undefined behaviour. btVector3 inertia(0, 0, 0); - mainShape->calculateLocalInertia(mass, inertia); + if (EMPTY_SHAPE_PROXYTYPE != mainShape->getShapeType()) // Necessary to avoid assertion of the empty shape + mainShape->calculateLocalInertia(mass, inertia); btBody->setMassProps(mass, inertia); } btBody->updateInertiaTensor(); reload_kinematic_shapes(); - + set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0); reload_body(); } @@ -862,7 +866,7 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) { if (p_area->is_spOv_gravityPoint()) { ++countGravityPointSpaces; - assert(0 < countGravityPointSpaces); + ERR_FAIL_COND(countGravityPointSpaces <= 0); } } @@ -884,7 +888,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { if (wasTheAreaFound) { if (p_area->is_spOv_gravityPoint()) { --countGravityPointSpaces; - assert(0 <= countGravityPointSpaces); + ERR_FAIL_COND(countGravityPointSpaces < 0); } --areaWhereIamCount; diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 0696073d21..2c9bdb8b0b 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,7 +54,7 @@ class BulletPhysicsDirectBodyState; /// created by BulletPhysicsServer and is held by the "singleton" variable of this class /// Each time something require it, the body must be set again. class BulletPhysicsDirectBodyState : public PhysicsDirectBodyState { - GDCLASS(BulletPhysicsDirectBodyState, PhysicsDirectBodyState) + GDCLASS(BulletPhysicsDirectBodyState, PhysicsDirectBodyState); static BulletPhysicsDirectBodyState *singleton; @@ -167,7 +167,7 @@ public: KinematicShape() : shape(NULL) {} - const bool is_active() const { return shape; } + bool is_active() const { return shape; } }; struct KinematicUtilities { diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 8bb621a863..f15bcec914 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -148,7 +148,13 @@ btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<rea const bool flipQuadEdges = false; const void *heightsPtr = p_heights.read().ptr(); - return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); + btHeightfieldTerrainShape *heightfield = bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); + + // The shape can be created without params when you do PhysicsServer.shape_create(PhysicsServer.SHAPE_HEIGHTMAP) + if (heightsPtr) + heightfield->buildAccelerator(16); + + return heightfield; } btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) { @@ -461,7 +467,47 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { int l_width = d["width"]; int l_depth = d["depth"]; - PoolVector<real_t> l_heights = d["heights"]; + + // TODO This code will need adjustments if real_t is set to `double`, + // because that precision is unnecessary for a heightmap and Bullet doesn't support it... + + PoolVector<real_t> l_heights; + Variant l_heights_v = d["heights"]; + + if (l_heights_v.get_type() == Variant::POOL_REAL_ARRAY) { + // Ready-to-use heights can be passed + + l_heights = l_heights_v; + + } else if (l_heights_v.get_type() == Variant::OBJECT) { + // If an image is passed, we have to convert it to a format Bullet supports. + // this would be expensive to do with a script, so it's nice to have it here. + + Ref<Image> l_image = l_heights_v; + ERR_FAIL_COND(l_image.is_null()); + + // Float is the only common format between Godot and Bullet that can be used for decent collision. + // (Int16 would be nice too but we still don't have it) + // We could convert here automatically but it's better to not be intrusive and let the caller do it if necessary. + ERR_FAIL_COND(l_image->get_format() != Image::FORMAT_RF); + + PoolByteArray im_data = l_image->get_data(); + + l_heights.resize(l_image->get_width() * l_image->get_height()); + + PoolRealArray::Write w = l_heights.write(); + PoolByteArray::Read r = im_data.read(); + float *rp = (float *)r.ptr(); + // At this point, `rp` could be used directly for Bullet, but I don't know how safe it would be. + + for (int i = 0; i < l_heights.size(); ++i) { + w[i] = rp[i]; + } + + } else { + ERR_EXPLAIN("Expected PoolRealArray or float Image."); + ERR_FAIL(); + } ERR_FAIL_COND(l_width <= 0); ERR_FAIL_COND(l_depth <= 0); @@ -470,16 +516,17 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { // Compute min and max heights if not specified. if (!d.has("min_height") && !d.has("max_height")) { - PoolVector<real_t>::Read r = heights.read(); - int heights_size = heights.size(); + PoolVector<real_t>::Read r = l_heights.read(); + int heights_size = l_heights.size(); for (int i = 0; i < heights_size; ++i) { real_t h = r[i]; - if (h < l_min_height) + if (h < l_min_height) { l_min_height = h; - else if (h > l_max_height) + } else if (h > l_max_height) { l_max_height = h; + } } } @@ -497,19 +544,8 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const { void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes - { // Copy - - // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields - // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data). - - const int heights_size = p_heights.size(); - heights.resize(heights_size); - PoolVector<real_t>::Read p_heights_r = p_heights.read(); - PoolVector<real_t>::Write heights_w = heights.write(); - for (int i = heights_size - 1; 0 <= i; --i) { - heights_w[i] = p_heights_r[i]; - } - } + // If this array is resized outside of here, it should be preserved due to CoW + heights = p_heights; width = p_width; depth = p_depth; diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 9a1c8f5bfa..39d0ba7b95 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/shape_owner_bullet.cpp b/modules/bullet/shape_owner_bullet.cpp index d6ba5d81bc..d504db1844 100644 --- a/modules/bullet/shape_owner_bullet.cpp +++ b/modules/bullet/shape_owner_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/shape_owner_bullet.h b/modules/bullet/shape_owner_bullet.h index 72ddbc482c..02852bc03d 100644 --- a/modules/bullet/shape_owner_bullet.h +++ b/modules/bullet/shape_owner_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp index 9016ec3bf5..45be0578d9 100644 --- a/modules/bullet/slider_joint_bullet.cpp +++ b/modules/bullet/slider_joint_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/slider_joint_bullet.h b/modules/bullet/slider_joint_bullet.h index d532906c0d..fa9ea3f782 100644 --- a/modules/bullet/slider_joint_bullet.h +++ b/modules/bullet/slider_joint_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 94f350210f..88190f8e9e 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index d04bfca046..5e05c266a8 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index fed12cd5ed..738b415d16 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -658,6 +658,8 @@ void SpaceBullet::check_ghost_overlaps() { for (x = areas.size() - 1; 0 <= x; --x) { area = areas[x]; + btVector3 area_scale(area->get_bt_body_scale()); + if (!area->is_monitoring()) continue; @@ -678,11 +680,15 @@ void SpaceBullet::check_ghost_overlaps() { // For each overlapping for (i = ghostOverlaps.size() - 1; 0 <= i; --i) { + bool hasOverlap = false; btCollisionObject *overlapped_bt_co = ghostOverlaps[i]; RigidCollisionObjectBullet *otherObject = static_cast<RigidCollisionObjectBullet *>(overlapped_bt_co->getUserPointer()); + btVector3 other_body_scale(otherObject->get_bt_body_scale()); - if (!area->is_transform_changed() && !otherObject->is_transform_changed()) - continue; + if (!area->is_transform_changed() && !otherObject->is_transform_changed()) { + hasOverlap = -1 != area->find_overlapping_object(otherObject); + goto collision_found; + } if (overlapped_bt_co->getUserIndex() == CollisionObjectBullet::TYPE_AREA) { if (!static_cast<AreaBullet *>(overlapped_bt_co->getUserPointer())->is_monitorable()) @@ -690,26 +696,43 @@ void SpaceBullet::check_ghost_overlaps() { } else if (overlapped_bt_co->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) continue; - bool hasOverlap = false; - // For each area shape for (y = area->get_shape_count() - 1; 0 <= y; --y) { if (!area->get_bt_shape(y)->isConvex()) continue; - gjk_input.m_transformA = area->get_transform__bullet() * area->get_bt_shape_transform(y); + btTransform area_shape_treansform(area->get_bt_shape_transform(y)); + area_shape_treansform.getOrigin() *= area_scale; + + gjk_input.m_transformA = + area->get_transform__bullet() * + area_shape_treansform; + area_shape = static_cast<btConvexShape *>(area->get_bt_shape(y)); // For each other object shape for (z = otherObject->get_shape_count() - 1; 0 <= z; --z) { other_body_shape = static_cast<btCollisionShape *>(otherObject->get_bt_shape(z)); - gjk_input.m_transformB = otherObject->get_transform__bullet() * otherObject->get_bt_shape_transform(z); + + if (other_body_shape->isConcave()) + continue; + + btTransform other_shape_transform(otherObject->get_bt_shape_transform(z)); + other_shape_transform.getOrigin() *= other_body_scale; + + gjk_input.m_transformB = + otherObject->get_transform__bullet() * + other_shape_transform; if (other_body_shape->isConvex()) { btPointCollector result; - btGjkPairDetector gjk_pair_detector(area_shape, static_cast<btConvexShape *>(other_body_shape), gjk_simplex_solver, gjk_epa_pen_solver); + btGjkPairDetector gjk_pair_detector( + area_shape, + static_cast<btConvexShape *>(other_body_shape), + gjk_simplex_solver, + gjk_epa_pen_solver); gjk_pair_detector.getClosestPoints(gjk_input, result, 0); if (0 >= result.m_distance) { @@ -1020,23 +1043,16 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p btVector3 recover_motion(0, 0, 0); int rays_found = 0; + int rays_found_this_round = 0; for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { - int last_ray_index = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max, recover_motion, r_results); + PhysicsServer::SeparationResult *next_results = &r_results[rays_found]; + rays_found_this_round = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max - rays_found, recover_motion, next_results); - rays_found = MAX(last_ray_index, rays_found); - if (!rays_found) { - break; - } else { + rays_found += rays_found_this_round; + if (rays_found_this_round == 0) { body_transform.getOrigin() += recover_motion; - } - } - - //optimize results (remove non colliding) - for (int i = 0; i < rays_found; i++) { - if (r_results[i].collision_depth >= 0) { - rays_found--; - SWAP(r_results[i], r_results[rays_found]); + break; } } @@ -1046,18 +1062,47 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p struct RecoverPenetrationBroadPhaseCallback : public btBroadphaseAabbCallback { private: + btDbvtVolume bounds; + const btCollisionObject *self_collision_object; uint32_t collision_layer; uint32_t collision_mask; + struct CompoundLeafCallback : btDbvt::ICollide { + private: + RecoverPenetrationBroadPhaseCallback *parent_callback; + btCollisionObject *collision_object; + + public: + CompoundLeafCallback(RecoverPenetrationBroadPhaseCallback *p_parent_callback, btCollisionObject *p_collision_object) : + parent_callback(p_parent_callback), + collision_object(p_collision_object) { + } + + void Process(const btDbvtNode *leaf) { + BroadphaseResult result; + result.collision_object = collision_object; + result.compound_child_index = leaf->dataAsInt; + parent_callback->results.push_back(result); + } + }; + public: - Vector<btCollisionObject *> result_collision_objects; + struct BroadphaseResult { + btCollisionObject *collision_object; + int compound_child_index; + }; + + Vector<BroadphaseResult> results; public: - RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, uint32_t p_collision_mask) : + RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, uint32_t p_collision_mask, btVector3 p_aabb_min, btVector3 p_aabb_max) : self_collision_object(p_self_collision_object), collision_layer(p_collision_layer), - collision_mask(p_collision_mask) {} + collision_mask(p_collision_mask) { + + bounds = btDbvtVolume::FromMM(p_aabb_min, p_aabb_max); + } virtual ~RecoverPenetrationBroadPhaseCallback() {} @@ -1066,35 +1111,98 @@ public: btCollisionObject *co = static_cast<btCollisionObject *>(proxy->m_clientObject); if (co->getInternalType() <= btCollisionObject::CO_RIGID_BODY) { if (self_collision_object != proxy->m_clientObject && GodotFilterCallback::test_collision_filters(collision_layer, collision_mask, proxy->m_collisionFilterGroup, proxy->m_collisionFilterMask)) { - result_collision_objects.push_back(co); + if (co->getCollisionShape()->isCompound()) { + const btCompoundShape *cs = static_cast<btCompoundShape *>(co->getCollisionShape()); + + if (cs->getNumChildShapes() > 1) { + const btDbvt *tree = cs->getDynamicAabbTree(); + ERR_FAIL_COND_V(tree == NULL, true); + + // Transform bounds into compound shape local space + const btTransform other_in_compound_space = co->getWorldTransform().inverse(); + const btMatrix3x3 abs_b = other_in_compound_space.getBasis().absolute(); + const btVector3 local_center = other_in_compound_space(bounds.Center()); + const btVector3 local_extent = bounds.Extents().dot3(abs_b[0], abs_b[1], abs_b[2]); + const btVector3 local_aabb_min = local_center - local_extent; + const btVector3 local_aabb_max = local_center + local_extent; + const btDbvtVolume local_bounds = btDbvtVolume::FromMM(local_aabb_min, local_aabb_max); + + // Test collision against compound child shapes using its AABB tree + CompoundLeafCallback compound_leaf_callback(this, co); + tree->collideTV(tree->m_root, local_bounds, compound_leaf_callback); + } else { + // If there's only a single child shape then there's no need to search any more, we know which child overlaps + BroadphaseResult result; + result.collision_object = co; + result.compound_child_index = 0; + results.push_back(result); + } + } else { + BroadphaseResult result; + result.collision_object = co; + result.compound_child_index = -1; + results.push_back(result); + } return true; } } return false; } - - void reset() { - result_collision_objects.clear(); - } }; bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) { - RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask()); + // Calculate the cumulative AABB of all shapes of the kinematic body + btVector3 aabb_min, aabb_max; + bool shapes_found = false; + + for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) { + + const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]); + if (!kin_shape.is_active()) { + continue; + } + + if (kin_shape.shape->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) { + // Skip rayshape in order to implement custom separation process + continue; + } + + btTransform shape_transform = p_body_position * kin_shape.transform; + shape_transform.getOrigin() += r_delta_recover_movement; - btTransform body_shape_position; - btTransform body_shape_position_recovered; + btVector3 shape_aabb_min, shape_aabb_max; + kin_shape.shape->getAabb(shape_transform, shape_aabb_min, shape_aabb_max); - // Broad phase support - btVector3 minAabb, maxAabb; + if (!shapes_found) { + aabb_min = shape_aabb_min; + aabb_max = shape_aabb_max; + shapes_found = true; + } else { + aabb_min.setX((aabb_min.x() < shape_aabb_min.x()) ? aabb_min.x() : shape_aabb_min.x()); + aabb_min.setY((aabb_min.y() < shape_aabb_min.y()) ? aabb_min.y() : shape_aabb_min.y()); + aabb_min.setZ((aabb_min.z() < shape_aabb_min.z()) ? aabb_min.z() : shape_aabb_min.z()); + + aabb_max.setX((aabb_max.x() > shape_aabb_max.x()) ? aabb_max.x() : shape_aabb_max.x()); + aabb_max.setY((aabb_max.y() > shape_aabb_max.y()) ? aabb_max.y() : shape_aabb_max.y()); + aabb_max.setZ((aabb_max.z() > shape_aabb_max.z()) ? aabb_max.z() : shape_aabb_max.z()); + } + } + + // If there are no shapes then there is no penetration either + if (!shapes_found) { + return false; + } + + // Perform broadphase test + RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max); + dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result); bool penetration = false; - // For each shape + // Perform narrowphase per shape for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) { - recover_broad_result.reset(); - const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]); if (!kin_shape.is_active()) { continue; @@ -1105,15 +1213,11 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran continue; } - body_shape_position = p_body_position * kin_shape.transform; - body_shape_position_recovered = body_shape_position; - body_shape_position_recovered.getOrigin() += r_delta_recover_movement; + btTransform shape_transform = p_body_position * kin_shape.transform; + shape_transform.getOrigin() += r_delta_recover_movement; - kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb); - dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result); - - for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) { - btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i]; + for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) { + btCollisionObject *otherObject = recover_broad_result.results[i].collision_object; if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { otherObject->activate(); // Force activation of hitten rigid, soft body continue; @@ -1121,30 +1225,28 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran continue; if (otherObject->getCollisionShape()->isCompound()) { + const btCompoundShape *cs = static_cast<const btCompoundShape *>(otherObject->getCollisionShape()); + int shape_idx = recover_broad_result.results[i].compound_child_index; + ERR_FAIL_COND_V(shape_idx < 0 || shape_idx >= cs->getNumChildShapes(), false); - // Each convex shape - btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape()); - for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) { - - if (cs->getChildShape(x)->isConvex()) { - if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(x)), otherObject, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { + if (cs->getChildShape(shape_idx)->isConvex()) { + if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(shape_idx)), otherObject, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { - penetration = true; - } - } else { - if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { + penetration = true; + } + } else { + if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(shape_idx), p_body->get_bt_collision_object(), otherObject, kinIndex, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { - penetration = true; - } + penetration = true; } } } else if (otherObject->getCollisionShape()->isConvex()) { /// Execute GJK test against object shape - if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), otherObject, 0, body_shape_position, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { + if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), otherObject, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { penetration = true; } } else { - if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, body_shape_position, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { + if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) { penetration = true; } @@ -1160,7 +1262,6 @@ bool SpaceBullet::RFP_convex_convex_test(const btConvexShape *p_shapeA, const bt // Initialize GJK input btGjkPairDetector::ClosestPointInput gjk_input; gjk_input.m_transformA = p_transformA; - gjk_input.m_transformA.getOrigin() += r_delta_recover_movement; gjk_input.m_transformB = p_transformB; // Perform GJK test @@ -1191,7 +1292,6 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC /// Contact test btTransform tA(p_transformA); - tA.getOrigin() += r_delta_recover_movement; btCollisionObjectWrapper obA(NULL, p_shapeA, p_objectA, tA, -1, p_shapeId_A); btCollisionObjectWrapper obB(NULL, p_shapeB, p_objectB, p_transformB, -1, p_shapeId_B); @@ -1223,24 +1323,81 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC return false; } -int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) { +int SpaceBullet::add_separation_result(PhysicsServer::SeparationResult *r_result, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const { + + // optimize results (ignore non-colliding) + if (p_recover_result.penetration_distance < 0.0) { + const btRigidBody *btRigid = static_cast<const btRigidBody *>(p_other_object); + CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(p_other_object->getUserPointer()); - RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask()); + r_result->collision_depth = p_recover_result.penetration_distance; + B_TO_G(p_recover_result.pointWorld, r_result->collision_point); + B_TO_G(p_recover_result.normal, r_result->collision_normal); + B_TO_G(btRigid->getVelocityInLocalPoint(p_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); + r_result->collision_local_shape = p_shape_id; + r_result->collider_id = collisionObject->get_instance_id(); + r_result->collider = collisionObject->get_self(); + r_result->collider_shape = p_recover_result.other_compound_shape_index; - btTransform body_shape_position; - btTransform body_shape_position_recovered; + return 1; + } else { + return 0; + } +} - // Broad phase support - btVector3 minAabb, maxAabb; +int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) { - int ray_index = 0; + // Calculate the cumulative AABB of all shapes of the kinematic body + btVector3 aabb_min, aabb_max; + bool shapes_found = false; - // For each shape for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) { - recover_broad_result.reset(); + const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]); + if (!kin_shape.is_active()) { + continue; + } - if (ray_index >= p_result_max) { + if (kin_shape.shape->getShapeType() != CUSTOM_CONVEX_SHAPE_TYPE) { + continue; + } + + btTransform shape_transform = p_body_position * kin_shape.transform; + shape_transform.getOrigin() += r_delta_recover_movement; + + btVector3 shape_aabb_min, shape_aabb_max; + kin_shape.shape->getAabb(shape_transform, shape_aabb_min, shape_aabb_max); + + if (!shapes_found) { + aabb_min = shape_aabb_min; + aabb_max = shape_aabb_max; + shapes_found = true; + } else { + aabb_min.setX((aabb_min.x() < shape_aabb_min.x()) ? aabb_min.x() : shape_aabb_min.x()); + aabb_min.setY((aabb_min.y() < shape_aabb_min.y()) ? aabb_min.y() : shape_aabb_min.y()); + aabb_min.setZ((aabb_min.z() < shape_aabb_min.z()) ? aabb_min.z() : shape_aabb_min.z()); + + aabb_max.setX((aabb_max.x() > shape_aabb_max.x()) ? aabb_max.x() : shape_aabb_max.x()); + aabb_max.setY((aabb_max.y() > shape_aabb_max.y()) ? aabb_max.y() : shape_aabb_max.y()); + aabb_max.setZ((aabb_max.z() > shape_aabb_max.z()) ? aabb_max.z() : shape_aabb_max.z()); + } + } + + // If there are no shapes then there is no penetration either + if (!shapes_found) { + return 0; + } + + // Perform broadphase test + RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max); + dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result); + + int ray_count = 0; + + // Perform narrowphase per shape + for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) { + + if (ray_count >= p_result_max) { break; } @@ -1253,15 +1410,11 @@ int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btT continue; } - body_shape_position = p_body_position * kin_shape.transform; - body_shape_position_recovered = body_shape_position; - body_shape_position_recovered.getOrigin() += r_delta_recover_movement; + btTransform shape_transform = p_body_position * kin_shape.transform; + shape_transform.getOrigin() += r_delta_recover_movement; - kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb); - dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result); - - for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) { - btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i]; + for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) { + btCollisionObject *otherObject = recover_broad_result.results[i].collision_object; if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { otherObject->activate(); // Force activation of hitten rigid, soft body continue; @@ -1269,32 +1422,25 @@ int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btT continue; if (otherObject->getCollisionShape()->isCompound()) { + const btCompoundShape *cs = static_cast<const btCompoundShape *>(otherObject->getCollisionShape()); + int shape_idx = recover_broad_result.results[i].compound_child_index; + ERR_FAIL_COND_V(shape_idx < 0 || shape_idx >= cs->getNumChildShapes(), false); - // Each convex shape - btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape()); - for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) { + RecoverResult recover_result; + if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(shape_idx), p_body->get_bt_collision_object(), otherObject, kinIndex, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) { - RecoverResult r_recover_result; - if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, &r_recover_result)) { + ray_count = add_separation_result(&r_results[ray_count], recover_result, kinIndex, otherObject); + } + } else { - const btRigidBody *btRigid = static_cast<const btRigidBody *>(otherObject); - CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(otherObject->getUserPointer()); + RecoverResult recover_result; + if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) { - r_results[ray_index].collision_depth = r_recover_result.penetration_distance; - B_TO_G(r_recover_result.pointWorld, r_results[ray_index].collision_point); - B_TO_G(r_recover_result.normal, r_results[ray_index].collision_normal); - B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_results[ray_index].collider_velocity); - r_results[ray_index].collision_local_shape = kinIndex; - r_results[ray_index].collider_id = collisionObject->get_instance_id(); - r_results[ray_index].collider = collisionObject->get_self(); - r_results[ray_index].collider_shape = r_recover_result.other_compound_shape_index; - } + ray_count = add_separation_result(&r_results[ray_count], recover_result, kinIndex, otherObject); } } } - - ++ray_index; } - return ray_index; + return ray_count; } diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index c3d55cbbb1..eb4a065e54 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,7 +68,8 @@ class btGjkEpaPenetrationDepthSolver; extern ContactAddedCallback gContactAddedCallback; class BulletPhysicsDirectSpaceState : public PhysicsDirectSpaceState { - GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState) + GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState); + private: SpaceBullet *space; @@ -212,6 +213,7 @@ private: /// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + int add_separation_result(PhysicsServer::SeparationResult *r_results, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const; int recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results); }; #endif diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 88f3c0338a..fd0d36eddf 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "core/math/face3.h" #include "core/math/geometry.h" #include "core/os/os.h" -#include "core/sort.h" +#include "core/sort_array.h" #include "thirdparty/misc/triangulator.h" void CSGBrush::clear() { @@ -45,7 +45,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo int vc = p_vertices.size(); - ERR_FAIL_COND((vc % 3) != 0) + ERR_FAIL_COND((vc % 3) != 0); PoolVector<Vector3>::Read rv = p_vertices.read(); int uvc = p_uvs.size(); @@ -242,7 +242,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ //check if edge and poly share a vertex, of so, assign it to segment_idx for (int i = 0; i < points.size(); i++) { for (int j = 0; j < 2; j++) { - if (segment[j].distance_to(points[i].point) < CMP_EPSILON) { + if (Math::is_zero_approx(segment[j].distance_to(points[i].point))) { segment_idx[j] = i; inserted_points.push_back(i); break; @@ -310,7 +310,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point }; Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg); - if (closest.distance_to(segment[j]) < CMP_EPSILON) { + if (Math::is_zero_approx(closest.distance_to(segment[j]))) { //point rest of this edge res = closest; found = true; @@ -439,7 +439,7 @@ void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, Mes //transform A points to 2D - if (segment[0].distance_to(segment[1]) < CMP_EPSILON) + if (Math::is_zero_approx(segment[0].distance_to(segment[1]))) return; //too small _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B); @@ -461,10 +461,10 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map { //check if either is a degenerate - if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON) + if (Math::is_zero_approx(va[0].distance_to(va[1])) || Math::is_zero_approx(va[0].distance_to(va[2])) || Math::is_zero_approx(va[1].distance_to(va[2]))) return; - if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON) + if (Math::is_zero_approx(vb[0].distance_to(vb[1])) || Math::is_zero_approx(vb[0].distance_to(vb[2])) || Math::is_zero_approx(vb[1].distance_to(vb[2]))) return; } @@ -611,7 +611,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in { EdgeSort es; - es.angle = 0; //wont be checked here + es.angle = 0; //won't be checked here es.edge = p_edge; es.prev_point = p_from_point; es.edge_point = p_to_point; @@ -666,14 +666,14 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in if (opposite_point == prev_point) continue; //not going back - EdgeSort e; + EdgeSort e2; Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point); - e.angle = -local_vec.angle(); //negate so we can sort by minimum angle - e.edge = edge; - e.edge_point = opposite_point; - e.prev_point = to_point; + e2.angle = -local_vec.angle(); //negate so we can sort by minimum angle + e2.edge = edge; + e2.edge_point = opposite_point; + e2.prev_point = to_point; - next_edges.push_back(e); + next_edges.push_back(e2); } //finally, sort by minimum angle @@ -953,13 +953,15 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build //duplicate point int insert_at = with_outline_vertex; - polys.write[i].points.insert(insert_at, polys[i].points[insert_at]); + int point = polys[i].points[insert_at]; + polys.write[i].points.insert(insert_at, point); insert_at++; //insert all others, outline should be backwards (must check) int holesize = polys[i].holes[j].size(); for (int k = 0; k <= holesize; k++) { int idx = (from_hole_vertex + k) % holesize; - polys.write[i].points.insert(insert_at, polys[i].holes[j][idx]); + int point2 = polys[i].holes[j][idx]; + polys.write[i].points.insert(insert_at, point2); insert_at++; } @@ -1016,15 +1018,15 @@ int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from max_depth = p_depth; } - if (p_size <= BVH_LIMIT) { + if (p_size == 0) { + + return -1; + } else if (p_size <= BVH_LIMIT) { for (int i = 0; i < p_size - 1; i++) { p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh; } return p_bb[p_from] - p_bvh; - } else if (p_size == 0) { - - return -1; } AABB aabb; diff --git a/modules/csg/csg.h b/modules/csg/csg.h index 5d6432eca8..4fa1a945cc 100644 --- a/modules/csg/csg.h +++ b/modules/csg/csg.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,6 @@ #ifndef CSG_H #define CSG_H -#include "core/dvector.h" #include "core/map.h" #include "core/math/aabb.h" #include "core/math/plane.h" @@ -39,6 +38,7 @@ #include "core/math/transform.h" #include "core/math/vector3.h" #include "core/oa_hash_map.h" +#include "core/pool_vector.h" #include "scene/resources/material.h" struct CSGBrush { diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index 5864d02615..e6bfa5525d 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,8 +34,18 @@ CSGShapeSpatialGizmoPlugin::CSGShapeSpatialGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1)); - create_material("shape_material", gizmo_color); + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15)); + create_material("shape_union_material", gizmo_color); + create_material("shape_union_solid_material", gizmo_color); + gizmo_color.invert(); + create_material("shape_subtraction_material", gizmo_color); + create_material("shape_subtraction_solid_material", gizmo_color); + gizmo_color.r = 0.95; + gizmo_color.g = 0.95; + gizmo_color.b = 0.95; + create_material("shape_intersection_material", gizmo_color); + create_material("shape_intersection_solid_material", gizmo_color); + create_handle_material("handles"); } @@ -120,6 +130,10 @@ void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -135,6 +149,10 @@ void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -154,6 +172,9 @@ void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } if (d < 0.001) d = 0.001; @@ -173,6 +194,9 @@ void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } if (d < 0.001) d = 0.001; @@ -283,6 +307,10 @@ String CSGShapeSpatialGizmoPlugin::get_name() const { return "CSGShapes"; } +int CSGShapeSpatialGizmoPlugin::get_priority() const { + return -1; +} + bool CSGShapeSpatialGizmoPlugin::is_selectable_when_hidden() const { return true; } @@ -293,7 +321,19 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->clear(); - Ref<Material> material = get_material("shape_material", p_gizmo); + Ref<Material> material; + switch (cs->get_operation()) { + case CSGShape::OPERATION_UNION: + material = get_material("shape_union_material", p_gizmo); + break; + case CSGShape::OPERATION_INTERSECTION: + material = get_material("shape_intersection_material", p_gizmo); + break; + case CSGShape::OPERATION_SUBTRACTION: + material = get_material("shape_subtraction_material", p_gizmo); + break; + } + Ref<Material> handles_material = get_material("handles"); PoolVector<Vector3> faces = cs->get_brush_faces(); @@ -316,6 +356,30 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->add_lines(lines, material); p_gizmo->add_collision_segments(lines); + if (p_gizmo->is_selected()) { + // Draw a translucent representation of the CSG node + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Array array; + array.resize(Mesh::ARRAY_MAX); + array[Mesh::ARRAY_VERTEX] = faces; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); + + Ref<Material> solid_material; + switch (cs->get_operation()) { + case CSGShape::OPERATION_UNION: + solid_material = get_material("shape_union_solid_material", p_gizmo); + break; + case CSGShape::OPERATION_INTERSECTION: + solid_material = get_material("shape_intersection_solid_material", p_gizmo); + break; + case CSGShape::OPERATION_SUBTRACTION: + solid_material = get_material("shape_subtraction_solid_material", p_gizmo); + break; + } + + p_gizmo->add_mesh(mesh, false, RID(), solid_material); + } + if (Object::cast_to<CSGSphere>(cs)) { CSGSphere *s = Object::cast_to<CSGSphere>(cs); @@ -356,5 +420,5 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) { Ref<CSGShapeSpatialGizmoPlugin> gizmo_plugin = Ref<CSGShapeSpatialGizmoPlugin>(memnew(CSGShapeSpatialGizmoPlugin)); - SpatialEditor::get_singleton()->register_gizmo_plugin(gizmo_plugin); + SpatialEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); } diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index d65d1f58c1..d38dafc936 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,6 +42,7 @@ class CSGShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { public: bool has_gizmo(Spatial *p_spatial); String get_name() const; + int get_priority() const; bool is_selectable_when_hidden() const; void redraw(EditorSpatialGizmo *p_gizmo); @@ -54,7 +55,8 @@ public: }; class EditorPluginCSG : public EditorPlugin { - GDCLASS(EditorPluginCSG, EditorPlugin) + GDCLASS(EditorPluginCSG, EditorPlugin); + public: EditorPluginCSG(EditorNode *p_editor); }; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 4e35014459..23725c4960 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,18 +48,76 @@ void CSGShape::set_use_collision(bool p_enable) { PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); + set_collision_layer(collision_layer); + set_collision_mask(collision_mask); _make_dirty(); //force update } else { PhysicsServer::get_singleton()->free(root_collision_instance); root_collision_instance = RID(); root_collision_shape.unref(); } + _change_notify(); } bool CSGShape::is_using_collision() const { return use_collision; } +void CSGShape::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + if (root_collision_instance.is_valid()) { + PhysicsServer::get_singleton()->body_set_collision_layer(root_collision_instance, p_layer); + } +} + +uint32_t CSGShape::get_collision_layer() const { + + return collision_layer; +} + +void CSGShape::set_collision_mask(uint32_t p_mask) { + + collision_mask = p_mask; + if (root_collision_instance.is_valid()) { + PhysicsServer::get_singleton()->body_set_collision_mask(root_collision_instance, p_mask); + } +} + +uint32_t CSGShape::get_collision_mask() const { + + return collision_mask; +} + +void CSGShape::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool CSGShape::get_collision_mask_bit(int p_bit) const { + + return get_collision_mask() & (1 << p_bit); +} + +void CSGShape::set_collision_layer_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_layer(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_layer(mask); +} + +bool CSGShape::get_collision_layer_bit(int p_bit) const { + + return get_collision_layer() & (1 << p_bit); +} + bool CSGShape::is_root_shape() const { return !parent; @@ -197,17 +255,6 @@ void CSGShape::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexc fvTexcOut[1] = t.y; } -void CSGShape::mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) { - ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); - - int i = (iFace * 3 + iVert) * 4; - - surface.tansw[i++] = fvTangent[0]; - surface.tansw[i++] = fvTangent[1]; - surface.tansw[i++] = fvTangent[2]; - surface.tansw[i++] = fSign; -} - void CSGShape::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, const tbool bIsOrientationPreserving, const int iFace, const int iVert) { @@ -216,7 +263,7 @@ void CSGShape::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const f int i = iFace * 3 + iVert; Vector3 normal = surface.normalsw[i]; Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]); - Vector3 bitangent = Vector3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]); + Vector3 bitangent = Vector3(-fvBiTangent[0], -fvBiTangent[1], -fvBiTangent[2]); // for some reason these are reversed, something with the coordinate system in Godot float d = bitangent.dot(normal.cross(tangent)); i *= 4; @@ -389,10 +436,10 @@ void CSGShape::_update_shape() { } // unset write access - surfaces.write[i].verticesw = PoolVector<Vector3>::Write(); - surfaces.write[i].normalsw = PoolVector<Vector3>::Write(); - surfaces.write[i].uvsw = PoolVector<Vector2>::Write(); - surfaces.write[i].tansw = PoolVector<float>::Write(); + surfaces.write[i].verticesw.release(); + surfaces.write[i].normalsw.release(); + surfaces.write[i].uvsw.release(); + surfaces.write[i].tansw.release(); if (surfaces[i].last_added == 0) continue; @@ -470,6 +517,8 @@ void CSGShape::_notification(int p_what) { PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); + set_collision_layer(collision_layer); + set_collision_mask(collision_mask); } _make_dirty(); @@ -482,13 +531,20 @@ void CSGShape::_notification(int p_what) { } } + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (parent) { + parent->_make_dirty(); + } + } + if (p_what == NOTIFICATION_EXIT_TREE) { if (parent) parent->_make_dirty(); parent = NULL; - if (use_collision && is_root_shape()) { + if (use_collision && is_root_shape() && root_collision_instance.is_valid()) { PhysicsServer::get_singleton()->free(root_collision_instance); root_collision_instance = RID(); root_collision_shape.unref(); @@ -501,6 +557,7 @@ void CSGShape::set_operation(Operation p_operation) { operation = p_operation; _make_dirty(); + update_gizmo(); } CSGShape::Operation CSGShape::get_operation() const { @@ -517,12 +574,27 @@ bool CSGShape::is_calculating_tangents() const { } void CSGShape::_validate_property(PropertyInfo &property) const { - if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) { + bool is_collision_prefixed = property.name.begins_with("collision_"); + if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) { //hide collision if not root property.usage = PROPERTY_USAGE_NOEDITOR; + } else if (is_collision_prefixed && !bool(get("use_collision"))) { + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; } } +Array CSGShape::get_meshes() const { + + if (root_mesh.is_valid()) { + Array arr; + arr.resize(2); + arr[0] = Transform(); + arr[1] = root_mesh; + return arr; + } + + return Array(); +} void CSGShape::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape); @@ -531,34 +603,54 @@ void CSGShape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation); ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation); + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); + ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); + ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision); ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision); - ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); - ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); + ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CSGShape::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &CSGShape::get_collision_layer); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape::get_collision_mask_bit); + + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape::get_collision_layer_bit); ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents); ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents); + ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape::get_meshes); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents"); + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION); } CSGShape::CSGShape() { + operation = OPERATION_UNION; + parent = NULL; brush = NULL; - set_notify_local_transform(true); dirty = false; - parent = NULL; - use_collision = false; - operation = OPERATION_UNION; snap = 0.001; + use_collision = false; + collision_layer = 1; + collision_mask = 1; calculate_tangents = true; + set_notify_local_transform(true); } CSGShape::~CSGShape() { @@ -633,6 +725,7 @@ CSGBrush *CSGMesh::_build_brush() { PoolVector<bool> smooth; PoolVector<Ref<Material> > materials; PoolVector<Vector2> uvs; + Ref<Material> material = get_material(); for (int i = 0; i < mesh->get_surface_count(); i++) { @@ -669,7 +762,12 @@ CSGBrush *CSGMesh::_build_brush() { uvr_used = true; } - Ref<Material> mat = mesh->surface_get_material(i); + Ref<Material> mat; + if (material.is_valid()) { + mat = material; + } else { + mat = mesh->surface_get_material(i); + } PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX]; if (aindices.size()) { @@ -715,8 +813,8 @@ CSGBrush *CSGMesh::_build_brush() { uvw[as + j + 1] = uv[1]; uvw[as + j + 2] = uv[2]; - sw[j / 3] = !flat; - mw[j / 3] = mat; + sw[(as + j) / 3] = !flat; + mw[(as + j) / 3] = mat; } } else { int as = vertices.size(); @@ -758,8 +856,8 @@ CSGBrush *CSGMesh::_build_brush() { uvw[as + j + 1] = uv[1]; uvw[as + j + 2] = uv[2]; - sw[j / 3] = !flat; - mw[j / 3] = mat; + sw[(as + j) / 3] = !flat; + mw[(as + j) / 3] = mat; } } } @@ -775,6 +873,18 @@ void CSGMesh::_mesh_changed() { update_gizmo(); } +void CSGMesh::set_material(const Ref<Material> &p_material) { + if (material == p_material) + return; + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGMesh::get_material() const { + + return material; +} + void CSGMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh); @@ -782,7 +892,11 @@ void CSGMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed); + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh::get_material); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); } void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) { @@ -1062,9 +1176,9 @@ CSGBrush *CSGBox::_build_brush() { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } @@ -1703,11 +1817,9 @@ CSGBrush *CSGPolygon::_build_brush() { path_cache = path; - if (path_cache) { - path_cache->connect("tree_exited", this, "_path_exited"); - path_cache->connect("curve_changed", this, "_path_changed"); - path_cache = NULL; - } + path_cache->connect("tree_exited", this, "_path_exited"); + path_cache->connect("curve_changed", this, "_path_changed"); + path_cache = NULL; } curve = path->get_curve(); if (curve.is_null()) @@ -1969,6 +2081,9 @@ CSGBrush *CSGPolygon::_build_brush() { for (int i = 0; i <= splits; i++) { float ofs = i * path_interval; + if (ofs > bl) { + ofs = bl; + } if (i == splits && path_joined) { ofs = 0.0; } diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 0a4bb5f665..6c9419b3c2 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,8 +38,8 @@ #include "scene/resources/concave_polygon_shape.h" #include "thirdparty/misc/mikktspace.h" -class CSGShape : public VisualInstance { - GDCLASS(CSGShape, VisualInstance); +class CSGShape : public GeometryInstance { + GDCLASS(CSGShape, GeometryInstance); public: enum Operation { @@ -61,6 +61,8 @@ private: float snap; bool use_collision; + uint32_t collision_layer; + uint32_t collision_mask; Ref<ConcavePolygonShape> root_collision_shape; RID root_collision_instance; @@ -97,7 +99,6 @@ private: static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert); static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert); static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert); - static void mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, const tbool bIsOrientationPreserving, const int iFace, const int iVert); @@ -116,6 +117,8 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: + Array get_meshes() const; + void set_operation(Operation p_operation); Operation get_operation() const; @@ -127,6 +130,18 @@ public: void set_use_collision(bool p_enable); bool is_using_collision() const; + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + void set_snap(float p_snap); float get_snap() const; @@ -141,7 +156,8 @@ public: VARIANT_ENUM_CAST(CSGShape::Operation) class CSGCombiner : public CSGShape { - GDCLASS(CSGCombiner, CSGShape) + GDCLASS(CSGCombiner, CSGShape); + private: virtual CSGBrush *_build_brush(); @@ -150,7 +166,7 @@ public: }; class CSGPrimitive : public CSGShape { - GDCLASS(CSGPrimitive, CSGShape) + GDCLASS(CSGPrimitive, CSGShape); private: bool invert_faces; @@ -167,11 +183,12 @@ public: }; class CSGMesh : public CSGPrimitive { - GDCLASS(CSGMesh, CSGPrimitive) + GDCLASS(CSGMesh, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Mesh> mesh; + Ref<Material> material; void _mesh_changed(); @@ -181,11 +198,14 @@ protected: public: void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh(); + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; }; class CSGSphere : public CSGPrimitive { - GDCLASS(CSGSphere, CSGPrimitive) + GDCLASS(CSGSphere, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -218,7 +238,7 @@ public: class CSGBox : public CSGPrimitive { - GDCLASS(CSGBox, CSGPrimitive) + GDCLASS(CSGBox, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -247,7 +267,7 @@ public: class CSGCylinder : public CSGPrimitive { - GDCLASS(CSGCylinder, CSGPrimitive) + GDCLASS(CSGCylinder, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -284,7 +304,7 @@ public: class CSGTorus : public CSGPrimitive { - GDCLASS(CSGTorus, CSGPrimitive) + GDCLASS(CSGTorus, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -321,7 +341,7 @@ public: class CSGPolygon : public CSGPrimitive { - GDCLASS(CSGPolygon, CSGPrimitive) + GDCLASS(CSGPolygon, CSGPrimitive); public: enum Mode { @@ -384,7 +404,7 @@ public: void set_spin_degrees(float p_spin_degrees); float get_spin_degrees() const; - void set_spin_sides(int p_sides); + void set_spin_sides(int p_spin_sides); int get_spin_sides() const; void set_path_node(const NodePath &p_path); diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml index 5ec7b5089d..14f5a1952e 100644 --- a/modules/csg/doc_classes/CSGBox.xml +++ b/modules/csg/doc_classes/CSGBox.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> A CSG Box shape. </brief_description> @@ -8,21 +8,19 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="depth" type="float" setter="set_depth" getter="get_depth"> + <member name="depth" type="float" setter="set_depth" getter="get_depth" default="2.0"> Depth of the box measured from the center of the box. </member> - <member name="height" type="float" setter="set_height" getter="get_height"> + <member name="height" type="float" setter="set_height" getter="get_height" default="2.0"> Height of the box measured from the center of the box. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> The material used to render the box. </member> - <member name="width" type="float" setter="set_width" getter="get_width"> + <member name="width" type="float" setter="set_width" getter="get_width" default="2.0"> Width of the box measured from the center of the box. </member> </members> diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml index 1cfaa74b7d..51428b25f8 100644 --- a/modules/csg/doc_classes/CSGCombiner.xml +++ b/modules/csg/doc_classes/CSGCombiner.xml @@ -1,15 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.1"> +<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.2"> <brief_description> A CSG node that allows you to combine other CSG modifiers. </brief_description> <description> - For complex arrangements of shapes it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as their input to create the final shape. + For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as its input to create the final shape. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml index 92b170ed1f..9fc0281887 100644 --- a/modules/csg/doc_classes/CSGCylinder.xml +++ b/modules/csg/doc_classes/CSGCylinder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> A CSG Cylinder shape. </brief_description> @@ -8,28 +8,26 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="cone" type="bool" setter="set_cone" getter="is_cone"> - If true a cone is created, the [member radius] will only apply to one side. + <member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false"> + If [code]true[/code] a cone is created, the [member radius] will only apply to one side. </member> - <member name="height" type="float" setter="set_height" getter="get_height"> + <member name="height" type="float" setter="set_height" getter="get_height" default="1.0"> The height of the cylinder. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> The material used to render the cylinder. </member> - <member name="radius" type="float" setter="set_radius" getter="get_radius"> + <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0"> The radius of the cylinder. </member> - <member name="sides" type="int" setter="set_sides" getter="get_sides"> + <member name="sides" type="int" setter="set_sides" getter="get_sides" default="8"> The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder. </member> - <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. When false the cylinder will have a flat shaded look. + <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true"> + If [code]true[/code] the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. If [code]false[/code] the cylinder will have a flat shaded look. </member> </members> <constants> diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml index 419214b7e6..afe0bc262d 100644 --- a/modules/csg/doc_classes/CSGMesh.xml +++ b/modules/csg/doc_classes/CSGMesh.xml @@ -1,18 +1,18 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> A CSG Mesh shape that uses a mesh resource. </brief_description> <description> - This CSG node allows you to use any mesh resource as a CSG shape provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces. + This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> + <member name="material" type="Material" setter="set_material" getter="get_material"> + </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> The mesh resource to use as a CSG shape. </member> diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml index a33e5557cb..0ecee92cd5 100644 --- a/modules/csg/doc_classes/CSGPolygon.xml +++ b/modules/csg/doc_classes/CSGPolygon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> Extrudes a 2D polygon shape to create a 3D mesh. </brief_description> @@ -8,31 +8,29 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="depth" type="float" setter="set_depth" getter="get_depth"> + <member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0"> Extrusion depth when [member mode] is [constant MODE_DEPTH]. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> Material to use for the resulting mesh. </member> - <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode"> + <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode" default="0"> Extrusion mode. </member> <member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u"> - If true the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH]. + If [code]true[/code] the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH]. </member> <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval"> Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH]. </member> <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined"> - If true the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH]. + If [code]true[/code] the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH]. </member> <member name="path_local" type="bool" setter="set_path_local" getter="is_path_local"> - If false we extrude centered on our path, if true we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH]. + If [code]false[/code] we extrude centered on our path, if [code]true[/code] we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH]. </member> <member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node"> The [Shape] object containing the path along which we extrude when [member mode] is [constant MODE_PATH]. @@ -40,10 +38,10 @@ <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon.PathRotation"> The method by which each slice is rotated along the path when [member mode] is [constant MODE_PATH]. </member> - <member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon"> + <member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon" default="PoolVector2Array( 0, 0, 0, 1, 1, 1, 1, 0 )"> Point array that defines the shape that we'll extrude. </member> - <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> + <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false"> Generates smooth normals so smooth shading is applied to our mesh. </member> <member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees"> diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml index 2591bab7e3..6c2f837637 100644 --- a/modules/csg/doc_classes/CSGPrimitive.xml +++ b/modules/csg/doc_classes/CSGPrimitive.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.1"> +<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.2"> <brief_description> Base class for CSG primitives. </brief_description> @@ -7,12 +7,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces"> + <member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces" default="false"> Invert the faces of the mesh. </member> </members> diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml index ac3c2342fc..755d8df67e 100644 --- a/modules/csg/doc_classes/CSGShape.xml +++ b/modules/csg/doc_classes/CSGShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.1"> +<class name="CSGShape" inherits="GeometryInstance" category="Core" version="3.2"> <brief_description> The CSG base class. </brief_description> @@ -8,27 +8,79 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> + <method name="get_collision_layer_bit" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <description> + Returns an individual bit on the collision mask. + </description> + </method> + <method name="get_collision_mask_bit" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <description> + Returns an individual bit on the collision mask. + </description> + </method> + <method name="get_meshes" qualifiers="const"> + <return type="Array"> + </return> + <description> + </description> + </method> <method name="is_root_shape" qualifiers="const"> <return type="bool"> </return> <description> - Returns true if this is a root shape and is thus the object that is rendered. + Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. + </description> + </method> + <method name="set_collision_layer_bit"> + <return type="void"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <argument index="1" name="value" type="bool"> + </argument> + <description> + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + </description> + </method> + <method name="set_collision_mask_bit"> + <return type="void"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <argument index="1" name="value" type="bool"> + </argument> + <description> + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> </methods> <members> - <member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents"> + <member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents" default="true"> Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child. </member> - <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation"> + <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1"> + The physics layers this area is in. + Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. + A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. + </member> + <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> + The physics layers this CSG shape scans for collisions. + </member> + <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation" default="0"> The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. </member> - <member name="snap" type="float" setter="set_snap" getter="get_snap"> + <member name="snap" type="float" setter="set_snap" getter="get_snap" default="0.001"> </member> - <member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision"> + <member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false"> Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. </member> </members> @@ -40,7 +92,7 @@ Only intersecting geometry remains, the rest is removed. </constant> <constant name="OPERATION_SUBTRACTION" value="2" enum="Operation"> - The second shape is susbtracted from the first, leaving a dent with it's shape. + The second shape is subtracted from the first, leaving a dent with its shape. </constant> </constants> </class> diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml index a0069879cb..714e725acb 100644 --- a/modules/csg/doc_classes/CSGSphere.xml +++ b/modules/csg/doc_classes/CSGSphere.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> A CSG Sphere shape. </brief_description> @@ -8,25 +8,23 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="material" type="Material" setter="set_material" getter="get_material"> The material used to render the sphere. </member> - <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments"> + <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="12"> Number of vertical slices for the sphere. </member> - <member name="radius" type="float" setter="set_radius" getter="get_radius"> + <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0"> Radius of the sphere. </member> - <member name="rings" type="int" setter="set_rings" getter="get_rings"> + <member name="rings" type="int" setter="set_rings" getter="get_rings" default="6"> Number of horizontal slices for the sphere. </member> - <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the sphere are set to give a smooth effect making the sphere seem rounded. When false the sphere will have a flat shaded look. + <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true"> + If [code]true[/code] the normals of the sphere are set to give a smooth effect making the sphere seem rounded. If [code]false[/code] the sphere will have a flat shaded look. </member> </members> <constants> diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml index 187d71a2fa..5dc6bb8380 100644 --- a/modules/csg/doc_classes/CSGTorus.xml +++ b/modules/csg/doc_classes/CSGTorus.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.1"> +<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.2"> <brief_description> A CSG Torus shape. </brief_description> @@ -8,28 +8,26 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius"> + <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="2.0"> The inner radius of the torus. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> The material used to render the torus. </member> - <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius"> + <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="3.0"> The outer radius of the torus. </member> - <member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides"> + <member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides" default="6"> The number of edges each ring of the torus is constructed of. </member> - <member name="sides" type="int" setter="set_sides" getter="get_sides"> + <member name="sides" type="int" setter="set_sides" getter="get_sides" default="8"> The number of slices the torus is constructed of. </member> - <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the torus are set to give a smooth effect making the torus seem rounded. When false the torus will have a flat shaded look. + <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="true"> + If [code]true[/code] the normals of the torus are set to give a smooth effect making the torus seem rounded. If [code]false[/code] the torus will have a flat shaded look. </member> </members> <constants> diff --git a/modules/csg/icons/icon_c_s_g_box.svg b/modules/csg/icons/icon_c_s_g_box.svg new file mode 100644 index 0000000000..67e34df444 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_box.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +<path transform="translate(0 1036.4)" d="m8 0.94531-7 3.5v7.2227l7 3.5 0.29492-0.14844c-0.18282-0.30101-0.29492-0.64737-0.29492-1.0195v-2c0-0.72651 0.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c0.36419 0 0.70336 0.10754 1 0.2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_capsule.svg b/modules/csg/icons/icon_c_s_g_capsule.svg new file mode 100644 index 0000000000..92a7b5a870 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_capsule.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922 0.092943 0 0.18367-0.008623 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72887 0.41095-1.3691 1.0059-1.7188v-0.28125c0.34771-0.034464 0.68259-0.10691 1.0156-0.19922 0.10394-0.99856 0.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-0.66556-0.1047-1.2974-0.37231-1.9941-0.66211v-1.3223c0-1.3474 0.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957 0.4088 1.9941 1.5256 1.9941 2.873v1.3457c-0.68406 0.3054-1.3142 0.57292-1.9941 0.66602v-4.8848zm-4.0059 6.334c0.67836 0.2231 1.3126 0.44599 1.9941 0.52539v2.8848c-1.1957-0.4092-1.9941-1.5237-1.9941-2.8711v-0.53906z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_combiner.svg b/modules/csg/icons/icon_c_s_g_combiner.svg new file mode 100644 index 0000000000..cce2902e24 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_combiner.svg @@ -0,0 +1,8 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +<g fill="#fc9c9c"> +<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-0.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046 0.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/> +</g> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_cylinder.svg b/modules/csg/icons/icon_c_s_g_cylinder.svg new file mode 100644 index 0000000000..645a74c79b --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_cylinder.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg"> +<g> +<path transform="scale(.9375)" d="m8 1c-1.7469 0-3.328 0.22648-4.5586 0.63672-0.61528 0.20512-1.1471 0.45187-1.5898 0.80078-0.44272 0.34891-0.85156 0.88101-0.85156 1.5625v8c0 0.68149 0.40884 1.2155 0.85156 1.5645 0.44272 0.34891 0.97457 0.59577 1.5898 0.80078 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477 0.095648 0 0.18467-0.008426 0.2793-0.009766-0.1722-0.29446-0.2793-0.62995-0.2793-0.99023v-1c-1.5668 0-2.9867-0.2195-3.9277-0.5332-0.46329-0.15435-0.90474-0.33752-1.0723-0.4668v-5.8125c0.1468 0.058667 0.2835 0.12515 0.44141 0.17773 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477s3.328-0.22453 4.5586-0.63477c0.15791-0.052267 0.29461-0.11864 0.44141-0.17773v1.8125h1c0.36396 0 0.70348 0.10774 1 0.2832v-4.2832c0-0.68149-0.40884-1.2136-0.85156-1.5625-0.44272-0.34891-0.97457-0.59566-1.5898-0.80078-1.2306-0.41024-2.8117-0.63672-4.5586-0.63672zm0 2c1.5668 0 2.9867 0.22145 3.9277 0.53516 0.46368 0.15456 0.80138 0.33741 0.96875 0.4668-0.16752 0.12928-0.50546 0.3105-0.96875 0.46484-0.94102 0.31371-2.361 0.5332-3.9277 0.5332s-2.9867-0.2195-3.9277-0.5332c-0.46329-0.15435-0.80123-0.33556-0.96875-0.46484 0.16737-0.12939 0.50507-0.31224 0.96875-0.4668 0.94102-0.31371 2.361-0.53516 3.9277-0.53516z" fill="#fc9c9c" stroke-width="1.0667"/> +<path d="m11.25 8.4375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v0.9375h1.875v1.875h0.9375c0.51938 0 0.9375-0.41812 0.9375-0.9375v-1.875c0-0.51938-0.41812-0.9375-0.9375-0.9375zm0.9375 3.75h-1.875v-1.875h-0.9375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v1.875c0 0.51938 0.41812 0.9375 0.9375 0.9375h1.875c0.51938 0 0.9375-0.41812 0.9375-0.9375z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_mesh.svg b/modules/csg/icons/icon_c_s_g_mesh.svg new file mode 100644 index 0000000000..6e940a4aa5 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_mesh.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.649e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35663-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.65e-4 1.3735-0.38169 1.7305-1h3.2695v-2h-3.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l4.916 4.916c0.31428-0.20669 0.68609-0.33008 1.084-0.33008 0-0.3979 0.12338-0.76971 0.33008-1.084l-4.916-4.916h5.8574c0.17478 0.30301 0.42598 0.55488 0.72852 0.73047v3.2695h2v-3.2715c0.61771-0.35663 0.99874-1.0152 1-1.7285 0-1.1046-0.89543-2-2-2-0.71397 5.648e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35663-0.61771-1.0152-0.99874-1.7285-1z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_polygon.svg b/modules/csg/icons/icon_c_s_g_polygon.svg new file mode 100644 index 0000000000..71b03cb8e6 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_polygon.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m7.9629 1.002c-0.14254 0.00487-0.28238 0.04016-0.41016 0.10352l-6 3c-0.33878 0.16944-0.55276 0.51574-0.55273 0.89453v5.832c-0.105 0.61631 0.37487 1.1768 1 1.168h5v2c2.16e-5 0.67546 0.64487 1.1297 1.2617 0.95898-0.16118-0.28721-0.26172-0.61135-0.26172-0.95898v-2c0-0.72673 0.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c0.36397 0 0.70348 0.10774 1 0.2832v-3.2773c6e-6 -0.00195 6e-6 -0.0039094 0-0.0058594 2.6e-5 -0.37879-0.21395-0.72509-0.55273-0.89453l-6-3c-0.15022-0.074574-0.31679-0.11017-0.48438-0.10352zm0.037109 2.1172l3.7637 1.8809-2.7637 1.3809v-1.3809c-5.52e-5 -0.55226-0.44774-0.99994-1-1h-1.7617l1.7617-0.88086zm-5 2.8809h4v4h-4v-4z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_sphere.svg b/modules/csg/icons/icon_c_s_g_sphere.svg new file mode 100644 index 0000000000..f81b566993 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_sphere.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 0.093042 0 0.18321-0.01004 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72673 0.40794-1.3664 1-1.7168v-0.33398c0.34074-0.019259 0.67728-0.069097 1.0156-0.10547 0.083091-1.0187 0.94713-1.8438 1.9844-1.8438h2c0.35841 0 0.69292 0.10484 0.98633 0.27539 0.003633-0.092184 0.013672-0.18235 0.013672-0.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-0.071342-2.6061-0.29819-3.9434-0.69141 0.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549 0.41253 3.637 2.0767 3.9414 4.1699-1.3046 0.36677-2.6158 0.60259-3.9414 0.6875v-4.8574zm-5.7793 6.2988c1.2733 0.31892 2.5337 0.50215 3.7793 0.5625v2.9414c-1.8291-0.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/icons/icon_c_s_g_torus.svg b/modules/csg/icons/icon_c_s_g_torus.svg new file mode 100644 index 0000000000..3d30aa47b2 --- /dev/null +++ b/modules/csg/icons/icon_c_s_g_torus.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m8 3c-1.8145 0-3.4691 0.41721-4.7461 1.1621-1.277 0.745-2.2539 1.9082-2.2539 3.3379 0 1.4298 0.9769 2.5949 2.2539 3.3398 1.277 0.7449 2.9316 1.1602 4.7461 1.1602 0-1.0907 0.90931-2 2-2 0-0.080836 0.013744-0.15778 0.023438-0.23633-0.61769 0.14673-1.3008 0.23633-2.0234 0.23633-1.4992 0-2.8437-0.36687-3.7383-0.88867-0.89456-0.5219-1.2617-1.108-1.2617-1.6113 0-0.5032 0.36716-1.0876 1.2617-1.6094 0.89456-0.5219 2.2391-0.89062 3.7383-0.89062s2.8437 0.36872 3.7383 0.89062c0.89456 0.5218 1.2617 1.1062 1.2617 1.6094 0 0.15978-0.053679 0.32822-0.13281 0.5h1.1328c0.32481 0 0.62893 0.088408 0.90234 0.23047 0.057552-0.23582 0.097656-0.47718 0.097656-0.73047 0-1.4297-0.9769-2.5929-2.2539-3.3379-1.277-0.7449-2.9316-1.1621-4.7461-1.1621z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp index 0dea09808a..33e29ec43e 100644 --- a/modules/csg/register_types.cpp +++ b/modules/csg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h index 49490d31d3..9e112b72f0 100644 --- a/modules/csg/register_types.h +++ b/modules/csg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index fcc69d8371..142af0c800 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -14,7 +14,7 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_cvtt.Append(CPPPATH=[thirdparty_dir]) + env_cvtt.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_cvtt.Clone() env_thirdparty.disable_warnings() diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 732b9cf733..17b0038780 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -145,7 +145,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS int h = p_image->get_height(); bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8); - bool is_hdr = (p_image->get_format() == Image::FORMAT_RGBH); + bool is_hdr = (p_image->get_format() >= Image::FORMAT_RH) && (p_image->get_format() <= Image::FORMAT_RGBE9995); if (!is_ldr && !is_hdr) { return; // Not a usable source format @@ -175,6 +175,10 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS bool is_signed = false; if (is_hdr) { + if (p_image->get_format() != Image::FORMAT_RGBH) { + p_image->convert(Image::FORMAT_RGBH); + } + PoolVector<uint8_t>::Read rb = p_image->get_data().read(); const uint16_t *source_data = reinterpret_cast<const uint16_t *>(&rb[0]); @@ -384,8 +388,8 @@ void image_decompress_cvtt(Image *p_image) { h >>= 1; } - rb = PoolVector<uint8_t>::Read(); - wb = PoolVector<uint8_t>::Write(); + rb.release(); + wb.release(); p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h index fe912ad3bb..a4a9641236 100644 --- a/modules/cvtt/image_compress_cvtt.h +++ b/modules/cvtt/image_compress_cvtt.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp index c96fbbf340..40381e75d2 100644 --- a/modules/cvtt/register_types.cpp +++ b/modules/cvtt/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h index 73de9728d3..63cf172e63 100644 --- a/modules/cvtt/register_types.h +++ b/modules/cvtt/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp index d6351fb6fe..66ce3ce35d 100644 --- a/modules/dds/register_types.cpp +++ b/modules/dds/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,15 +32,16 @@ #include "texture_loader_dds.h" -static ResourceFormatDDS *resource_loader_dds = NULL; +static Ref<ResourceFormatDDS> resource_loader_dds; void register_dds_types() { - resource_loader_dds = memnew(ResourceFormatDDS); + resource_loader_dds.instance(); ResourceLoader::add_resource_format_loader(resource_loader_dds); } void unregister_dds_types() { - memdelete(resource_loader_dds); + ResourceLoader::remove_resource_format_loader(resource_loader_dds); + resource_loader_dds.unref(); } diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h index c7f7839c24..ae58fc435e 100644 --- a/modules/dds/register_types.h +++ b/modules/dds/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 53e9773791..4628bd9a5b 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,8 @@ #include "texture_loader_dds.h" #include "core/os/file_access.h" +#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0]))) + enum { DDS_MAGIC = 0x20534444, DDSD_CAPS = 0x00000001, @@ -51,6 +53,7 @@ enum DDSFormat { DDS_DXT5, DDS_ATI1, DDS_ATI2, + DDS_A2XY, DDS_BGRA8, DDS_BGR8, DDS_RGBA8, //flipped in dds @@ -74,9 +77,12 @@ struct DDSFormatInfo { }; static const DDSFormatInfo dds_format_info[DDS_MAX] = { - { "DXT1", true, false, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3", true, false, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5", true, false, 4, 16, Image::FORMAT_DXT5 }, + { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, + { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, + { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, + { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, + { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, + { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, @@ -158,22 +164,25 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, DDSFormat dds_format; - if (format_flags & DDPF_FOURCC && format_fourcc == 0x31545844) { //'1TXD' + if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { dds_format = DDS_DXT1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x33545844) { //'3TXD' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { dds_format = DDS_DXT3; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x35545844) { //'5TXD' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { dds_format = DDS_DXT5; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x31495441) { //'1ITA' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { dds_format = DDS_ATI1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x32495441) { //'2ITA' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { dds_format = DDS_ATI2; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { + + dds_format = DDS_A2XY; } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { @@ -242,7 +251,6 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, src_data.resize(size); PoolVector<uint8_t>::Write wb = src_data.write(); f->get_buffer(wb.ptr(), size); - wb = PoolVector<uint8_t>::Write(); } else if (info.palette) { @@ -263,14 +271,14 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, colsize = 4; } - int w = width; - int h = height; + int w2 = width; + int h2 = height; for (uint32_t i = 1; i < mipmaps; i++) { - w = (w + 1) >> 1; - h = (h + 1) >> 1; - size += w * h * info.block_size; + w2 = (w2 + 1) >> 1; + h2 = (h2 + 1) >> 1; + size += w2 * h2 * info.block_size; } src_data.resize(size + 256 * colsize); @@ -287,8 +295,6 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, if (colsize == 4) wb[dst_ofs + 3] = palette[src_ofs + 3]; } - - wb = PoolVector<uint8_t>::Write(); } else { //uncompressed generic... @@ -432,10 +438,9 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, } break; - default: {} + default: { + } } - - wb = PoolVector<uint8_t>::Write(); } Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data)); diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h index 4e2593c744..6ddef4e770 100644 --- a/modules/dds/texture_loader_dds.h +++ b/modules/dds/texture_loader_dds.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/SCsub b/modules/enet/SCsub index a57a4b29ea..485c33b1a8 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -21,8 +21,8 @@ if env['builtin_enet']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_enet.Append(CPPPATH=[thirdparty_dir]) - env_enet.Append(CPPFLAGS=["-DGODOT_ENET"]) + env_enet.Prepend(CPPPATH=[thirdparty_dir]) + env_enet.Append(CPPDEFINES=["GODOT_ENET"]) env_thirdparty = env_enet.Clone() env_thirdparty.disable_warnings() diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index e057a435ac..84ed5fd9ee 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> +<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2"> <brief_description> - PacketPeer implementation using the ENet library. + PacketPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library. </brief_description> <description> - A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. + A PacketPeer implementation that should be passed to [member SceneTree.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> - <demos> - </demos> <methods> <method name="close_connection"> <return type="void"> @@ -36,7 +34,7 @@ <argument index="4" name="client_port" type="int" default="0"> </argument> <description> - Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain nome (e.g. [code]www.example.com[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]192.168.1.1[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port, this is useful in some NAT traversal technique. + Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques. </description> </method> <method name="create_server"> @@ -51,7 +49,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created. + Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created. </description> </method> <method name="disconnect_peer"> @@ -62,7 +60,7 @@ <argument index="1" name="now" type="bool" default="false"> </argument> <description> - Disconnect the given peer. If "now" is set to true, the connection will be closed immediately without flushing queued messages. + Disconnect the given peer. If "now" is set to [code]true[/code], the connection will be closed immediately without flushing queued messages. </description> </method> <method name="get_last_packet_channel" qualifiers="const"> @@ -103,39 +101,39 @@ <argument index="0" name="ip" type="String"> </argument> <description> - The IP used when creating a server. This is set to the wildcard [code]*[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. + The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code]. </description> </method> </methods> <members> - <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered"> - Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system. + <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false"> + Enforce ordered packets when using [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE] (thus behaving similarly to [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE_ORDERED]). This is the only way to use ordering with the RPC system. </member> - <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count"> - The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis. + <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count" default="3"> + The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per channel basis. </member> - <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode"> - The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. + <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0"> + The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. </member> - <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel"> - Set the default channel to be used to transfer data. By default this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. + <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="-1"> + Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. </member> </members> <constants> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> - No compression. + No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. </constant> <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> - ENet's buildin range encoding. + ENet's built-in range encoding. </constant> <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> - FastLZ compression. + [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. </constant> <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> - zlib compression. + [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources. </constant> <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> - ZStandard compression. + [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. </constant> </constants> </class> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 7b5fd854ff..a5a356ced2 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,6 +80,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); ENetAddress address; + memset(&address, 0, sizeof(address)); #ifdef GODOT_ENET if (bind_ip.is_wildcard()) { @@ -231,6 +232,13 @@ void NetworkedMultiplayerENet::poll() { break; } + // A client joined with an invalid ID (negative values, 0, and 1 are reserved). + // Probably trying to exploit us. + if (server && ((int)event.data < 2 || peer_map.has((int)event.data))) { + enet_peer_reset(event.peer); + ERR_CONTINUE(true); + } + int *new_id = memnew(int); *new_id = event.data; @@ -339,11 +347,10 @@ void NetworkedMultiplayerENet::poll() { uint32_t *id = (uint32_t *)event.peer->data; - ERR_CONTINUE(event.packet->dataLength < 12) + ERR_CONTINUE(event.packet->dataLength < 8); uint32_t source = decode_uint32(&event.packet->data[0]); int target = decode_uint32(&event.packet->data[4]); - uint32_t flags = decode_uint32(&event.packet->data[8]); packet.from = source; packet.channel = event.channelID; @@ -364,7 +371,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source) // Do not resend to self continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -378,7 +385,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -426,7 +433,6 @@ bool NetworkedMultiplayerENet::is_server() const { void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) { ERR_FAIL_COND(!active); - ERR_FAIL_COND(wait_usec < 0); _pop_current_packet(); @@ -457,7 +463,7 @@ void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) { ERR_FAIL_COND(!active); ERR_FAIL_COND(!is_server()); - ERR_FAIL_COND(!peer_map.has(p_peer)) + ERR_FAIL_COND(!peer_map.has(p_peer)); if (now) { enet_peer_disconnect_now(peer_map[p_peer], 0); @@ -497,8 +503,8 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - *r_buffer = (const uint8_t *)(¤t_packet.packet->data[12]); - r_buffer_size = current_packet.packet->dataLength - 12; + *r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]); + r_buffer_size = current_packet.packet->dataLength - 8; return OK; } @@ -543,11 +549,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } } - ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags); + ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 8, packet_flags); encode_uint32(unique_id, &packet->data[0]); // Source ID encode_uint32(target_peer, &packet->data[4]); // Dest ID - encode_uint32(packet_flags, &packet->data[8]); // Dest ID - copymem(&packet->data[12], p_buffer, p_buffer_size); + copymem(&packet->data[8], p_buffer, p_buffer_size); if (server) { @@ -684,7 +689,9 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * case COMPRESS_ZSTD: { mode = Compression::MODE_ZSTD; } break; - default: { ERR_FAIL_V(0); } + default: { + ERR_FAIL_V(0); + } } int req_size = Compression::get_max_compressed_buffer_size(ofs, mode); @@ -721,7 +728,8 @@ size_t NetworkedMultiplayerENet::enet_decompress(void *context, const enet_uint8 ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD); } break; - default: {} + default: { + } } if (ret < 0) { return 0; @@ -874,7 +882,9 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet() { NetworkedMultiplayerENet::~NetworkedMultiplayerENet() { - close_connection(); + if (active) { + close_connection(); + } } // Sets IP for ENet to bind when using create_server or create_client diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index a2b35f2395..8dcb202314 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,7 +38,8 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer { - GDCLASS(NetworkedMultiplayerENet, NetworkedMultiplayerPeer) + GDCLASS(NetworkedMultiplayerENet, NetworkedMultiplayerPeer); + public: enum CompressionMode { COMPRESS_NONE, diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp index cde70e8d5c..f2c1451446 100644 --- a/modules/enet/register_types.cpp +++ b/modules/enet/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h index bcf0893ae4..8d001b8c30 100644 --- a/modules/enet/register_types.h +++ b/modules/enet/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/SCsub b/modules/etc/SCsub index d2c77d6e3c..532b97b006 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -27,16 +27,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_etc.Append(CPPPATH=[thirdparty_dir]) +env_etc.Prepend(CPPPATH=[thirdparty_dir]) # upstream uses c++11 if not env.msvc: - env_etc.Append(CCFLAGS="-std=c++11") - -# -ffast-math seems to be incompatible with etc2comp on recent versions of -# GCC and Clang -if '-ffast-math' in env_etc['CCFLAGS']: - env_etc['CCFLAGS'].remove('-ffast-math') + env_etc.Append(CXXFLAGS="-std=c++11") env_thirdparty = env_etc.Clone() env_thirdparty.disable_warnings() diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index fbbc765bf2..6f54436bf9 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -115,7 +115,8 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f case Image::FORMAT_RGBA5551: { detected_channels = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } diff --git a/modules/etc/image_etc.h b/modules/etc/image_etc.h index 371c38176f..724d06545b 100644 --- a/modules/etc/image_etc.h +++ b/modules/etc/image_etc.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/register_types.cpp b/modules/etc/register_types.cpp index 1d1f0e1b77..584a1fcce8 100644 --- a/modules/etc/register_types.cpp +++ b/modules/etc/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,11 +33,11 @@ #include "image_etc.h" #include "texture_loader_pkm.h" -static ResourceFormatPKM *resource_loader_pkm = NULL; +static Ref<ResourceFormatPKM> resource_loader_pkm; void register_etc_types() { - resource_loader_pkm = memnew(ResourceFormatPKM); + resource_loader_pkm.instance(); ResourceLoader::add_resource_format_loader(resource_loader_pkm); _register_etc_compress_func(); @@ -45,5 +45,6 @@ void register_etc_types() { void unregister_etc_types() { - memdelete(resource_loader_pkm); + ResourceLoader::remove_resource_format_loader(resource_loader_pkm); + resource_loader_pkm.unref(); } diff --git a/modules/etc/register_types.h b/modules/etc/register_types.h index 4a8513a687..161c37b52f 100644 --- a/modules/etc/register_types.h +++ b/modules/etc/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp index 3041dde876..ff925480b8 100644 --- a/modules/etc/texture_loader_pkm.cpp +++ b/modules/etc/texture_loader_pkm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,7 +80,7 @@ RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, src_data.resize(size); PoolVector<uint8_t>::Write wb = src_data.write(); f->get_buffer(wb.ptr(), size); - wb = PoolVector<uint8_t>::Write(); + wb.release(); int mipmaps = h.format; int width = h.origWidth; diff --git a/modules/etc/texture_loader_pkm.h b/modules/etc/texture_loader_pkm.h index b5a95767c7..79c17953fc 100644 --- a/modules/etc/texture_loader_pkm.h +++ b/modules/etc/texture_loader_pkm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index 3e2068b8db..b47377cbc4 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -12,7 +12,6 @@ if env['builtin_freetype']: thirdparty_dir = "#thirdparty/freetype/" thirdparty_sources = [ "src/autofit/autofit.c", - "src/base/ftapi.c", "src/base/ftbase.c", "src/base/ftbbox.c", "src/base/ftbdf.c", @@ -62,24 +61,27 @@ if env['builtin_freetype']: # Globally too, as freetype is used in scene (see bottom) env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) - sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' - if env['platform'] == 'javascript': - # Forcibly undefine this macro so SIMD is not used in this file, - # since currently unsupported in WASM - sfnt = env_freetype.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) - thirdparty_sources += [sfnt] - - env_freetype.Append(CPPPATH=[thirdparty_dir + "/include"]) + env_freetype.Prepend(CPPPATH=[thirdparty_dir + "/include"]) # Also needed in main env for scene/ - env.Append(CPPPATH=[thirdparty_dir + "/include"]) + env.Prepend(CPPPATH=[thirdparty_dir + "/include"]) - env_freetype.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY', '-DFT_CONFIG_OPTION_USE_PNG']) + env_freetype.Append(CPPDEFINES=['FT2_BUILD_LIBRARY', 'FT_CONFIG_OPTION_USE_PNG']) if (env['target'] != 'release'): - env_freetype.Append(CCFLAGS=['-DZLIB_DEBUG']) + env_freetype.Append(CPPDEFINES=['ZLIB_DEBUG']) # Also requires libpng headers if env['builtin_libpng']: - env_freetype.Append(CPPPATH=["#thirdparty/libpng"]) + env_freetype.Prepend(CPPPATH=["#thirdparty/libpng"]) + + sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' + # Must be done after all CPPDEFINES are being set so we can copy them. + if env['platform'] == 'javascript': + # Forcibly undefine this macro so SIMD is not used in this file, + # since currently unsupported in WASM + tmp_env = env_freetype.Clone() + tmp_env.Append(CPPFLAGS=['-U__OPTIMIZE__']) + sfnt = tmp_env.Object(sfnt) + thirdparty_sources += [sfnt] env_thirdparty = env_freetype.Clone() env_thirdparty.disable_warnings() @@ -101,4 +103,4 @@ if env['builtin_freetype']: # Godot source files env_freetype.add_source_files(env.modules_sources, "*.cpp") # Used in scene/, needs to be in main env -env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) +env.Append(CPPDEFINES=['FREETYPE_ENABLED']) diff --git a/modules/freetype/register_types.cpp b/modules/freetype/register_types.cpp index bde04b714c..108ecab329 100644 --- a/modules/freetype/register_types.cpp +++ b/modules/freetype/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h index 0cec0e3951..dae7ad1f40 100644 --- a/modules/freetype/register_types.h +++ b/modules/freetype/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/freetype/uwpdef.h b/modules/freetype/uwpdef.h index 3887c3e25c..e4d2d223bb 100644 --- a/modules/freetype/uwpdef.h +++ b/modules/freetype/uwpdef.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index fe2d8c7ce9..0cdd585558 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -12,13 +12,14 @@ env_gdnative.add_source_files(env.modules_sources, "nativescript/*.cpp") env_gdnative.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") env_gdnative.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") -env_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) +env_gdnative.Prepend(CPPPATH=['#modules/gdnative/include/']) Export('env_gdnative') SConscript("net/SCsub") SConscript("arvr/SCsub") SConscript("pluginscript/SCsub") +SConscript("videodecoder/SCsub") from platform_methods import run_in_subprocess @@ -35,7 +36,7 @@ if ARGUMENTS.get('gdnative_wrapper', False): gensource, = env_gdnative.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) gd_wrapper_env = env.Clone() - gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) + gd_wrapper_env.Prepend(CPPPATH=['#modules/gdnative/include/']) if gd_wrapper_env['use_lto']: if not env.msvc: diff --git a/modules/gdnative/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp index edc948e086..624ef19dec 100644 --- a/modules/gdnative/android/android_gdn.cpp +++ b/modules/gdnative/android/android_gdn.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,8 @@ // These entry points are only for the android platform and are simple stubs in all others. #ifdef __ANDROID__ +#include "platform/android/java_godot_wrapper.h" +#include "platform/android/os_android.h" #include "platform/android/thread_jandroid.h" #else #define JNIEnv void @@ -54,20 +56,31 @@ JNIEnv *GDAPI godot_android_get_env() { jobject GDAPI godot_android_get_activity() { #ifdef __ANDROID__ - JNIEnv *env = ThreadAndroid::get_env(); - - jclass activityThread = env->FindClass("android/app/ActivityThread"); - jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;"); - jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread); - jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;"); - jobject context = env->CallObjectMethod(at, getApplication); + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->get_activity(); +#else + return NULL; +#endif +} - return env->NewGlobalRef(context); +jobject GDAPI godot_android_get_surface() { +#ifdef __ANDROID__ + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->get_surface(); #else return NULL; #endif } +bool GDAPI godot_android_is_activity_resumed() { +#ifdef __ANDROID__ + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->is_activity_resumed(); +#else + return false; +#endif +} + #ifdef __cplusplus } #endif
\ No newline at end of file diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index b525725107..64e2c362b2 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,11 +31,15 @@ #include "arvr_interface_gdnative.h" #include "main/input_default.h" #include "servers/arvr/arvr_positional_tracker.h" -#include "servers/visual/visual_server_global.h" +#include "servers/visual/visual_server_globals.h" + +void ARVRInterfaceGDNative::_bind_methods() { + ADD_PROPERTY_DEFAULT("interface_is_initialized", false); + ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); +} ARVRInterfaceGDNative::ARVRInterfaceGDNative() { - // testing - printf("Construct gdnative interface\n"); + print_verbose("Construct gdnative interface\n"); // we won't have our data pointer until our library gets set data = NULL; @@ -44,9 +48,9 @@ ARVRInterfaceGDNative::ARVRInterfaceGDNative() { } ARVRInterfaceGDNative::~ARVRInterfaceGDNative() { - printf("Destruct gdnative interface\n"); + print_verbose("Destruct gdnative interface\n"); - if (is_initialized()) { + if (interface != NULL && is_initialized()) { uninitialize(); }; @@ -99,13 +103,10 @@ int ARVRInterfaceGDNative::get_capabilities() const { } bool ARVRInterfaceGDNative::get_anchor_detection_is_enabled() const { - bool enabled; ERR_FAIL_COND_V(interface == NULL, false); - enabled = interface->get_anchor_detection_is_enabled(data); - - return enabled; + return interface->get_anchor_detection_is_enabled(data); } void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { @@ -115,6 +116,17 @@ void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { interface->set_anchor_detection_is_enabled(data, p_enable); } +int ARVRInterfaceGDNative::get_camera_feed_id() { + + ERR_FAIL_COND_V(interface == NULL, 0); + + if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { + return (unsigned int)interface->get_camera_feed_id(data); + } else { + return 0; + } +} + bool ARVRInterfaceGDNative::is_stereo() { bool stereo; @@ -126,21 +138,16 @@ bool ARVRInterfaceGDNative::is_stereo() { } bool ARVRInterfaceGDNative::is_initialized() const { - bool initialized; ERR_FAIL_COND_V(interface == NULL, false); - initialized = interface->is_initialized(data); - - return initialized; + return interface->is_initialized(data); } bool ARVRInterfaceGDNative::initialize() { - bool initialized; - ERR_FAIL_COND_V(interface == NULL, false); - initialized = interface->initialize(data); + bool initialized = interface->initialize(data); if (initialized) { // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface @@ -198,6 +205,17 @@ CameraMatrix ARVRInterfaceGDNative::get_projection_for_eye(ARVRInterface::Eyes p return cm; } +unsigned int ARVRInterfaceGDNative::get_external_texture_for_eye(ARVRInterface::Eyes p_eye) { + + ERR_FAIL_COND_V(interface == NULL, 0); + + if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { + return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); + } else { + return 0; + } +} + void ARVRInterfaceGDNative::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { ERR_FAIL_COND(interface == NULL); @@ -211,6 +229,15 @@ void ARVRInterfaceGDNative::process() { interface->process(data); } +void ARVRInterfaceGDNative::notification(int p_what) { + ERR_FAIL_COND(interface == NULL); + + // this is only available in interfaces that implement 1.1 or later + if ((interface->version.major > 1) || ((interface->version.major == 1) && (interface->version.minor > 0))) { + interface->notification(data, p_what); + } +} + ///////////////////////////////////////////////////////////////////////////////////// // some helper callbacks @@ -223,7 +250,7 @@ void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_ Ref<ARVRInterfaceGDNative> new_interface; new_interface.instance(); - new_interface->set_interface((godot_arvr_interface_gdnative *const)p_interface); + new_interface->set_interface((const godot_arvr_interface_gdnative *)p_interface); ARVRServer::get_singleton()->add_interface(new_interface); } diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h index 26d0cdf9f4..ab7090876a 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ b/modules/gdnative/arvr/arvr_interface_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,7 @@ */ class ARVRInterfaceGDNative : public ARVRInterface { - GDCLASS(ARVRInterfaceGDNative, ARVRInterface) + GDCLASS(ARVRInterfaceGDNative, ARVRInterface); void cleanup(); @@ -49,6 +49,8 @@ protected: const godot_arvr_interface_gdnative *interface; void *data; + static void _bind_methods(); + public: /** general interface information **/ ARVRInterfaceGDNative(); @@ -66,6 +68,7 @@ public: /** specific to AR **/ virtual bool get_anchor_detection_is_enabled() const; virtual void set_anchor_detection_is_enabled(bool p_enable); + virtual int get_camera_feed_id(); /** rendering and internal **/ virtual Size2 get_render_targetsize(); @@ -78,9 +81,11 @@ public: // and a CameraMatrix version to ARVRServer virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual unsigned int get_external_texture_for_eye(ARVRInterface::Eyes p_eye); virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); + virtual void notification(int p_what); }; #endif // ARVR_INTERFACE_GDNATIVE_H diff --git a/modules/gdnative/arvr/register_types.cpp b/modules/gdnative/arvr/register_types.cpp index d792e1b4a4..fbebccb4c1 100644 --- a/modules/gdnative/arvr/register_types.cpp +++ b/modules/gdnative/arvr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/arvr/register_types.h b/modules/gdnative/arvr/register_types.h index 4c651b4f76..8fd2600037 100644 --- a/modules/gdnative/arvr/register_types.h +++ b/modules/gdnative/arvr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 701a13d32f..b9e5afcdf3 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@NativeScript", "ARVRInterfaceGDNative", "GDNative", "GDNativeLibrary", @@ -14,6 +15,9 @@ def get_doc_classes(): "PacketPeerGDNative", "PluginScript", "StreamPeerGDNative", + "VideoStreamGDNative", + "WebRTCPeerConnectionGDNative", + "WebRTCDataChannelGDNative", ] def get_doc_path(): diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/gdnative/doc_classes/@NativeScript.xml index 018f4734ec..cb5de198ac 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/gdnative/doc_classes/@NativeScript.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.1"> +<class name="@NativeScript" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index be86ff0541..47c2ee3358 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -1,15 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.1"> +<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.2"> <brief_description> - GDNative wrapper for an ARVR interface + GDNative wrapper for an ARVR interface. </brief_description> <description> - This is a wrapper class for GDNative implementations of the ARVR interface. To use a GDNative ARVR interface simply instantiate this object and set your GDNative library containing the ARVR interface implementation. + This is a wrapper class for GDNative implementations of the ARVR interface. To use a GDNative ARVR interface, simply instantiate this object and set your GDNative library containing the ARVR interface implementation. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index ca0457623f..8750ddc56d 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNative" inherits="Reference" category="Core" version="3.1"> +<class name="GDNative" inherits="Reference" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="call_native"> <return type="Variant"> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 754a6d2514..7e1cac243a 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.1"> +<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_current_dependencies" qualifiers="const"> <return type="PoolStringArray"> @@ -25,13 +23,13 @@ <members> <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file"> </member> - <member name="load_once" type="bool" setter="set_load_once" getter="should_load_once"> + <member name="load_once" type="bool" setter="set_load_once" getter="should_load_once" default="true"> </member> - <member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable"> + <member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable" default="true"> </member> - <member name="singleton" type="bool" setter="set_singleton" getter="is_singleton"> + <member name="singleton" type="bool" setter="set_singleton" getter="is_singleton" default="false"> </member> - <member name="symbol_prefix" type="String" setter="set_symbol_prefix" getter="get_symbol_prefix"> + <member name="symbol_prefix" type="String" setter="set_symbol_prefix" getter="get_symbol_prefix" default=""godot_""> </member> </members> <constants> diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml index 4433179726..b9a01672a6 100644 --- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml +++ b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiplayerPeerGDNative" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> +<class name="MultiplayerPeerGDNative" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 37d5b79e7a..e34e209374 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NativeScript" inherits="Script" category="Core" version="3.1"> +<class name="NativeScript" inherits="Script" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_class_documentation" qualifiers="const"> <return type="String"> @@ -53,13 +51,13 @@ </method> </methods> <members> - <member name="class_name" type="String" setter="set_class_name" getter="get_class_name"> + <member name="class_name" type="String" setter="set_class_name" getter="get_class_name" default=""""> </member> <member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library"> </member> - <member name="script_class_icon_path" type="String" setter="set_script_class_icon_path" getter="get_script_class_icon_path"> + <member name="script_class_icon_path" type="String" setter="set_script_class_icon_path" getter="get_script_class_icon_path" default=""""> </member> - <member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name"> + <member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name" default=""""> </member> </members> <constants> diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml index 0ae54bc9c7..acfb597cff 100644 --- a/modules/gdnative/doc_classes/PacketPeerGDNative.xml +++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PacketPeerGDNative" inherits="PacketPeer" category="Core" version="3.1"> +<class name="PacketPeerGDNative" inherits="PacketPeer" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 27c6adae3f..b07122bbdf 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -1,14 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PluginScript" inherits="Script" category="Core" version="3.1"> +<class name="PluginScript" inherits="Script" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> + <method name="new" qualifiers="vararg"> + <return type="Object"> + </return> + <description> + Returns a new instance of the script. + </description> + </method> </methods> <constants> </constants> diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml index d86cd2c25a..f7e0d76fdb 100644 --- a/modules/gdnative/doc_classes/StreamPeerGDNative.xml +++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeerGDNative" inherits="StreamPeer" category="Core" version="3.1"> +<class name="StreamPeerGDNative" inherits="StreamPeer" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml new file mode 100644 index 0000000000..ed7678b7be --- /dev/null +++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VideoStreamGDNative" inherits="VideoStream" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_file"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="set_file"> + <return type="void"> + </return> + <argument index="0" name="file" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml new file mode 100644 index 0000000000..ac18ec6020 --- /dev/null +++ b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml new file mode 100644 index 0000000000..44cb8e5db8 --- /dev/null +++ b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeerConnectionGDNative" inherits="WebRTCPeerConnection" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index f07fdef488..4eb9a2a0a3 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,7 +48,7 @@ static const bool default_reloadable = true; // Defined in gdnative_api_struct.gen.cpp extern const godot_gdnative_core_api_struct api_struct; -Map<String, Vector<Ref<GDNative> > > *GDNativeLibrary::loaded_libraries = NULL; +Map<String, Vector<Ref<GDNative> > > GDNativeLibrary::loaded_libraries; GDNativeLibrary::GDNativeLibrary() { config_file.instance(); @@ -57,10 +57,6 @@ GDNativeLibrary::GDNativeLibrary() { load_once = default_load_once; singleton = default_singleton; reloadable = default_reloadable; - - if (GDNativeLibrary::loaded_libraries == NULL) { - GDNativeLibrary::loaded_libraries = memnew((Map<String, Vector<Ref<GDNative> > >)); - } } GDNativeLibrary::~GDNativeLibrary() { @@ -243,7 +239,7 @@ void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix); ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile", 0), "set_config_file", "get_config_file"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton"); @@ -318,10 +314,10 @@ bool GDNative::initialize() { #endif if (library->should_load_once()) { - if (GDNativeLibrary::loaded_libraries->has(lib_path)) { + if (GDNativeLibrary::loaded_libraries.has(lib_path)) { // already loaded. Don't load again. // copy some of the stuff instead - this->native_handle = (*GDNativeLibrary::loaded_libraries)[lib_path][0]->native_handle; + this->native_handle = GDNativeLibrary::loaded_libraries[lib_path][0]->native_handle; initialized = true; return true; } @@ -377,11 +373,11 @@ bool GDNative::initialize() { initialized = true; - if (library->should_load_once() && !GDNativeLibrary::loaded_libraries->has(lib_path)) { + if (library->should_load_once() && !GDNativeLibrary::loaded_libraries.has(lib_path)) { Vector<Ref<GDNative> > gdnatives; gdnatives.resize(1); gdnatives.write[0] = Ref<GDNative>(this); - GDNativeLibrary::loaded_libraries->insert(lib_path, gdnatives); + GDNativeLibrary::loaded_libraries.insert(lib_path, gdnatives); } return true; @@ -395,7 +391,7 @@ bool GDNative::terminate() { } if (library->should_load_once()) { - Vector<Ref<GDNative> > *gdnatives = &(*GDNativeLibrary::loaded_libraries)[library->get_current_library_path()]; + Vector<Ref<GDNative> > *gdnatives = &GDNativeLibrary::loaded_libraries[library->get_current_library_path()]; if (gdnatives->size() > 1) { // there are other GDNative's still using this library, so we actually don't terminate gdnatives->erase(Ref<GDNative>(this)); @@ -404,8 +400,8 @@ bool GDNative::terminate() { } else if (gdnatives->size() == 1) { // we're the last one, terminate! gdnatives->clear(); - // wew this looks scary, but all it does is remove the entry completely - GDNativeLibrary::loaded_libraries->erase(GDNativeLibrary::loaded_libraries->find(library->get_current_library_path())); + // whew this looks scary, but all it does is remove the entry completely + GDNativeLibrary::loaded_libraries.erase(GDNativeLibrary::loaded_libraries.find(library->get_current_library_path())); } } @@ -414,6 +410,7 @@ bool GDNative::terminate() { if (error || !library_terminate) { OS::get_singleton()->close_dynamic_library(native_handle); native_handle = NULL; + initialized = false; return true; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index c5364a72ac..408af26753 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,9 +45,9 @@ class GDNativeLibraryResourceLoader; class GDNative; class GDNativeLibrary : public Resource { - GDCLASS(GDNativeLibrary, Resource) + GDCLASS(GDNativeLibrary, Resource); - static Map<String, Vector<Ref<GDNative> > > *loaded_libraries; + static Map<String, Vector<Ref<GDNative> > > loaded_libraries; friend class GDNativeLibraryResourceLoader; friend class GDNative; @@ -99,16 +99,20 @@ public: } _FORCE_INLINE_ void set_load_once(bool p_load_once) { + config_file->set_value("general", "load_once", p_load_once); load_once = p_load_once; } _FORCE_INLINE_ void set_singleton(bool p_singleton) { + config_file->set_value("general", "singleton", p_singleton); singleton = p_singleton; } _FORCE_INLINE_ void set_symbol_prefix(String p_symbol_prefix) { + config_file->set_value("general", "symbol_prefix", p_symbol_prefix); symbol_prefix = p_symbol_prefix; } _FORCE_INLINE_ void set_reloadable(bool p_reloadable) { + config_file->set_value("general", "reloadable", p_reloadable); reloadable = p_reloadable; } @@ -133,7 +137,7 @@ struct GDNativeCallRegistry { }; class GDNative : public Reference { - GDCLASS(GDNative, Reference) + GDCLASS(GDNative, Reference); Ref<GDNativeLibrary> library; diff --git a/modules/gdnative/gdnative/aabb.cpp b/modules/gdnative/gdnative/aabb.cpp index 0597e1cdd6..91537290af 100644 --- a/modules/gdnative/gdnative/aabb.cpp +++ b/modules/gdnative/gdnative/aabb.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index a30cc09bf6..1ef8e9f900 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ #include "core/os/memory.h" #include "core/color.h" -#include "core/dvector.h" +#include "core/pool_vector.h" #include "core/variant.h" @@ -305,13 +305,13 @@ void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, con godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before) { Array *self = (Array *)p_self; - return self->bsearch((const Variant *)p_value, p_before); + return self->bsearch(*(const Variant *)p_value, p_before); } godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before) { Array *self = (Array *)p_self; const String *func = (const String *)p_func; - return self->bsearch_custom((const Variant *)p_value, (Object *)p_obj, *func, p_before); + return self->bsearch_custom(*(const Variant *)p_value, (Object *)p_obj, *func, p_before); } void GDAPI godot_array_destroy(godot_array *p_self) { diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp index d88499ade1..d77c7d91ac 100644 --- a/modules/gdnative/gdnative/basis.cpp +++ b/modules/gdnative/gdnative/basis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "gdnative/basis.h" -#include "core/math/matrix3.h" +#include "core/math/basis.h" #include "core/variant.h" #ifdef __cplusplus diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp index 79f0c71b5e..e63129460c 100644 --- a/modules/gdnative/gdnative/color.cpp +++ b/modules/gdnative/gdnative/color.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp index 34cc91129e..fff3fc3625 100644 --- a/modules/gdnative/gdnative/dictionary.cpp +++ b/modules/gdnative/gdnative/dictionary.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -55,6 +55,15 @@ void GDAPI godot_dictionary_destroy(godot_dictionary *p_self) { self->~Dictionary(); } +godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep) { + const Dictionary *self = (const Dictionary *)p_self; + godot_dictionary res; + Dictionary *val = (Dictionary *)&res; + memnew_placement(val, Dictionary); + *val = self->duplicate(p_deep); + return res; +} + godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self) { const Dictionary *self = (const Dictionary *)p_self; return self->size(); @@ -155,12 +164,26 @@ godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self) { return raw_dest; } +// GDNative core 1.1 + godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key) { Dictionary *self = (Dictionary *)p_self; const Variant *key = (const Variant *)p_key; return self->erase(*key); } +godot_variant GDAPI godot_dictionary_get_with_default(const godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_default) { + const Dictionary *self = (const Dictionary *)p_self; + const Variant *key = (const Variant *)p_key; + const Variant *def = (const Variant *)p_default; + + godot_variant raw_dest; + Variant *dest = (Variant *)&raw_dest; + memnew_placement(dest, Variant(self->get(*key, *def))); + + return raw_dest; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index 8f10f116e6..98a4fd880c 100644 --- a/modules/gdnative/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index cf82940e09..c49899fa25 100644 --- a/modules/gdnative/gdnative/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/plane.cpp b/modules/gdnative/gdnative/plane.cpp index be821edcc3..d7fceb7516 100644 --- a/modules/gdnative/gdnative/plane.cpp +++ b/modules/gdnative/gdnative/plane.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/pool_arrays.cpp b/modules/gdnative/gdnative/pool_arrays.cpp index d55d81b5b6..74c540ca14 100644 --- a/modules/gdnative/gdnative/pool_arrays.cpp +++ b/modules/gdnative/gdnative/pool_arrays.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #include "gdnative/pool_arrays.h" #include "core/array.h" -#include "core/dvector.h" +#include "core/pool_vector.h" #include "core/variant.h" #include "core/color.h" diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp index 2594759508..169d0134a8 100644 --- a/modules/gdnative/gdnative/quat.cpp +++ b/modules/gdnative/gdnative/quat.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp index 5cbc2712c3..04ca92f8dd 100644 --- a/modules/gdnative/gdnative/rect2.cpp +++ b/modules/gdnative/gdnative/rect2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/rid.cpp b/modules/gdnative/gdnative/rid.cpp index 4374738f48..c13a9fdde2 100644 --- a/modules/gdnative/gdnative/rid.cpp +++ b/modules/gdnative/gdnative/rid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 0996633b70..913c57eb56 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "gdnative/string.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/ustring.h" #include "core/variant.h" @@ -74,7 +74,7 @@ void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t memnew_placement(dest, String(p_contents, p_size)); } -wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { +const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { String *self = (String *)p_self; return &(self->operator[](p_idx)); } diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp index b2a86b843c..dcbb773c25 100644 --- a/modules/gdnative/gdnative/string_name.cpp +++ b/modules/gdnative/gdnative/string_name.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "gdnative/string_name.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/ustring.h" #include <string.h> diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp index ee6140c7d0..188c4be1ba 100644 --- a/modules/gdnative/gdnative/transform.cpp +++ b/modules/gdnative/gdnative/transform.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp index fa0e15d9d2..b735f64251 100644 --- a/modules/gdnative/gdnative/transform2d.cpp +++ b/modules/gdnative/gdnative/transform2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp index fd6babfc3a..ac4d5a86b2 100644 --- a/modules/gdnative/gdnative/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,8 +37,22 @@ extern "C" { #endif +// Workaround GCC ICE on armv7hl which was affected GCC 6.0 up to 8.0 (GH-16100). +// It was fixed upstream in 8.1, and a fix was backported to 7.4. +// This can be removed once no supported distro ships with versions older than 7.4. +#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) && \ + (__GNUC__ == 6 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) || (__GNUC__ == 8 && __GNUC_MINOR__ < 1)) +#pragma GCC push_options +#pragma GCC optimize("-O0") +#endif + #define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr) +#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) && \ + (__GNUC__ == 6 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) || (__GNUC__ == 8 && __GNUC_MINOR__ < 1)) +#pragma GCC pop_options +#endif + // Constructors godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_self) { @@ -504,7 +518,7 @@ void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_varia const Variant *a = (const Variant *)p_a; const Variant *b = (const Variant *)p_b; Variant *ret = (Variant *)r_ret; - Variant::evaluate(op, a, b, *ret, *r_valid); + Variant::evaluate(op, *a, *b, *ret, *r_valid); } #ifdef __cplusplus diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp index c7902e06ee..a2ac61b35e 100644 --- a/modules/gdnative/gdnative/vector2.cpp +++ b/modules/gdnative/gdnative/vector2.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -119,6 +119,14 @@ godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, return dest; } +godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta) { + godot_vector2 dest; + const Vector2 *self = (const Vector2 *)p_self; + const Vector2 *to = (const Vector2 *)p_to; + *((Vector2 *)&dest) = self->move_toward(*to, p_delta); + return dest; +} + godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi) { godot_vector2 dest; const Vector2 *self = (const Vector2 *)p_self; diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp index a7e18fa22b..894683ab38 100644 --- a/modules/gdnative/gdnative/vector3.cpp +++ b/modules/gdnative/gdnative/vector3.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -124,6 +124,14 @@ godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, return dest; } +godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta) { + godot_vector3 dest; + const Vector3 *self = (const Vector3 *)p_self; + const Vector3 *to = (const Vector3 *)p_to; + *((Vector3 *)&dest) = self->move_toward(*to, p_delta); + return dest; +} + godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b) { const Vector3 *self = (const Vector3 *)p_self; const Vector3 *b = (const Vector3 *)p_b; diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index c5a1fa139e..6c12ee6534 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -11,7 +11,42 @@ "major": 1, "minor": 1 }, - "next": null, + "next": { + "type": "CORE", + "version": { + "major": 1, + "minor": 2 + }, + "next": null, + "api": [ + { + "name": "godot_dictionary_duplicate", + "return_type": "godot_dictionary", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_bool", "p_deep"] + ] + }, + { + "name": "godot_vector3_move_toward", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_to"], + ["const godot_real", "p_delta"] + ] + }, + { + "name": "godot_vector2_move_toward", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"], + ["const godot_real", "p_delta"] + ] + } + ] + }, "api": [ { "name": "godot_color_to_abgr32", @@ -107,6 +142,23 @@ ] }, { + "name": "godot_dictionary_get_with_default", + "return_type": "godot_variant", + "arguments": [ + ["const godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"], + ["const godot_variant *", "p_default"] + ] + }, + { + "name": "godot_dictionary_erase_with_return", + "return_type": "bool", + "arguments": [ + ["godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { "name": "godot_node_path_get_as_property_path", "return_type": "godot_node_path", "arguments": [ @@ -234,14 +286,6 @@ ] }, { - "name": "godot_dictionary_erase_with_return", - "return_type": "bool", - "arguments": [ - ["godot_dictionary *", "p_self"], - ["const godot_variant *", "p_key"] - ] - }, - { "name": "godot_is_instance_valid", "return_type": "bool", "arguments": [ @@ -4755,7 +4799,7 @@ }, { "name": "godot_string_operator_index", - "return_type": "wchar_t *", + "return_type": "const wchar_t *", "arguments": [ ["godot_string *", "p_self"], ["const godot_int", "p_idx"] @@ -6260,7 +6304,7 @@ "type": "ANDROID", "version": { "major": 1, - "minor": 0 + "minor": 1 }, "next": null, "api": [ @@ -6275,6 +6319,18 @@ "return_type": "jobject", "arguments": [ ] + }, + { + "name": "godot_android_get_surface", + "return_type": "jobject", + "arguments": [ + ] + }, + { + "name": "godot_android_is_activity_resumed", + "return_type": "bool", + "arguments": [ + ] } ] }, @@ -6374,6 +6430,109 @@ ] } ] + }, + { + "name": "videodecoder", + "type": "VIDEODECODER", + "version": { + "major": 0, + "minor": 1 + }, + "next": null, + "api": [ + { + "name": "godot_videodecoder_file_read", + "return_type": "godot_int", + "arguments": [ + ["void *", "file_ptr"], + ["uint8_t *", "buf"], + ["int", "buf_size"] + ] + }, + { + "name": "godot_videodecoder_file_seek", + "return_type": "int64_t", + "arguments": [ + [ "void *", "file_ptr"], + ["int64_t", "pos"], + ["int", "whence"] + ] + }, + { + "name": "godot_videodecoder_register_decoder", + "return_type": "void", + "arguments": [ + ["const godot_videodecoder_interface_gdnative *", "p_interface"] + ] + } + ] + }, + { + "name": "net", + "type": "NET", + "version": { + "major": 3, + "minor": 1 + }, + "next": { + "type": "NET", + "version": { + "major": 3, + "minor": 2 + }, + "next": null, + "api": [ + { + "name": "godot_net_set_webrtc_library", + "return_type": "godot_error", + "arguments": [ + ["const godot_net_webrtc_library *", "p_library"] + ] + }, + { + "name": "godot_net_bind_webrtc_peer_connection", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_webrtc_peer_connection *", "p_interface"] + ] + }, + { + "name": "godot_net_bind_webrtc_data_channel", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_webrtc_data_channel *", "p_interface"] + ] + } + ] + }, + "api": [ + { + "name": "godot_net_bind_stream_peer", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_stream_peer *", "p_interface"] + ] + }, + { + "name": "godot_net_bind_packet_peer", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_packet_peer *", "p_interface"] + ] + }, + { + "name": "godot_net_bind_multiplayer_peer", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_multiplayer_peer *", "p_interface"] + ] + } + ] } ] } diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index ff18a3ae69..7ab0e01108 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -45,7 +45,9 @@ def _build_gdnative_api_struct_header(api): '#include <android/godot_android.h>', '#include <arvr/godot_arvr.h>', '#include <nativescript/godot_nativescript.h>', + '#include <net/godot_net.h>', '#include <pluginscript/godot_pluginscript.h>', + '#include <videodecoder/godot_videodecoder.h>', '', '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)', '', @@ -212,7 +214,7 @@ def _build_gdnative_api_struct_source(api): 'extern const godot_gdnative_core_api_struct api_struct = {', '\tGDNATIVE_' + api['core']['type'] + ',', '\t{' + str(api['core']['version']['major']) + ', ' + str(api['core']['version']['minor']) + '},', - '\tNULL,', + '\t(const godot_gdnative_api_struct *)&api_1_1,', '\t' + str(len(api['extensions'])) + ',', '\tgdnative_extensions_pointers,', ] @@ -244,6 +246,7 @@ def _build_gdnative_wrapper_code(api): '#include <nativescript/godot_nativescript.h>', '#include <pluginscript/godot_pluginscript.h>', '#include <arvr/godot_arvr.h>', + '#include <videodecoder/godot_videodecoder.h>', '', '#include <gdnative_api_struct.gen.h>', '', diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp index 6424b66d1e..e2a69b1635 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.cpp +++ b/modules/gdnative/gdnative_library_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h index 04d2911d8b..e7d50ba29f 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.h +++ b/modules/gdnative/gdnative_library_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp index 0aafb95e2e..389b353a51 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.cpp +++ b/modules/gdnative/gdnative_library_singleton_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,16 @@ #include "gdnative_library_singleton_editor.h" #include "gdnative.h" -void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list) { +#include "editor/editor_node.h" + +Set<String> GDNativeLibrarySingletonEditor::_find_singletons_recursive(EditorFileSystemDirectory *p_dir) { + + Set<String> file_paths; // check children for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_name = p_dir->get_file(i); String file_type = p_dir->get_file_type(i); if (file_type != "GDNativeLibrary") { @@ -45,23 +50,57 @@ void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemD Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); if (lib.is_valid() && lib->is_singleton()) { - String path = p_dir->get_file_path(i); - TreeItem *ti = libraries->create_item(libraries->get_root()); - ti->set_text(0, path.get_file()); - ti->set_tooltip(0, path); - ti->set_metadata(0, path); - ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - ti->set_text(1, "Disabled,Enabled"); - bool enabled = enabled_list.has(path) ? true : false; - - ti->set_range(1, enabled ? 1 : 0); - ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0)); + file_paths.insert(p_dir->get_file_path(i)); } } // check subdirectories for (int i = 0; i < p_dir->get_subdir_count(); i++) { - _find_gdnative_singletons(p_dir->get_subdir(i), enabled_list); + Set<String> paths = _find_singletons_recursive(p_dir->get_subdir(i)); + + for (Set<String>::Element *E = paths.front(); E; E = E->next()) { + file_paths.insert(E->get()); + } + } + + return file_paths; +} + +void GDNativeLibrarySingletonEditor::_discover_singletons() { + + EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem(); + + Set<String> file_paths = _find_singletons_recursive(dir); + + bool changed = false; + Array current_files; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { + current_files = ProjectSettings::get_singleton()->get("gdnative/singletons"); + } + Array files; + for (Set<String>::Element *E = file_paths.front(); E; E = E->next()) { + if (!current_files.has(E->get())) { + changed = true; + } + files.append(E->get()); + } + + // Check for removed files + if (!changed) { + // Removed singleton + for (int j = 0; j < current_files.size(); j++) { + if (!files.has(current_files[j])) { + changed = true; + break; + } + } + } + + if (changed) { + + ProjectSettings::get_singleton()->set("gdnative/singletons", files); + _update_libraries(); // So singleton options (i.e. disabled) updates too + ProjectSettings::get_singleton()->save(); } } @@ -69,22 +108,40 @@ void GDNativeLibrarySingletonEditor::_update_libraries() { updating = true; libraries->clear(); - libraries->create_item(); //rppt + libraries->create_item(); // root item - Vector<String> enabled_paths; + Array singletons; if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); } - Set<String> enabled_list; - for (int i = 0; i < enabled_paths.size(); i++) { - enabled_list.insert(enabled_paths[i]); + Array singletons_disabled; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + singletons_disabled = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); } - EditorFileSystemDirectory *fs = EditorFileSystem::get_singleton()->get_filesystem(); - if (fs) { - _find_gdnative_singletons(fs, enabled_list); + Array updated_disabled; + for (int i = 0; i < singletons.size(); i++) { + bool enabled = true; + String path = singletons[i]; + if (singletons_disabled.has(path)) { + enabled = false; + updated_disabled.push_back(path); + } + TreeItem *ti = libraries->create_item(libraries->get_root()); + ti->set_text(0, path.get_file()); + ti->set_tooltip(0, path); + ti->set_metadata(0, path); + ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); + ti->set_text(1, "Disabled,Enabled"); + ti->set_range(1, enabled ? 1 : 0); + ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0)); + ti->set_editable(1, true); } + // The singletons list changed, we must update the settings + if (updated_disabled.size() != singletons_disabled.size()) + ProjectSettings::get_singleton()->set("gdnative/singletons_disabled", updated_disabled); + updating = false; } @@ -99,24 +156,29 @@ void GDNativeLibrarySingletonEditor::_item_edited() { bool enabled = item->get_range(1); String path = item->get_metadata(0); - Vector<String> enabled_paths; - if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + Array disabled_paths; + Array undo_paths; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + disabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); + // Duplicate so redo works (not a reference) + disabled_paths = disabled_paths.duplicate(); + // For undo, so we can reset the property. + undo_paths = disabled_paths.duplicate(); } if (enabled) { - if (enabled_paths.find(path) == -1) { - enabled_paths.push_back(path); - } + disabled_paths.erase(path); } else { - enabled_paths.erase(path); + if (disabled_paths.find(path) == -1) + disabled_paths.push_back(path); } - if (enabled_paths.size()) { - ProjectSettings::get_singleton()->set("gdnative/singletons", enabled_paths); - } else { - ProjectSettings::get_singleton()->set("gdnative/singletons", Variant()); - } + undo_redo->create_action(enabled ? TTR("Enabled GDNative Singleton") : TTR("Disabled GDNative Singleton")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", disabled_paths); + undo_redo->add_do_method(this, "_update_libraries"); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", undo_paths); + undo_redo->add_undo_method(this, "_update_libraries"); + undo_redo->commit_action(); } void GDNativeLibrarySingletonEditor::_notification(int p_what) { @@ -131,9 +193,12 @@ void GDNativeLibrarySingletonEditor::_notification(int p_what) { void GDNativeLibrarySingletonEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_item_edited"), &GDNativeLibrarySingletonEditor::_item_edited); + ClassDB::bind_method(D_METHOD("_discover_singletons"), &GDNativeLibrarySingletonEditor::_discover_singletons); + ClassDB::bind_method(D_METHOD("_update_libraries"), &GDNativeLibrarySingletonEditor::_update_libraries); } GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() { + undo_redo = EditorNode::get_singleton()->get_undo_redo(); libraries = memnew(Tree); libraries->set_columns(2); libraries->set_column_titles_visible(true); @@ -143,6 +208,7 @@ GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() { add_margin_child(TTR("Libraries: "), libraries, true); updating = false; libraries->connect("item_edited", this, "_item_edited"); + EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_discover_singletons"); } #endif // TOOLS_ENABLED diff --git a/modules/gdnative/gdnative_library_singleton_editor.h b/modules/gdnative/gdnative_library_singleton_editor.h index d3b5ba3846..b43080dfdb 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.h +++ b/modules/gdnative/gdnative_library_singleton_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,18 +36,24 @@ #include "editor/project_settings_editor.h" class GDNativeLibrarySingletonEditor : public VBoxContainer { + GDCLASS(GDNativeLibrarySingletonEditor, VBoxContainer); + +private: Tree *libraries; + UndoRedo *undo_redo; bool updating; - void _update_libraries(); - void _find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list); - void _item_edited(); + static Set<String> _find_singletons_recursive(EditorFileSystemDirectory *p_dir); protected: void _notification(int p_what); static void _bind_methods(); + void _discover_singletons(); + void _item_edited(); + void _update_libraries(); + public: GDNativeLibrarySingletonEditor(); }; diff --git a/modules/gdnative/icons/icon_g_d_native_library.svg b/modules/gdnative/icons/icon_g_d_native_library.svg new file mode 100644 index 0000000000..b494c7af6e --- /dev/null +++ b/modules/gdnative/icons/icon_g_d_native_library.svg @@ -0,0 +1,5 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539v-5.2695a2 2 0 0 1 -1 -1.7305 2 2 0 0 1 1 -1.7285v-0.27148h1 4.5762a5 5 0 0 0 -0.11328 -0.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm2 7v1 5 1h5c0.55228 0 1-0.4477 1-1v-5c0-0.5523-0.44772-1-1-1v4l-1-1-1 1v-4h-3z" fill="#e0e0e0"/> +</g> +</svg> diff --git a/modules/gdnative/icons/icon_native_script.svg b/modules/gdnative/icons/icon_native_script.svg new file mode 100644 index 0000000000..fb9e135627 --- /dev/null +++ b/modules/gdnative/icons/icon_native_script.svg @@ -0,0 +1,5 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625h3v1 1h2v-0.95117a2 2 0 0 1 0 -0.048828 2 2 0 0 1 2 -2 2 2 0 0 1 2 2v1h5v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3 -3 3 3 0 0 0 -3 -3v-2h-2zm6 0v2h2v-2h-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3 -3h-2zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v-2zm4 0v4h2v-4h-2z" fill="#e0e0e0"/> +</g> +</svg> diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h index 832dac9ac3..7063e1d2c5 100644 --- a/modules/gdnative/include/android/godot_android.h +++ b/modules/gdnative/include/android/godot_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,6 +46,8 @@ extern "C" { JNIEnv *GDAPI godot_android_get_env(); jobject GDAPI godot_android_get_activity(); +jobject GDAPI godot_android_get_surface(); +bool GDAPI godot_android_is_activity_resumed(); #ifdef __cplusplus } diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h index 63de62b507..d465bbde54 100644 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ b/modules/gdnative/include/arvr/godot_arvr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ extern "C" { // Use these to populate version in your plugin #define GODOTVR_API_MAJOR 1 -#define GODOTVR_API_MINOR 0 +#define GODOTVR_API_MINOR 1 typedef struct { godot_gdnative_api_version version; /* version of our API */ @@ -61,6 +61,10 @@ typedef struct { void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real); void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); void (*process)(void *); + // only in 1.1 onwards + godot_int (*get_external_texture_for_eye)(void *, godot_int); + void (*notification)(void *, godot_int); + godot_int (*get_camera_feed_id)(void *); } godot_arvr_interface_gdnative; void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); diff --git a/modules/gdnative/include/gdnative/aabb.h b/modules/gdnative/include/gdnative/aabb.h index dca5d4bb14..6b9b342f10 100644 --- a/modules/gdnative/include/gdnative/aabb.h +++ b/modules/gdnative/include/gdnative/aabb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 876b8f8e8f..10ef8a73d2 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h index 6128bf3ac3..9f1923c02b 100644 --- a/modules/gdnative/include/gdnative/basis.h +++ b/modules/gdnative/include/gdnative/basis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h index 3007dbc6e3..b7837a0940 100644 --- a/modules/gdnative/include/gdnative/color.h +++ b/modules/gdnative/include/gdnative/color.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h index faace818ee..483cd9c4e3 100644 --- a/modules/gdnative/include/gdnative/dictionary.h +++ b/modules/gdnative/include/gdnative/dictionary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,6 +63,8 @@ void GDAPI godot_dictionary_new(godot_dictionary *r_dest); void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src); void GDAPI godot_dictionary_destroy(godot_dictionary *p_self); +godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep); + godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self); godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self); @@ -94,8 +96,12 @@ godot_bool GDAPI godot_dictionary_operator_equal(const godot_dictionary *p_self, godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self); +// GDNative core 1.1 + godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key); +godot_variant GDAPI godot_dictionary_get_with_default(const godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_default); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 796ced84f4..884bcf60d2 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -67,7 +67,7 @@ extern "C" { ////// Error typedef enum { - GODOT_OK, + GODOT_OK, // (0) GODOT_FAILED, ///< Generic fail error GODOT_ERR_UNAVAILABLE, ///< What is requested is unsupported/unavailable GODOT_ERR_UNCONFIGURED, ///< The object being used hasn't been properly set up yet @@ -97,12 +97,12 @@ typedef enum { GODOT_ERR_CONNECTION_ERROR, GODOT_ERR_CANT_ACQUIRE_RESOURCE, GODOT_ERR_CANT_FORK, - GODOT_ERR_INVALID_DATA, ///< Data passed is invalid (30) + GODOT_ERR_INVALID_DATA, ///< Data passed is invalid (30) GODOT_ERR_INVALID_PARAMETER, ///< Parameter passed is invalid GODOT_ERR_ALREADY_EXISTS, ///< When adding, item already exists GODOT_ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, it item does not exist GODOT_ERR_DATABASE_CANT_READ, ///< database is full - GODOT_ERR_DATABASE_CANT_WRITE, ///< database is full (35) + GODOT_ERR_DATABASE_CANT_WRITE, ///< database is full (35) GODOT_ERR_COMPILATION_FAILED, GODOT_ERR_METHOD_NOT_FOUND, GODOT_ERR_LINK_FAILED, diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 48fe5b4d3d..a59833b924 100644 --- a/modules/gdnative/include/gdnative/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/plane.h b/modules/gdnative/include/gdnative/plane.h index 6c8a6ae1a4..074f127fa8 100644 --- a/modules/gdnative/include/gdnative/plane.h +++ b/modules/gdnative/include/gdnative/plane.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/pool_arrays.h b/modules/gdnative/include/gdnative/pool_arrays.h index 1210039e34..96730ab085 100644 --- a/modules/gdnative/include/gdnative/pool_arrays.h +++ b/modules/gdnative/include/gdnative/pool_arrays.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h index 634f486e66..7239c13a36 100644 --- a/modules/gdnative/include/gdnative/quat.h +++ b/modules/gdnative/include/gdnative/quat.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h index 47c15c80bd..7652bf054f 100644 --- a/modules/gdnative/include/gdnative/rect2.h +++ b/modules/gdnative/include/gdnative/rect2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/rid.h b/modules/gdnative/include/gdnative/rid.h index 0942334ee5..4a61cb171f 100644 --- a/modules/gdnative/include/gdnative/rid.h +++ b/modules/gdnative/include/gdnative/rid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index 95ae42a9ec..f045ac9d58 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,7 +79,7 @@ void GDAPI godot_string_new(godot_string *r_dest); void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src); void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size); -wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); +const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self); diff --git a/modules/gdnative/include/gdnative/string_name.h b/modules/gdnative/include/gdnative/string_name.h index 5068a3d8f9..c6465aff03 100644 --- a/modules/gdnative/include/gdnative/string_name.h +++ b/modules/gdnative/include/gdnative/string_name.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/transform.h b/modules/gdnative/include/gdnative/transform.h index 880f21c88a..e17a18cb43 100644 --- a/modules/gdnative/include/gdnative/transform.h +++ b/modules/gdnative/include/gdnative/transform.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/transform2d.h b/modules/gdnative/include/gdnative/transform2d.h index aed941f139..e53f36a8c3 100644 --- a/modules/gdnative/include/gdnative/transform2d.h +++ b/modules/gdnative/include/gdnative/transform2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h index 5e71aa9f11..17afd905b4 100644 --- a/modules/gdnative/include/gdnative/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h index af97524dac..7a5ae6afa9 100644 --- a/modules/gdnative/include/gdnative/vector2.h +++ b/modules/gdnative/include/gdnative/vector2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -83,6 +83,8 @@ godot_vector2 GDAPI godot_vector2_linear_interpolate(const godot_vector2 *p_self godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_vector2 *p_pre_a, const godot_vector2 *p_post_b, const godot_real p_t); +godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta); + godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi); godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self); diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h index e0299a8a30..70ec6422ac 100644 --- a/modules/gdnative/include/gdnative/vector3.h +++ b/modules/gdnative/include/gdnative/vector3.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -90,6 +90,8 @@ godot_vector3 GDAPI godot_vector3_linear_interpolate(const godot_vector3 *p_self godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_vector3 *p_pre_a, const godot_vector3 *p_post_b, const godot_real p_t); +godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta); + godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b); godot_vector3 GDAPI godot_vector3_cross(const godot_vector3 *p_self, const godot_vector3 *p_b); diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index ba044117e2..f3b9f7fb31 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h index bfa688592d..3a411755c1 100644 --- a/modules/gdnative/include/net/godot_net.h +++ b/modules/gdnative/include/net/godot_net.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,9 +51,9 @@ typedef struct { /* This is StreamPeer */ godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes); - godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int &r_received); + godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int *r_received); godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes); - godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int &r_sent); + godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int *r_sent); int (*get_available_bytes)(const void *user); @@ -61,7 +61,7 @@ typedef struct { } godot_net_stream_peer; /* Binds a StreamPeerGDNative to the provided interface */ -void godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface); +void godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface); typedef struct { godot_gdnative_api_version version; /* version of our API */ @@ -69,7 +69,7 @@ typedef struct { godot_object *data; /* User reference */ /* This is PacketPeer */ - godot_error (*get_packet)(void *, const uint8_t **, int &); + godot_error (*get_packet)(void *, const uint8_t **, int *); godot_error (*put_packet)(void *, const uint8_t *, int); godot_int (*get_available_packet_count)(const void *); godot_int (*get_max_packet_size)(const void *); @@ -86,7 +86,7 @@ typedef struct { godot_object *data; /* User reference */ /* This is PacketPeer */ - godot_error (*get_packet)(void *, const uint8_t **, int &); + godot_error (*get_packet)(void *, const uint8_t **, int *); godot_error (*put_packet)(void *, const uint8_t *, int); godot_int (*get_available_packet_count)(const void *); godot_int (*get_max_packet_size)(const void *); @@ -115,4 +115,7 @@ void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_ } #endif +// WebRTC Bindings +#include "net/godot_webrtc.h" + #endif /* GODOT_NATIVENET_H */ diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h new file mode 100644 index 0000000000..783f7b727d --- /dev/null +++ b/modules/gdnative/include/net/godot_webrtc.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* godot_webrtc.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GODOT_NATIVEWEBRTC_H +#define GODOT_NATIVEWEBRTC_H + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GODOT_NET_WEBRTC_API_MAJOR 3 +#define GODOT_NET_WEBRTC_API_MINOR 2 + +/* Library Interface (used to set default GDNative WebRTC implementation */ +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + + /* Called when the library is unset as default interface via godot_net_set_webrtc_library */ + void (*unregistered)(); + + /* Used by WebRTCPeerConnection create when GDNative is the default implementation. */ + /* Takes a pointer to WebRTCPeerConnectionGDNative, should bind and return OK, failure if binding was unsuccessful. */ + godot_error (*create_peer_connection)(godot_object *); + + void *next; /* For extension */ +} godot_net_webrtc_library; + +/* WebRTCPeerConnection interface */ +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + + godot_object *data; /* User reference */ + + /* This is WebRTCPeerConnection */ + godot_int (*get_connection_state)(const void *); + + godot_error (*initialize)(void *, const godot_dictionary *); + godot_object *(*create_data_channel)(void *, const char *p_channel_name, const godot_dictionary *); + godot_error (*create_offer)(void *); + godot_error (*create_answer)(void *); /* unused for now, should be done automatically on set_local_description */ + godot_error (*set_remote_description)(void *, const char *, const char *); + godot_error (*set_local_description)(void *, const char *, const char *); + godot_error (*add_ice_candidate)(void *, const char *, int, const char *); + godot_error (*poll)(void *); + void (*close)(void *); + + void *next; /* For extension? */ +} godot_net_webrtc_peer_connection; + +/* WebRTCDataChannel interface */ +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + + godot_object *data; /* User reference */ + + /* This is PacketPeer */ + godot_error (*get_packet)(void *, const uint8_t **, int *); + godot_error (*put_packet)(void *, const uint8_t *, int); + godot_int (*get_available_packet_count)(const void *); + godot_int (*get_max_packet_size)(const void *); + + /* This is WebRTCDataChannel */ + void (*set_write_mode)(void *, godot_int); + godot_int (*get_write_mode)(const void *); + bool (*was_string_packet)(const void *); + + godot_int (*get_ready_state)(const void *); + const char *(*get_label)(const void *); + bool (*is_ordered)(const void *); + int (*get_id)(const void *); + int (*get_max_packet_life_time)(const void *); + int (*get_max_retransmits)(const void *); + const char *(*get_protocol)(const void *); + bool (*is_negotiated)(const void *); + + godot_error (*poll)(void *); + void (*close)(void *); + + void *next; /* For extension? */ +} godot_net_webrtc_data_channel; + +/* Set the default GDNative library */ +godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *); +/* Binds a WebRTCPeerConnectionGDNative to the provided interface */ +void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *); +/* Binds a WebRTCDataChannelGDNative to the provided interface */ +void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h index d1671c014e..a9e83d1524 100644 --- a/modules/gdnative/include/pluginscript/godot_pluginscript.h +++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,7 +136,7 @@ typedef struct { godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions); int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be NULL godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args); - godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint); + godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint); void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line); void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value); diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/gdnative/include/videodecoder/godot_videodecoder.h index 1d858d80bf..1b08be30f0 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/gdnative/include/videodecoder/godot_videodecoder.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* monodevelop_instance.cpp */ +/* godot_videodecoder.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,58 +28,48 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "monodevelop_instance.h" - -#include "../mono_gd/gd_mono.h" -#include "../mono_gd/gd_mono_class.h" - -void MonoDevelopInstance::execute(const Vector<String> &p_files) { - - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) - - ERR_FAIL_NULL(execute_method); - ERR_FAIL_COND(gc_handle.is_null()); - - MonoException *exc = NULL; - - Variant files = p_files; - const Variant *args[1] = { &files }; - execute_method->invoke(gc_handle->get_target(), args, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL(); - } -} - -void MonoDevelopInstance::execute(const String &p_file) { - - Vector<String> files; - files.push_back(p_file); - execute(files); +#ifndef GODOT_NATIVEVIDEODECODER_H +#define GODOT_NATIVEVIDEODECODER_H + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GODOTAV_API_MAJOR 0 +#define GODOTAV_API_MINOR 1 + +typedef struct +{ + godot_gdnative_api_version version; + void *next; + void *(*constructor)(godot_object *); + void (*destructor)(void *); + const char *(*get_plugin_name)(void); + const char **(*get_supported_extensions)(int *count); + godot_bool (*open_file)(void *, void *); // data struct, and a FileAccess pointer + godot_real (*get_length)(const void *); + godot_real (*get_playback_position)(const void *); + void (*seek)(void *, godot_real); + void (*set_audio_track)(void *, godot_int); + void (*update)(void *, godot_real); + godot_pool_byte_array *(*get_videoframe)(void *); + godot_int (*get_audioframe)(void *, float *, int); + godot_int (*get_channels)(const void *); + godot_int (*get_mix_rate)(const void *); + godot_vector2 (*get_texture_size)(const void *); +} godot_videodecoder_interface_gdnative; + +typedef int (*GDNativeAudioMixCallback)(void *, const float *, int); + +// FileAccess wrappers for custom FFmpeg IO +godot_int GDAPI godot_videodecoder_file_read(void *file_ptr, uint8_t *buf, int buf_size); +int64_t GDAPI godot_videodecoder_file_seek(void *file_ptr, int64_t pos, int whence); +void GDAPI godot_videodecoder_register_decoder(const godot_videodecoder_interface_gdnative *p_interface); + +#ifdef __cplusplus } +#endif -MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) { - - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) - - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance"); - - MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); - - GDMonoMethod *ctor = klass->get_method(".ctor", 2); - MonoException *exc = NULL; - - Variant solution = p_solution; - Variant editor_id = p_editor_id; - const Variant *args[2] = { &solution, &editor_id }; - ctor->invoke(obj, args, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL(); - } - - gc_handle = MonoGCHandle::create_strong(obj); - execute_method = klass->get_method("Execute", 1); -} +#endif /* GODOT_NATIVEVIDEODECODER_H */ diff --git a/modules/gdnative/nativescript/SCsub b/modules/gdnative/nativescript/SCsub index 5841ad5531..92c9d6630d 100644 --- a/modules/gdnative/nativescript/SCsub +++ b/modules/gdnative/nativescript/SCsub @@ -4,7 +4,6 @@ Import('env') Import('env_gdnative') env_gdnative.add_source_files(env.modules_sources, '*.cpp') -env_gdnative.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) if "platform" in env and env["platform"] in ["x11", "iphone"]: env.Append(LINKFLAGS=["-rdynamic"]) diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 8c6dace847..e0cf990f83 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -139,6 +139,34 @@ static String get_type_name(const PropertyInfo &info) { } /* + * Some comparison helper functions we need + */ + +struct MethodInfoComparator { + StringName::AlphCompare compare; + bool operator()(const MethodInfo &p_a, const MethodInfo &p_b) const { + + return compare(p_a.name, p_b.name); + } +}; + +struct PropertyInfoComparator { + StringName::AlphCompare compare; + bool operator()(const PropertyInfo &p_a, const PropertyInfo &p_b) const { + + return compare(p_a.name, p_b.name); + } +}; + +struct ConstantAPIComparator { + NoCaseComparator compare; + bool operator()(const ConstantAPI &p_a, const ConstantAPI &p_b) const { + + return compare(p_a.constant_name, p_b.constant_name); + } +}; + +/* * Reads the entire Godot API to a list */ List<ClassAPI> generate_c_api_classes() { @@ -147,6 +175,7 @@ List<ClassAPI> generate_c_api_classes() { List<StringName> classes; ClassDB::get_class_list(&classes); + classes.sort_custom<StringName::AlphCompare>(); // Register global constants as a fake GlobalConstants singleton class { @@ -162,6 +191,7 @@ List<ClassAPI> generate_c_api_classes() { constant_api.constant_value = GlobalConstants::get_global_constant_value(i); global_constants_api.constants.push_back(constant_api); } + global_constants_api.constants.sort_custom<ConstantAPIComparator>(); api.push_back(global_constants_api); } @@ -193,6 +223,7 @@ List<ClassAPI> generate_c_api_classes() { { List<String> constant; ClassDB::get_integer_constant_list(class_name, &constant, true); + constant.sort_custom<NoCaseComparator>(); for (List<String>::Element *c = constant.front(); c != NULL; c = c->next()) { ConstantAPI constant_api; constant_api.constant_name = c->get(); @@ -206,6 +237,7 @@ List<ClassAPI> generate_c_api_classes() { { List<MethodInfo> signals_; ClassDB::get_signal_list(class_name, &signals_, true); + signals_.sort_custom<MethodInfoComparator>(); for (int i = 0; i < signals_.size(); i++) { SignalAPI signal; @@ -245,6 +277,7 @@ List<ClassAPI> generate_c_api_classes() { { List<PropertyInfo> properties; ClassDB::get_property_list(class_name, &properties, true); + properties.sort_custom<PropertyInfoComparator>(); for (List<PropertyInfo>::Element *p = properties.front(); p != NULL; p = p->next()) { PropertyAPI property_api; @@ -272,6 +305,7 @@ List<ClassAPI> generate_c_api_classes() { { List<MethodInfo> methods; ClassDB::get_method_list(class_name, &methods, true); + methods.sort_custom<MethodInfoComparator>(); for (List<MethodInfo>::Element *m = methods.front(); m != NULL; m = m->next()) { MethodAPI method_api; @@ -279,7 +313,7 @@ List<ClassAPI> generate_c_api_classes() { MethodInfo &method_info = m->get(); //method name - method_api.method_name = m->get().name; + method_api.method_name = method_info.name; //method return type if (method_api.method_name.find(":") != -1) { method_api.return_type = method_api.method_name.get_slice(":", 1); @@ -321,6 +355,11 @@ List<ClassAPI> generate_c_api_classes() { arg_type = arg_info.hint_string; } else if (arg_info.type == Variant::NIL) { arg_type = "Variant"; + } else if (arg_info.type == Variant::OBJECT) { + arg_type = arg_info.class_name; + if (arg_type == "") { + arg_type = Variant::get_type_name(arg_info.type); + } } else { arg_type = Variant::get_type_name(arg_info.type); } diff --git a/modules/gdnative/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h index de234b2f0d..73310d1974 100644 --- a/modules/gdnative/nativescript/api_generator.h +++ b/modules/gdnative/nativescript/api_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index c39efe126f..863999d6d4 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index bcaf3f346e..f30c9da4c1 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #include "gdnative/gdnative.h" +#include "core/core_string_names.h" #include "core/global_constants.h" #include "core/io/file_access_encrypted.h" #include "core/os/file_access.h" @@ -39,7 +40,7 @@ #include "core/project_settings.h" #include "scene/main/scene_tree.h" -#include "scene/resources/scene_format_text.h" +#include "scene/resources/resource_format_text.h" #include <stdlib.h> @@ -160,8 +161,10 @@ bool NativeScript::can_instance() const { NativeScriptDesc *script_data = get_script_desc(); #ifdef TOOLS_ENABLED - - return script_data || (!is_tool() && !ScriptServer::is_scripting_enabled()); + // Only valid if this is either a tool script or a "regular" script. + // (so an environment whre scripting is disabled (and not the editor) would not + // create objects). + return script_data && (is_tool() || ScriptServer::is_scripting_enabled()); #else return script_data; #endif @@ -199,25 +202,6 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) { return NULL; } -#ifdef TOOLS_ENABLED - if (!ScriptServer::is_scripting_enabled() && !is_tool()) { - // placeholder for nodes. For tools we want the rool thing. - - PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(NSL, Ref<Script>(this), p_this)); - placeholders.insert(sins); - - if (script_data->create_func.create_func) { - script_data->create_func.create_func( - (godot_object *)p_this, - script_data->create_func.method_data); - } - - _update_placeholder(sins); - - return sins; - } -#endif - NativeScriptInstance *nsi = memnew(NativeScriptInstance); nsi->owner = p_this; @@ -246,6 +230,19 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) { return nsi; } +PlaceHolderScriptInstance *NativeScript::placeholder_instance_create(Object *p_this) { +#ifdef TOOLS_ENABLED + PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(NSL, Ref<Script>(this), p_this)); + placeholders.insert(sins); + + _update_placeholder(sins); + + return sins; +#else + return NULL; +#endif +} + bool NativeScript::instance_has(const Object *p_this) const { return instance_owners.has((Object *)p_this); } @@ -775,6 +772,27 @@ void NativeScriptInstance::notification(int p_notification) { call_multilevel("_notification", args, 1); } +String NativeScriptInstance::to_string(bool *r_valid) { + if (has_method(CoreStringNames::get_singleton()->_to_string)) { + Variant::CallError ce; + Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce); + if (ce.error == Variant::CallError::CALL_OK) { + if (ret.get_type() != Variant::STRING) { + if (r_valid) + *r_valid = false; + ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String."); + ERR_FAIL_V(String()); + } + if (r_valid) + *r_valid = true; + return ret.operator String(); + } + } + if (r_valid) + *r_valid = false; + return String(); +} + void NativeScriptInstance::refcount_incremented() { Variant::CallError err; call("_refcount_incremented", NULL, 0, err); @@ -1036,8 +1054,16 @@ NativeScriptLanguage::~NativeScriptLanguage() { for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) { - if (L->get().is_valid()) - L->get()->terminate(); + Ref<GDNative> lib = L->get(); + // only shut down valid libs, duh! + if (lib.is_valid()) { + + // If it's a singleton-library then the gdnative module + // manages the destruction at engine shutdown, not NativeScript. + if (!lib->get_library()->is_singleton()) { + lib->terminate(); + } + } } NSL->library_classes.clear(); @@ -1305,7 +1331,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) { for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) { Vector<void *> &binding_data = *E->get(); - if (binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data) + if (p_idx < binding_data.size() && binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data) binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]); } @@ -1341,7 +1367,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec if (!(*binding_data)[p_idx]) { - const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name()); + const void *global_type_tag = get_global_type_tag(p_idx, p_object->get_class_name()); // no binding data yet, soooooo alloc new one \o/ (*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); @@ -1450,6 +1476,9 @@ const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_cl const HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + if (!tags.has(p_class_name)) + return NULL; + const void *tag = tags.get(p_class_name); return tag; @@ -1599,18 +1628,20 @@ bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const } String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { - Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript"); - if (script.is_valid()) { + if (!p_path.empty()) { + Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript"); + if (script.is_valid()) { + if (r_base_type) + *r_base_type = script->get_instance_base_type(); + if (r_icon_path) + *r_icon_path = script->get_script_class_icon_path(); + return script->get_script_class_name(); + } if (r_base_type) - *r_base_type = script->get_instance_base_type(); + *r_base_type = String(); if (r_icon_path) - *r_icon_path = script->get_script_class_icon_path(); - return script->get_script_class_name(); + *r_icon_path = String(); } - if (r_base_type) - *r_base_type = String(); - if (r_icon_path) - *r_icon_path = String(); return String(); } @@ -1639,10 +1670,19 @@ void NativeReloadNode::_notification(int p_what) { continue; } + // Don't unload what should not be reloaded! if (!gdn->get_library()->is_reloadable()) { continue; } + // singleton libraries might have alive pointers living inside the + // editor. Also reloading a singleton library would mean that + // the singleton entry will not be called again, as this only + // happens at engine startup. + if (gdn->get_library()->is_singleton()) { + continue; + } + gdn->terminate(); } @@ -1670,6 +1710,12 @@ void NativeReloadNode::_notification(int p_what) { continue; } + // since singleton libraries are not unloaded there is no point + // in loading them again. + if (gdn->get_library()->is_singleton()) { + continue; + } + if (!gdn->initialize()) { libs_to_remove.insert(L->key()); continue; diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index e6f3c06ee5..79a41df107 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -99,7 +99,7 @@ struct NativeScriptDesc { }; class NativeScript : public Script { - GDCLASS(NativeScript, Script) + GDCLASS(NativeScript, Script); #ifdef TOOLS_ENABLED Set<PlaceHolderScriptInstance *> placeholders; @@ -149,6 +149,7 @@ public: virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so virtual ScriptInstance *instance_create(Object *p_this); + virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this); virtual bool instance_has(const Object *p_this) const; virtual bool has_source_code() const; @@ -207,6 +208,7 @@ public: virtual bool has_method(const StringName &p_method) const; virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); virtual void notification(int p_notification); + String to_string(bool *r_valid); virtual Ref<Script> get_script() const; virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; @@ -368,7 +370,7 @@ inline NativeScriptDesc *NativeScript::get_script_desc() const { } class NativeReloadNode : public Node { - GDCLASS(NativeReloadNode, Node) + GDCLASS(NativeReloadNode, Node); bool unloaded; public: diff --git a/modules/gdnative/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp index 4433c0a638..c602b80646 100644 --- a/modules/gdnative/nativescript/register_types.cpp +++ b/modules/gdnative/nativescript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,8 +39,8 @@ NativeScriptLanguage *native_script_language; -ResourceFormatLoaderNativeScript *resource_loader_gdns = NULL; -ResourceFormatSaverNativeScript *resource_saver_gdns = NULL; +Ref<ResourceFormatLoaderNativeScript> resource_loader_gdns; +Ref<ResourceFormatSaverNativeScript> resource_saver_gdns; void register_nativescript_types() { native_script_language = memnew(NativeScriptLanguage); @@ -50,18 +50,20 @@ void register_nativescript_types() { native_script_language->set_language_index(ScriptServer::get_language_count()); ScriptServer::register_language(native_script_language); - resource_saver_gdns = memnew(ResourceFormatSaverNativeScript); + resource_saver_gdns.instance(); ResourceSaver::add_resource_format_saver(resource_saver_gdns); - resource_loader_gdns = memnew(ResourceFormatLoaderNativeScript); + resource_loader_gdns.instance(); ResourceLoader::add_resource_format_loader(resource_loader_gdns); } void unregister_nativescript_types() { - memdelete(resource_loader_gdns); + ResourceLoader::remove_resource_format_loader(resource_loader_gdns); + resource_loader_gdns.unref(); - memdelete(resource_saver_gdns); + ResourceSaver::remove_resource_format_saver(resource_saver_gdns); + resource_saver_gdns.unref(); if (native_script_language) { ScriptServer::unregister_language(native_script_language); diff --git a/modules/gdnative/nativescript/register_types.h b/modules/gdnative/nativescript/register_types.h index 7389010f8e..3d2ce4c431 100644 --- a/modules/gdnative/nativescript/register_types.h +++ b/modules/gdnative/nativescript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub index e915703935..18ab9986b0 100644 --- a/modules/gdnative/net/SCsub +++ b/modules/gdnative/net/SCsub @@ -3,5 +3,11 @@ Import('env') Import('env_gdnative') -env_gdnative.add_source_files(env.modules_sources, '*.cpp') +env_net = env_gdnative.Clone() + +has_webrtc = env_net["module_webrtc_enabled"] +if has_webrtc: + env_net.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + +env_net.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp index e2d710b5ad..d2c95efa77 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp +++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,7 @@ void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multip Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); - return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size); + return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); } Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { @@ -113,6 +113,8 @@ NetworkedMultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connecti } void MultiplayerPeerGDNative::_bind_methods() { + ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE); + ADD_PROPERTY_DEFAULT("refuse_new_connections", true); } extern "C" { diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h index c8c95b3dd7..6fbc62aaa1 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.h +++ b/modules/gdnative/net/multiplayer_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "modules/gdnative/include/net/godot_net.h" class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer { - GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer) + GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer); protected: static void _bind_methods(); diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp index ceae79edc0..877baff9e2 100644 --- a/modules/gdnative/net/packet_peer_gdnative.cpp +++ b/modules/gdnative/net/packet_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,7 +46,7 @@ void PacketPeerGDNative::_bind_methods() { Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); - return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size); + return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); } Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h index 71814177ed..8483217210 100644 --- a/modules/gdnative/net/packet_peer_gdnative.h +++ b/modules/gdnative/net/packet_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "modules/gdnative/include/net/godot_net.h" class PacketPeerGDNative : public PacketPeer { - GDCLASS(PacketPeerGDNative, PacketPeer) + GDCLASS(PacketPeerGDNative, PacketPeer); protected: static void _bind_methods(); diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp index c3fb4d8008..51448bfcd3 100644 --- a/modules/gdnative/net/register_types.cpp +++ b/modules/gdnative/net/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h index 9545a2ba8f..4f5b0051b7 100644 --- a/modules/gdnative/net/register_types.h +++ b/modules/gdnative/net/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp index 4d1f2ec9a5..8a4ea35f95 100644 --- a/modules/gdnative/net/stream_peer_gdnative.cpp +++ b/modules/gdnative/net/stream_peer_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,7 @@ StreamPeerGDNative::StreamPeerGDNative() { StreamPeerGDNative::~StreamPeerGDNative() { } -void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) { +void StreamPeerGDNative::set_native_stream_peer(const godot_net_stream_peer *p_interface) { interface = p_interface; } @@ -51,7 +51,7 @@ Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) { Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); - return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, r_sent)); + return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, &r_sent)); } Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) { @@ -61,7 +61,7 @@ Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) { Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); - return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, r_received)); + return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, &r_received)); } int StreamPeerGDNative::get_available_bytes() const { @@ -71,7 +71,7 @@ int StreamPeerGDNative::get_available_bytes() const { extern "C" { -void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface) { +void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface) { ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface); } } diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h index 654234e6ab..7859d57a4b 100644 --- a/modules/gdnative/net/stream_peer_gdnative.h +++ b/modules/gdnative/net/stream_peer_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,14 +41,14 @@ class StreamPeerGDNative : public StreamPeer { protected: static void _bind_methods(); - godot_net_stream_peer *interface; + const godot_net_stream_peer *interface; public: StreamPeerGDNative(); ~StreamPeerGDNative(); /* Sets the interface implementation from GDNative */ - void set_native_stream_peer(godot_net_stream_peer *p_interface); + void set_native_stream_peer(const godot_net_stream_peer *p_interface); /* Specific to StreamPeer */ Error put_data(const uint8_t *p_data, int p_bytes); diff --git a/modules/gdnative/net/webrtc_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp new file mode 100644 index 0000000000..d77fa057c5 --- /dev/null +++ b/modules/gdnative/net/webrtc_gdnative.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* webrtc_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" + +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "modules/webrtc/webrtc_data_channel_gdnative.h" +#include "modules/webrtc/webrtc_peer_connection_gdnative.h" +#endif + +extern "C" { + +void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *p_impl) { +#ifdef WEBRTC_GDNATIVE_ENABLED + ((WebRTCPeerConnectionGDNative *)p_obj)->set_native_webrtc_peer_connection(p_impl); +#endif +} + +void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *p_impl) { +#ifdef WEBRTC_GDNATIVE_ENABLED + ((WebRTCDataChannelGDNative *)p_obj)->set_native_webrtc_data_channel(p_impl); +#endif +} + +godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p_lib) { +#ifdef WEBRTC_GDNATIVE_ENABLED + return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib); +#else + return ERR_UNAVAILABLE; +#endif +} +} diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp index 13026e8e7a..4b5dcc2c11 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.cpp +++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index 8be6a4ccaa..381c334231 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -62,7 +62,7 @@ public: virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); #if 0 // Rely on default implementations provided by ScriptInstance for the moment. - // Note that multilevel call could be removed in 3.0 release, so stay tunned + // Note that multilevel call could be removed in 3.0 release, so stay tuned // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes) virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount); virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount); diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index 2b538c4a36..9de073fc8e 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -159,22 +159,22 @@ String PluginScriptLanguage::make_function(const String &p_class, const String & return String(); } -Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { +Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { if (_desc.complete_code) { Array options; godot_error tmp = _desc.complete_code( _data, (godot_string *)&p_code, - (godot_string *)&p_base_path, + (godot_string *)&p_path, (godot_object *)p_owner, (godot_array *)&options, &r_force, (godot_string *)&r_call_hint); for (int i = 0; i < options.size(); i++) { - r_options->push_back(String(options[i])); + ScriptCodeCompletionOption option(options[i], ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_options->push_back(option); } - Error err = *(Error *)&tmp; - return err; + return (Error)tmp; } return ERR_UNAVAILABLE; } @@ -217,7 +217,7 @@ void PluginScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_ Dictionary constants; _desc.get_public_constants(_data, (godot_dictionary *)&constants); for (const Variant *key = constants.next(); key; key = constants.next(key)) { - Variant value = constants[key]; + Variant value = constants[*key]; p_constants->push_back(Pair<String, Variant>(*key, value)); } } @@ -417,8 +417,8 @@ void PluginScriptLanguage::unlock() { PluginScriptLanguage::PluginScriptLanguage(const godot_pluginscript_language_desc *desc) : _desc(*desc) { - _resource_loader = memnew(ResourceFormatLoaderPluginScript(this)); - _resource_saver = memnew(ResourceFormatSaverPluginScript(this)); + _resource_loader = Ref<ResourceFormatLoaderPluginScript>(memnew(ResourceFormatLoaderPluginScript(this))); + _resource_saver = Ref<ResourceFormatSaverPluginScript>(memnew(ResourceFormatSaverPluginScript(this))); // TODO: totally remove _lock attribute if NO_THREADS is set #ifdef NO_THREADS @@ -429,8 +429,6 @@ PluginScriptLanguage::PluginScriptLanguage(const godot_pluginscript_language_des } PluginScriptLanguage::~PluginScriptLanguage() { - memdelete(_resource_loader); - memdelete(_resource_saver); #ifndef NO_THREADS if (_lock) { memdelete(_lock); diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index c4df6f3a33..7b3844d0b0 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -48,8 +48,8 @@ class PluginScriptLanguage : public ScriptLanguage { friend class PluginScript; friend class PluginScriptInstance; - ResourceFormatLoaderPluginScript *_resource_loader; - ResourceFormatSaverPluginScript *_resource_saver; + Ref<ResourceFormatLoaderPluginScript> _resource_loader; + Ref<ResourceFormatSaverPluginScript> _resource_saver; const godot_pluginscript_language_desc _desc; godot_pluginscript_language_data *_data; @@ -59,8 +59,8 @@ class PluginScriptLanguage : public ScriptLanguage { public: virtual String get_name() const; - _FORCE_INLINE_ ResourceFormatLoaderPluginScript *get_resource_loader() { return _resource_loader; }; - _FORCE_INLINE_ ResourceFormatSaverPluginScript *get_resource_saver() { return _resource_saver; }; + _FORCE_INLINE_ Ref<ResourceFormatLoaderPluginScript> get_resource_loader() { return _resource_loader; } + _FORCE_INLINE_ Ref<ResourceFormatSaverPluginScript> get_resource_saver() { return _resource_saver; } /* LANGUAGE FUNCTIONS */ virtual void init(); @@ -81,7 +81,7 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint); virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp index 52d5b2b595..c7ff8518b5 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.cpp +++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h index 5c17bb932e..6218037a15 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.h +++ b/modules/gdnative/pluginscript/pluginscript_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,7 @@ class PluginScriptLanguage; class ResourceFormatLoaderPluginScript : public ResourceFormatLoader { + PluginScriptLanguage *_language; public: @@ -50,6 +51,7 @@ public: }; class ResourceFormatSaverPluginScript : public ResourceFormatSaver { + PluginScriptLanguage *_language; public: diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index c3a623e9a1..3ecb29404a 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,17 +34,17 @@ #include "pluginscript_instance.h" #include "pluginscript_script.h" -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED #define __ASSERT_SCRIPT_REASON "Cannot retrieve pluginscript class for this script, is you code correct ?" #define ASSERT_SCRIPT_VALID() \ { \ ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ - ERR_FAIL_COND(!can_instance()) \ + ERR_FAIL_COND(!can_instance()); \ } -#define ASSERT_SCRIPT_VALID_V(ret) \ - { \ - ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ - ERR_FAIL_COND_V(!can_instance(), ret) \ +#define ASSERT_SCRIPT_VALID_V(ret) \ + { \ + ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ + ERR_FAIL_COND_V(!can_instance(), ret); \ } #else #define ASSERT_SCRIPT_VALID() @@ -52,6 +52,79 @@ #endif void PluginScript::_bind_methods() { + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &PluginScript::_new, MethodInfo(Variant::OBJECT, "new")); +} + +PluginScriptInstance *PluginScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, Variant::CallError &r_error) { + + r_error.error = Variant::CallError::CALL_OK; + + // Create instance + PluginScriptInstance *instance = memnew(PluginScriptInstance()); + + if (instance->init(this, p_owner)) { + _language->lock(); + _instances.insert(instance->get_owner()); + _language->unlock(); + } else { + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + memdelete(instance); + ERR_FAIL_V(NULL); + } + + // Construct + // TODO: Support arguments in the constructor? + // There is currently no way to get the constructor function name of the script. + // instance->call("__init__", p_args, p_argcount, r_error); + if (p_argcount > 0) { + WARN_PRINT("PluginScript doesn't support arguments in the constructor"); + } + + return instance; +} + +Variant PluginScript::_new(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + + r_error.error = Variant::CallError::CALL_OK; + + if (!_valid) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + REF ref; + Object *owner = NULL; + + if (get_instance_base_type() == "") { + owner = memnew(Reference); + } else { + owner = ClassDB::instance(get_instance_base_type()); + } + + if (!owner) { + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return Variant(); + } + + Reference *r = Object::cast_to<Reference>(owner); + if (r) { + ref = REF(r); + } + + PluginScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r_error); + + if (!instance) { + if (ref.is_null()) { + memdelete(owner); //no owner, sorry + } + return Variant(); + } + + if (ref.is_valid()) { + return ref; + } else { + return owner; + } } #ifdef TOOLS_ENABLED @@ -129,17 +202,8 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) { } } - PluginScriptInstance *instance = memnew(PluginScriptInstance()); - const bool success = instance->init(this, p_this); - if (success) { - _language->lock(); - _instances.insert(instance->get_owner()); - _language->unlock(); - return instance; - } else { - memdelete(instance); - ERR_FAIL_V(NULL); - } + Variant::CallError unchecked_error; + return _create_instance(NULL, 0, p_this, unchecked_error); } bool PluginScript::instance_has(const Object *p_this) const { @@ -165,6 +229,8 @@ void PluginScript::set_source_code(const String &p_code) { } Error PluginScript::reload(bool p_keep_state) { + ERR_FAIL_COND_V(!_language, ERR_UNCONFIGURED); + _language->lock(); ERR_FAIL_COND_V(!p_keep_state && _instances.size(), ERR_ALREADY_IN_USE); _language->unlock(); @@ -220,7 +286,7 @@ Error PluginScript::reload(bool p_keep_state) { Dictionary *members = (Dictionary *)&manifest.member_lines; for (const Variant *key = members->next(); key != NULL; key = members->next(key)) { - _member_lines[*key] = (*members)[key]; + _member_lines[*key] = (*members)[*key]; } Array *methods = (Array *)&manifest.methods; for (int i = 0; i < methods->size(); ++i) { @@ -409,6 +475,8 @@ MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable PluginScript::PluginScript() : _data(NULL), + _desc(NULL), + _language(NULL), _tool(false), _valid(false), _script_list(this) { @@ -426,11 +494,15 @@ void PluginScript::init(PluginScriptLanguage *language) { } PluginScript::~PluginScript() { - _desc->finish(_data); + if (_desc && _data) { + _desc->finish(_data); + } #ifdef DEBUG_ENABLED - _language->lock(); - _language->_script_list.remove(&_script_list); - _language->unlock(); + if (_language) { + _language->lock(); + _language->_script_list.remove(&_script_list); + _language->unlock(); + } #endif } diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 3ade8ac004..0a84ccba0d 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,8 +37,6 @@ #include "pluginscript_language.h" #include <pluginscript/godot_pluginscript.h> -class PyInstance; - class PluginScript : public Script { GDCLASS(PluginScript, Script); @@ -74,6 +72,9 @@ private: protected: static void _bind_methods(); + PluginScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, Variant::CallError &r_error); + Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + #ifdef TOOLS_ENABLED Set<PlaceHolderScriptInstance *> placeholders; //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp index e677bc9867..b7ab887e11 100644 --- a/modules/gdnative/pluginscript/register_types.cpp +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/pluginscript/register_types.h b/modules/gdnative/pluginscript/register_types.h index 76651aa986..3114fb6b7d 100644 --- a/modules/gdnative/pluginscript/register_types.h +++ b/modules/gdnative/pluginscript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 62e87c3651..6ff6262b56 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,6 +38,7 @@ #include "nativescript/register_types.h" #include "net/register_types.h" #include "pluginscript/register_types.h" +#include "videodecoder/register_types.h" #include "core/engine.h" #include "core/io/resource_loader.h" @@ -49,97 +50,6 @@ #include "editor/editor_node.h" #include "gdnative_library_editor_plugin.h" #include "gdnative_library_singleton_editor.h" -// Class used to discover singleton gdnative files - -static void actual_discoverer_handler(); - -class GDNativeSingletonDiscover : public Object { - // GDCLASS(GDNativeSingletonDiscover, Object) - - virtual String get_class() const { - // okay, this is a really dirty hack. - // We're overriding get_class so we can connect it to a signal - // This works because get_class is a virtual method, so we don't - // need to register a new class to ClassDB just for this one - // little signal. - - actual_discoverer_handler(); - - return "Object"; - } -}; - -static Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) { - - Set<String> file_paths; - - // check children - - for (int i = 0; i < p_dir->get_file_count(); i++) { - String file_name = p_dir->get_file(i); - String file_type = p_dir->get_file_type(i); - - if (file_type != "GDNativeLibrary") { - continue; - } - - Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); - if (lib.is_valid() && lib->is_singleton()) { - file_paths.insert(p_dir->get_file_path(i)); - } - } - - // check subdirectories - for (int i = 0; i < p_dir->get_subdir_count(); i++) { - Set<String> paths = get_gdnative_singletons(p_dir->get_subdir(i)); - - for (Set<String>::Element *E = paths.front(); E; E = E->next()) { - file_paths.insert(E->get()); - } - } - - return file_paths; -} - -static void actual_discoverer_handler() { - - EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem(); - - Set<String> file_paths = get_gdnative_singletons(dir); - - bool changed = false; - Array current_files; - if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - current_files = ProjectSettings::get_singleton()->get("gdnative/singletons"); - } - Array files; - files.resize(file_paths.size()); - int i = 0; - for (Set<String>::Element *E = file_paths.front(); E; i++, E = E->next()) { - if (!current_files.has(E->get())) { - changed = true; - } - files.set(i, E->get()); - } - - // Check for removed files - if (!changed) { - for (int i = 0; i < current_files.size(); i++) { - if (!file_paths.has(current_files[i])) { - changed = true; - break; - } - } - } - - if (changed) { - - ProjectSettings::get_singleton()->set("gdnative/singletons", files); - ProjectSettings::get_singleton()->save(); - } -} - -static GDNativeSingletonDiscover *discoverer = NULL; class GDNativeExportPlugin : public EditorExportPlugin { @@ -190,6 +100,11 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty } String entry_lib_path = config->get_value("entry", key); + if (!entry_lib_path.begins_with("res://")) { + print_line("Skipping export of out-of-project library " + entry_lib_path); + continue; + } + add_shared_object(entry_lib_path, tags); } } @@ -219,6 +134,10 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty Vector<String> dependency_paths = config->get_value("dependencies", key); for (int i = 0; i < dependency_paths.size(); i++) { + if (!dependency_paths[i].begins_with("res://")) { + print_line("Skipping export of out-of-project library " + dependency_paths[i]); + continue; + } add_shared_object(dependency_paths[i], tags); } } @@ -274,9 +193,6 @@ static void editor_init_callback() { library_editor->set_name(TTR("GDNative")); ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(library_editor); - discoverer = memnew(GDNativeSingletonDiscover); - EditorFileSystem::get_singleton()->connect("filesystem_changed", discoverer, "get_class"); - Ref<GDNativeExportPlugin> export_plugin; export_plugin.instance(); @@ -299,8 +215,8 @@ GDNativeCallRegistry *GDNativeCallRegistry::singleton; Vector<Ref<GDNative> > singleton_gdnatives; -GDNativeLibraryResourceLoader *resource_loader_gdnlib = NULL; -GDNativeLibraryResourceSaver *resource_saver_gdnlib = NULL; +Ref<GDNativeLibraryResourceLoader> resource_loader_gdnlib; +Ref<GDNativeLibraryResourceSaver> resource_saver_gdnlib; void register_gdnative_types() { @@ -312,10 +228,10 @@ void register_gdnative_types() { ClassDB::register_class<GDNativeLibrary>(); ClassDB::register_class<GDNative>(); - resource_loader_gdnlib = memnew(GDNativeLibraryResourceLoader); - resource_saver_gdnlib = memnew(GDNativeLibraryResourceSaver); - + resource_loader_gdnlib.instance(); ResourceLoader::add_resource_format_loader(resource_loader_gdnlib); + + resource_saver_gdnlib.instance(); ResourceSaver::add_resource_format_saver(resource_saver_gdnlib); GDNativeCallRegistry::singleton = memnew(GDNativeCallRegistry); @@ -326,6 +242,7 @@ void register_gdnative_types() { register_arvr_types(); register_nativescript_types(); register_pluginscript_types(); + register_videodecoder_types(); // run singletons @@ -333,30 +250,36 @@ void register_gdnative_types() { if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); } - - singleton_gdnatives.resize(singletons.size()); + Array excluded = Array(); + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + excluded = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); + } for (int i = 0; i < singletons.size(); i++) { String path = singletons[i]; - Ref<GDNativeLibrary> lib = ResourceLoader::load(path); + if (excluded.has(path)) + continue; - singleton_gdnatives.write[i].instance(); - singleton_gdnatives.write[i]->set_library(lib); + Ref<GDNativeLibrary> lib = ResourceLoader::load(path); + Ref<GDNative> singleton; + singleton.instance(); + singleton->set_library(lib); - if (!singleton_gdnatives.write[i]->initialize()) { + if (!singleton->initialize()) { // Can't initialize. Don't make a native_call then continue; } void *proc_ptr; - Error err = singleton_gdnatives[i]->get_symbol( + Error err = singleton->get_symbol( lib->get_symbol_prefix() + "gdnative_singleton", proc_ptr); if (err != OK) { - ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); + ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); } else { + singleton_gdnatives.push_back(singleton); ((void (*)())proc_ptr)(); } } @@ -378,6 +301,7 @@ void unregister_gdnative_types() { } singleton_gdnatives.clear(); + unregister_videodecoder_types(); unregister_pluginscript_types(); unregister_nativescript_types(); unregister_arvr_types(); @@ -385,14 +309,11 @@ void unregister_gdnative_types() { memdelete(GDNativeCallRegistry::singleton); -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && discoverer != NULL) { - memdelete(discoverer); - } -#endif + ResourceLoader::remove_resource_format_loader(resource_loader_gdnlib); + resource_loader_gdnlib.unref(); - memdelete(resource_loader_gdnlib); - memdelete(resource_saver_gdnlib); + ResourceSaver::remove_resource_format_saver(resource_saver_gdnlib); + resource_saver_gdnlib.unref(); // This is for printing out the sizes of the core types diff --git a/modules/gdnative/register_types.h b/modules/gdnative/register_types.h index 4549687f55..9e5cab5581 100644 --- a/modules/gdnative/register_types.h +++ b/modules/gdnative/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdnative/videodecoder/SCsub b/modules/gdnative/videodecoder/SCsub new file mode 100644 index 0000000000..04cc8ed604 --- /dev/null +++ b/modules/gdnative/videodecoder/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_vsdecoder_gdnative = env_modules.Clone() + +env_vsdecoder_gdnative.Prepend(CPPPATH=['#modules/gdnative/include/']) +env_vsdecoder_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/videodecoder/register_types.cpp b/modules/gdnative/videodecoder/register_types.cpp new file mode 100644 index 0000000000..0a0b2f64d5 --- /dev/null +++ b/modules/gdnative/videodecoder/register_types.cpp @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "core/class_db.h" +#include "video_stream_gdnative.h" + +static Ref<ResourceFormatLoaderVideoStreamGDNative> resource_loader_vsgdnative; + +void register_videodecoder_types() { + + resource_loader_vsgdnative.instance(); + ResourceLoader::add_resource_format_loader(resource_loader_vsgdnative, true); + + ClassDB::register_class<VideoStreamGDNative>(); +} + +void unregister_videodecoder_types() { + + ResourceLoader::remove_resource_format_loader(resource_loader_vsgdnative); + resource_loader_vsgdnative.unref(); +} diff --git a/modules/gdnative/videodecoder/register_types.h b/modules/gdnative/videodecoder/register_types.h new file mode 100644 index 0000000000..f082343d8c --- /dev/null +++ b/modules/gdnative/videodecoder/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_videodecoder_types(); +void unregister_videodecoder_types(); diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp new file mode 100644 index 0000000000..be131c5402 --- /dev/null +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -0,0 +1,391 @@ +/*************************************************************************/ +/* video_stream_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "video_stream_gdnative.h" + +#include "core/project_settings.h" +#include "servers/audio_server.h" + +VideoDecoderServer *VideoDecoderServer::instance = NULL; + +static VideoDecoderServer decoder_server; + +const int AUX_BUFFER_SIZE = 1024; // Buffer 1024 samples. + +// NOTE: Callbacks for the GDNative libraries. +extern "C" { +godot_int GDAPI godot_videodecoder_file_read(void *ptr, uint8_t *buf, int buf_size) { + // ptr is a FileAccess + FileAccess *file = reinterpret_cast<FileAccess *>(ptr); + + // if file exists + if (file) { + long bytes_read = file->get_buffer(buf, buf_size); + // No bytes to read => EOF + if (bytes_read == 0) { + return 0; + } + return bytes_read; + } + return -1; +} + +int64_t GDAPI godot_videodecoder_file_seek(void *ptr, int64_t pos, int whence) { + // file + FileAccess *file = reinterpret_cast<FileAccess *>(ptr); + + if (file) { + size_t len = file->get_len(); + switch (whence) { + case SEEK_SET: { + // Just for explicitness + size_t new_pos = static_cast<size_t>(pos); + if (new_pos > len) { + return -1; + } + file->seek(new_pos); + pos = static_cast<int64_t>(file->get_position()); + return pos; + } break; + case SEEK_CUR: { + // Just in case it doesn't exist + if (pos < 0 && (size_t)-pos > file->get_position()) { + return -1; + } + pos = pos + static_cast<int>(file->get_position()); + file->seek(pos); + pos = static_cast<int64_t>(file->get_position()); + return pos; + } break; + case SEEK_END: { + // Just in case something goes wrong + if ((size_t)-pos > len) { + return -1; + } + file->seek_end(pos); + pos = static_cast<int64_t>(file->get_position()); + return pos; + } break; + default: { + // Only 4 possible options, hence default = AVSEEK_SIZE + // Asks to return the length of file + return static_cast<int64_t>(len); + } break; + } + } + // In case nothing works out. + return -1; +} + +void GDAPI godot_videodecoder_register_decoder(const godot_videodecoder_interface_gdnative *p_interface) { + + decoder_server.register_decoder_interface(p_interface); +} +} + +// VideoStreamPlaybackGDNative starts here. + +bool VideoStreamPlaybackGDNative::open_file(const String &p_file) { + ERR_FAIL_COND_V(interface == NULL, false); + file = FileAccess::open(p_file, FileAccess::READ); + bool file_opened = interface->open_file(data_struct, file); + + if (file_opened) { + num_channels = interface->get_channels(data_struct); + mix_rate = interface->get_mix_rate(data_struct); + + godot_vector2 vec = interface->get_texture_size(data_struct); + texture_size = *(Vector2 *)&vec; + + pcm = (float *)memalloc(num_channels * AUX_BUFFER_SIZE * sizeof(float)); + memset(pcm, 0, num_channels * AUX_BUFFER_SIZE * sizeof(float)); + pcm_write_idx = -1; + samples_decoded = 0; + + texture->create((int)texture_size.width, (int)texture_size.height, Image::FORMAT_RGBA8, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE); + } + + return file_opened; +} + +void VideoStreamPlaybackGDNative::update(float p_delta) { + if (!playing || paused) { + return; + } + if (!file) { + return; + } + time += p_delta; + ERR_FAIL_COND(interface == NULL); + interface->update(data_struct, p_delta); + + if (mix_callback) { + if (pcm_write_idx >= 0) { + // Previous remains + int mixed = mix_callback(mix_udata, pcm, samples_decoded); + if (mixed == samples_decoded) { + pcm_write_idx = -1; + } else { + samples_decoded -= mixed; + pcm_write_idx += mixed; + } + } + if (pcm_write_idx < 0) { + samples_decoded = interface->get_audioframe(data_struct, pcm, AUX_BUFFER_SIZE); + pcm_write_idx = mix_callback(mix_udata, pcm, samples_decoded); + if (pcm_write_idx == samples_decoded) { + pcm_write_idx = -1; + } else { + samples_decoded -= pcm_write_idx; + } + } + } + + while (interface->get_playback_position(data_struct) < time && playing) { + + update_texture(); + } +} + +void VideoStreamPlaybackGDNative::update_texture() { + PoolByteArray *pba = (PoolByteArray *)interface->get_videoframe(data_struct); + + if (pba == NULL) { + playing = false; + return; + } + + Ref<Image> img = memnew(Image(texture_size.width, texture_size.height, 0, Image::FORMAT_RGBA8, *pba)); + + texture->set_data(img); +} + +// ctor and dtor + +VideoStreamPlaybackGDNative::VideoStreamPlaybackGDNative() : + texture(Ref<ImageTexture>(memnew(ImageTexture))), + playing(false), + paused(false), + mix_udata(NULL), + mix_callback(NULL), + num_channels(-1), + time(0), + mix_rate(0), + delay_compensation(0), + pcm(NULL), + pcm_write_idx(0), + samples_decoded(0), + file(NULL), + interface(NULL), + data_struct(NULL) {} + +VideoStreamPlaybackGDNative::~VideoStreamPlaybackGDNative() { + cleanup(); +} + +void VideoStreamPlaybackGDNative::cleanup() { + if (data_struct) + interface->destructor(data_struct); + if (pcm) + memfree(pcm); + pcm = NULL; + time = 0; + num_channels = -1; + interface = NULL; + data_struct = NULL; +} + +void VideoStreamPlaybackGDNative::set_interface(const godot_videodecoder_interface_gdnative *p_interface) { + ERR_FAIL_COND(p_interface == NULL); + if (interface != NULL) { + cleanup(); + } + interface = p_interface; + data_struct = interface->constructor((godot_object *)this); +} + +// controls + +bool VideoStreamPlaybackGDNative::is_playing() const { + return playing; +} + +bool VideoStreamPlaybackGDNative::is_paused() const { + return paused; +} + +void VideoStreamPlaybackGDNative::play() { + + stop(); + + playing = true; + + delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms"); + delay_compensation /= 1000.0; +} + +void VideoStreamPlaybackGDNative::stop() { + if (playing) { + seek(0); + } + playing = false; +} + +void VideoStreamPlaybackGDNative::seek(float p_time) { + ERR_FAIL_COND(interface == NULL); + interface->seek(data_struct, p_time); +} + +void VideoStreamPlaybackGDNative::set_paused(bool p_paused) { + paused = p_paused; +} + +Ref<Texture> VideoStreamPlaybackGDNative::get_texture() { + return texture; +} + +float VideoStreamPlaybackGDNative::get_length() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_length(data_struct); +} + +float VideoStreamPlaybackGDNative::get_playback_position() const { + + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_playback_position(data_struct); +} + +bool VideoStreamPlaybackGDNative::has_loop() const { + // TODO: Implement looping? + return false; +} + +void VideoStreamPlaybackGDNative::set_loop(bool p_enable) { + // Do nothing +} + +void VideoStreamPlaybackGDNative::set_audio_track(int p_idx) { + ERR_FAIL_COND(interface == NULL); + interface->set_audio_track(data_struct, p_idx); +} + +void VideoStreamPlaybackGDNative::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { + + mix_udata = p_userdata; + mix_callback = p_callback; +} + +int VideoStreamPlaybackGDNative::get_channels() const { + ERR_FAIL_COND_V(interface == NULL, 0); + + return (num_channels > 0) ? num_channels : 0; +} + +int VideoStreamPlaybackGDNative::get_mix_rate() const { + ERR_FAIL_COND_V(interface == NULL, 0); + + return mix_rate; +} + +/* --- NOTE VideoStreamGDNative starts here. ----- */ + +Ref<VideoStreamPlayback> VideoStreamGDNative::instance_playback() { + Ref<VideoStreamPlaybackGDNative> pb = memnew(VideoStreamPlaybackGDNative); + VideoDecoderGDNative *decoder = decoder_server.get_decoder(file.get_extension().to_lower()); + if (decoder == NULL) + return NULL; + pb->set_interface(decoder->interface); + pb->set_audio_track(audio_track); + if (pb->open_file(file)) + return pb; + return NULL; +} + +void VideoStreamGDNative::set_file(const String &p_file) { + + file = p_file; +} + +String VideoStreamGDNative::get_file() { + + return file; +} + +void VideoStreamGDNative::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamGDNative::set_file); + ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamGDNative::get_file); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); +} + +void VideoStreamGDNative::set_audio_track(int p_track) { + + audio_track = p_track; +} + +/* --- NOTE ResourceFormatLoaderVideoStreamGDNative starts here. ----- */ + +RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error) { + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + return RES(); + } + memdelete(f); + VideoStreamGDNative *stream = memnew(VideoStreamGDNative); + stream->set_file(p_path); + Ref<VideoStreamGDNative> ogv_stream = Ref<VideoStreamGDNative>(stream); + if (r_error) { + *r_error = OK; + } + return ogv_stream; +} + +void ResourceFormatLoaderVideoStreamGDNative::get_recognized_extensions(List<String> *p_extensions) const { + Map<String, int>::Element *el = VideoDecoderServer::get_instance()->get_extensions().front(); + while (el) { + p_extensions->push_back(el->key()); + el = el->next(); + } +} + +bool ResourceFormatLoaderVideoStreamGDNative::handles_type(const String &p_type) const { + return ClassDB::is_parent_class(p_type, "VideoStream"); +} + +String ResourceFormatLoaderVideoStreamGDNative::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (VideoDecoderServer::get_instance()->get_extensions().has(el)) + return "VideoStreamGDNative"; + return ""; +} diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h new file mode 100644 index 0000000000..b9f1c8e4da --- /dev/null +++ b/modules/gdnative/videodecoder/video_stream_gdnative.h @@ -0,0 +1,207 @@ +/*************************************************************************/ +/* video_stream_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VIDEO_STREAM_GDNATIVE_H +#define VIDEO_STREAM_GDNATIVE_H + +#include "../gdnative.h" +#include "core/os/file_access.h" +#include "scene/resources/texture.h" +#include "scene/resources/video_stream.h" + +struct VideoDecoderGDNative { + const godot_videodecoder_interface_gdnative *interface; + String plugin_name; + Vector<String> supported_extensions; + + VideoDecoderGDNative() : + interface(NULL), + plugin_name("none") {} + + VideoDecoderGDNative(const godot_videodecoder_interface_gdnative *p_interface) : + interface(p_interface), + plugin_name(p_interface->get_plugin_name()) { + _get_supported_extensions(); + } + +private: + void _get_supported_extensions() { + supported_extensions.clear(); + int num_ext; + const char **supported_ext = interface->get_supported_extensions(&num_ext); + for (int i = 0; i < num_ext; i++) { + supported_extensions.push_back(supported_ext[i]); + } + } +}; + +class VideoDecoderServer { +private: + Vector<VideoDecoderGDNative *> decoders; + Map<String, int> extensions; + + static VideoDecoderServer *instance; + +public: + static VideoDecoderServer *get_instance() { + return instance; + } + + const Map<String, int> &get_extensions() { + return extensions; + } + + void register_decoder_interface(const godot_videodecoder_interface_gdnative *p_interface) { + VideoDecoderGDNative *decoder = memnew(VideoDecoderGDNative(p_interface)); + int index = decoders.size(); + for (int i = 0; i < decoder->supported_extensions.size(); i++) { + extensions[decoder->supported_extensions[i]] = index; + } + decoders.push_back(decoder); + } + + VideoDecoderGDNative *get_decoder(const String &extension) { + if (extensions.size() == 0 || !extensions.has(extension)) + return NULL; + return decoders[extensions[extension]]; + } + + VideoDecoderServer() { + instance = this; + } + + ~VideoDecoderServer() { + for (int i = 0; i < decoders.size(); i++) { + memdelete(decoders[i]); + } + decoders.clear(); + instance = NULL; + } +}; + +class VideoStreamPlaybackGDNative : public VideoStreamPlayback { + + GDCLASS(VideoStreamPlaybackGDNative, VideoStreamPlayback); + + Ref<ImageTexture> texture; + bool playing; + bool paused; + + Vector2 texture_size; + + void *mix_udata; + AudioMixCallback mix_callback; + + int num_channels; + float time; + int mix_rate; + double delay_compensation; + + float *pcm; + int pcm_write_idx; + int samples_decoded; + + void cleanup(); + void update_texture(); + +protected: + String file_name; + + FileAccess *file; + + const godot_videodecoder_interface_gdnative *interface; + void *data_struct; + +public: + VideoStreamPlaybackGDNative(); + ~VideoStreamPlaybackGDNative(); + + void set_interface(const godot_videodecoder_interface_gdnative *p_interface); + + bool open_file(const String &p_file); + + virtual void stop(); + virtual void play(); + + virtual bool is_playing() const; + + virtual void set_paused(bool p_paused); + virtual bool is_paused() const; + + virtual void set_loop(bool p_enable); + virtual bool has_loop() const; + + virtual float get_length() const; + + virtual float get_playback_position() const; + virtual void seek(float p_time); + + virtual void set_audio_track(int p_idx); + + //virtual int mix(int16_t* p_buffer,int p_frames)=0; + + virtual Ref<Texture> get_texture(); + virtual void update(float p_delta); + + virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); + virtual int get_channels() const; + virtual int get_mix_rate() const; +}; + +class VideoStreamGDNative : public VideoStream { + + GDCLASS(VideoStreamGDNative, VideoStream); + + String file; + int audio_track; + +protected: + static void + _bind_methods(); + +public: + void set_file(const String &p_file); + String get_file(); + + virtual void set_audio_track(int p_track); + virtual Ref<VideoStreamPlayback> instance_playback(); + + VideoStreamGDNative() {} +}; + +class ResourceFormatLoaderVideoStreamGDNative : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +#endif diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index 95b40d90af..a525eedaaa 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@GDScript", "GDScript", "GDScriptFunctionState", "GDScriptNativeClass", diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml new file mode 100644 index 0000000000..3870a5ea7d --- /dev/null +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -0,0 +1,1314 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="@GDScript" category="Core" version="3.2"> + <brief_description> + Built-in GDScript functions. + </brief_description> + <description> + List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.) + </description> + <tutorials> + </tutorials> + <methods> + <method name="Color8"> + <return type="Color"> + </return> + <argument index="0" name="r8" type="int"> + </argument> + <argument index="1" name="g8" type="int"> + </argument> + <argument index="2" name="b8" type="int"> + </argument> + <argument index="3" name="a8" type="int" default="255"> + </argument> + <description> + Returns a 32 bit color with red, green, blue and alpha channels. Each channel has 8 bits of information ranging from 0 to 255. + [code]r8[/code] red channel + [code]g8[/code] green channel + [code]b8[/code] blue channel + [code]a8[/code] alpha channel + [codeblock] + red = Color8(255, 0, 0) + [/codeblock] + </description> + </method> + <method name="ColorN"> + <return type="Color"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="alpha" type="float" default="1.0"> + </argument> + <description> + Returns a color according to the standardised [code]name[/code] with [code]alpha[/code] ranging from 0 to 1. + [codeblock] + red = ColorN("red", 1) + [/codeblock] + Supported color names: + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflower", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "webgray", "green", "webgreen", "greenyellow", "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", "lightgoldenrod", "lightgray", "lightgreen", "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "webmaroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navyblue", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "webpurple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen". + </description> + </method> + <method name="abs"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). + [codeblock] + # a is 1 + a = abs(-1) + [/codeblock] + </description> + </method> + <method name="acos"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code]. + [codeblock] + # c is 0.523599 or 30 degrees if converted with rad2deg(s) + c = acos(0.866025) + [/codeblock] + </description> + </method> + <method name="asin"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code]. + [codeblock] + # s is 0.523599 or 30 degrees if converted with rad2deg(s) + s = asin(0.5) + [/codeblock] + </description> + </method> + <method name="assert"> + <return type="void"> + </return> + <argument index="0" name="condition" type="bool"> + </argument> + <description> + Asserts that the [code]condition[/code] is [code]true[/code] . If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development. + [codeblock] + # Imagine we always want speed to be between 0 and 20 + speed = -10 + assert(speed < 20) # True, the program will continue + assert(speed >= 0) # False, the program will stop + assert(speed >= 0 && speed < 20) # You can also combine the two conditional statements in one check + [/codeblock] + </description> + </method> + <method name="atan"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code]. + The method cannot know in which quadrant the angle should fall. See [method atan2] if you always want an exact angle. + [codeblock] + a = atan(0.5) # a is 0.463648 + [/codeblock] + </description> + </method> + <method name="atan2"> + <return type="float"> + </return> + <argument index="0" name="y" type="float"> + </argument> + <argument index="1" name="x" type="float"> + </argument> + <description> + Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant. + [codeblock] + a = atan2(0, -1) # a is 3.141593 + [/codeblock] + </description> + </method> + <method name="bytes2var"> + <return type="Variant"> + </return> + <argument index="0" name="bytes" type="PoolByteArray"> + </argument> + <argument index="1" name="allow_objects" type="bool" default="false"> + </argument> + <description> + Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). + </description> + </method> + <method name="cartesian2polar"> + <return type="Vector2"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). + </description> + </method> + <method name="ceil"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Rounds [code]s[/code] upward, returning the smallest integral value that is not less than [code]s[/code]. + [codeblock] + i = ceil(1.45) # i is 2 + i = ceil(1.001) # i is 2 + [/codeblock] + </description> + </method> + <method name="char"> + <return type="String"> + </return> + <argument index="0" name="ascii" type="int"> + </argument> + <description> + Returns a character as a String of the given ASCII code. + [codeblock] + a = char(65) # a is "A" + a = char(65 + 32) # a is "a" + [/codeblock] + </description> + </method> + <method name="clamp"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="min" type="float"> + </argument> + <argument index="2" name="max" type="float"> + </argument> + <description> + Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code]. + [codeblock] + speed = 1000 + # a is 20 + a = clamp(speed, 1, 20) + + speed = -10 + # a is 1 + a = clamp(speed, 1, 20) + [/codeblock] + </description> + </method> + <method name="convert"> + <return type="Variant"> + </return> + <argument index="0" name="what" type="Variant"> + </argument> + <argument index="1" name="type" type="int"> + </argument> + <description> + Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the enum [code]TYPE_*[/code] in [@GlobalScope]. + [codeblock] + a = Vector2(1, 0) + # Prints 1 + print(a.length()) + a = convert(a, TYPE_STRING) + # Prints 6 as "(1, 0)" is 6 characters + print(a.length()) + [/codeblock] + </description> + </method> + <method name="cos"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the cosine of angle [code]s[/code] in radians. + [codeblock] + # Prints 1 then -1 + print(cos(PI * 2)) + print(cos(PI)) + [/codeblock] + </description> + </method> + <method name="cosh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic cosine of [code]s[/code] in radians. + [codeblock] + # Prints 1.543081 + print(cosh(1)) + [/codeblock] + </description> + </method> + <method name="db2linear"> + <return type="float"> + </return> + <argument index="0" name="db" type="float"> + </argument> + <description> + Converts from decibels to linear energy (audio). + </description> + </method> + <method name="decimals"> + <return type="int"> + </return> + <argument index="0" name="step" type="float"> + </argument> + <description> + Deprecated alias for [method step_decimals]. + </description> + </method> + <method name="dectime"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="amount" type="float"> + </argument> + <argument index="2" name="step" type="float"> + </argument> + <description> + Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code]. + [codeblock] + # a = 59 + a = dectime(60, 10, 0.1)) + [/codeblock] + </description> + </method> + <method name="deg2rad"> + <return type="float"> + </return> + <argument index="0" name="deg" type="float"> + </argument> + <description> + Returns degrees converted to radians. + [codeblock] + # r is 3.141593 + r = deg2rad(180) + [/codeblock] + </description> + </method> + <method name="dict2inst"> + <return type="Object"> + </return> + <argument index="0" name="dict" type="Dictionary"> + </argument> + <description> + Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing. + </description> + </method> + <method name="ease"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <argument index="1" name="curve" type="float"> + </argument> + <description> + Easing function, based on exponent. 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in. + </description> + </method> + <method name="exp"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it. + [b]e[/b] has an approximate value of 2.71828. + [codeblock] + a = exp(2) # Approximately 7.39 + [/codeblock] + </description> + </method> + <method name="floor"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Rounds [code]s[/code] to the closest smaller integer and returns it. + [codeblock] + # a is 2 + a = floor(2.99) + # a is -3 + a = floor(-2.99) + [/codeblock] + </description> + </method> + <method name="fmod"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the floating-point remainder of [code]x/y[/code]. + [codeblock] + # Remainder is 1.5 + var remainder = fmod(7, 5.5) + [/codeblock] + </description> + </method> + <method name="fposmod"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the floating-point remainder of [code]x/y[/code] that wraps equally in positive and negative. + [codeblock] + var i = -10 + while i < 0: + prints(i, fposmod(i, 10)) + i += 1 + [/codeblock] + Produces: + [codeblock] + -10 10 + -9 1 + -8 2 + -7 3 + -6 4 + -5 5 + -4 6 + -3 7 + -2 8 + -1 9 + [/codeblock] + </description> + </method> + <method name="funcref"> + <return type="FuncRef"> + </return> + <argument index="0" name="instance" type="Object"> + </argument> + <argument index="1" name="funcname" type="String"> + </argument> + <description> + Returns a reference to the specified function [code]funcname[/code] in the [code]instance[/code] node. As functions aren't first-class objects in GDscript, use [code]funcref[/code] to store a [FuncRef] in a variable and call it later. + [codeblock] + func foo(): + return("bar") + + a = funcref(self, "foo") + print(a.call_func()) # Prints bar + [/codeblock] + </description> + </method> + <method name="get_stack"> + <return type="Array"> + </return> + <description> + Returns an array of dictionaries representing the current call stack. + [codeblock] + func _ready(): + foo() + + func foo(): + bar() + + func bar(): + print(get_stack()) + [/codeblock] + would print + [codeblock] + [{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}] + [/codeblock] + </description> + </method> + <method name="hash"> + <return type="int"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Returns the integer hash of the variable passed. + [codeblock] + print(hash("a")) # Prints 177670 + [/codeblock] + </description> + </method> + <method name="inst2dict"> + <return type="Dictionary"> + </return> + <argument index="0" name="inst" type="Object"> + </argument> + <description> + Returns the passed instance converted to a dictionary (useful for serializing). + [codeblock] + var foo = "bar" + func _ready(): + var d = inst2dict(self) + print(d.keys()) + print(d.values()) + [/codeblock] + Prints out: + [codeblock] + [@subpath, @path, foo] + [, res://test.gd, bar] + [/codeblock] + </description> + </method> + <method name="instance_from_id"> + <return type="Object"> + </return> + <argument index="0" name="instance_id" type="int"> + </argument> + <description> + Returns the Object that corresponds to [code]instance_id[/code]. All Objects have a unique instance ID. + [codeblock] + var foo = "bar" + func _ready(): + var id = get_instance_id() + var inst = instance_from_id(id) + print(inst.foo) # Prints bar + [/codeblock] + </description> + </method> + <method name="inverse_lerp"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Returns a normalized value considering the given range. + [codeblock] + inverse_lerp(3, 5, 4) # Returns 0.5 + [/codeblock] + </description> + </method> + <method name="is_equal_approx"> + <return type="bool"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns True/False whether [code]a[/code] and [code]b[/code] are approximately equal to each other. + </description> + </method> + <method name="is_inf"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns whether [code]s[/code] is an infinity value (either positive infinity or negative infinity). + </description> + </method> + <method name="is_instance_valid"> + <return type="bool"> + </return> + <argument index="0" name="instance" type="Object"> + </argument> + <description> + Returns whether [code]instance[/code] is a valid object (e.g. has not been deleted from memory). + </description> + </method> + <method name="is_nan"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns whether [code]s[/code] is a NaN (Not-A-Number) value. + </description> + </method> + <method name="is_zero_approx"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns True/False whether [code]s[/code] is zero or almost zero. + </description> + </method> + <method name="len"> + <return type="int"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc. + [b]Note:[/b] Generates a fatal error if Variant can not provide a length. + [codeblock] + a = [1, 2, 3, 4] + len(a) # Returns 4 + [/codeblock] + </description> + </method> + <method name="lerp"> + <return type="Variant"> + </return> + <argument index="0" name="from" type="Variant"> + </argument> + <argument index="1" name="to" type="Variant"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Linearly interpolates between two values by a normalized value. + If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float]. + If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]linear_interpolate[/code] method). + [codeblock] + lerp(0, 4, 0.75) # Returns 3.0 + lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # Returns Vector2(2, 3.5) + [/codeblock] + </description> + </method> + <method name="linear2db"> + <return type="float"> + </return> + <argument index="0" name="nrg" type="float"> + </argument> + <description> + Converts from linear energy to decibels (audio). + </description> + </method> + <method name="load"> + <return type="Resource"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + Loads a resource from the filesystem located at [code]path[/code]. + [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing [b]Copy Path[/b]. + [codeblock] + # Load a scene called main located in the root of the project directory + var main = load("res://main.tscn") + [/codeblock] + </description> + </method> + <method name="log"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + [b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. + [codeblock] + log(10) # Returns 2.302585 + [/codeblock] + </description> + </method> + <method name="max"> + <return type="float"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns the maximum of two values. + [codeblock] + max(1, 2) # Returns 2 + max(-3.99, -4) # Returns -3.99 + [/codeblock] + </description> + </method> + <method name="min"> + <return type="float"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns the minimum of two values. + [codeblock] + min(1, 2) # Returns 1 + min(-3.99, -4) # Returns -4 + [/codeblock] + </description> + </method> + <method name="move_toward"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="delta" type="float"> + </argument> + <description> + Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value. + Use a negative [code]delta[/code] value to move away. + [codeblock] + move_toward(10, 5, 4) # Returns 6 + [/codeblock] + </description> + </method> + <method name="nearest_po2"> + <return type="int"> + </return> + <argument index="0" name="value" type="int"> + </argument> + <description> + Returns the nearest larger power of 2 for integer [code]value[/code]. + [codeblock] + nearest_po2(3) # Returns 4 + nearest_po2(4) # Returns 4 + nearest_po2(5) # Returns 8 + [/codeblock] + </description> + </method> + <method name="parse_json"> + <return type="Variant"> + </return> + <argument index="0" name="json" type="String"> + </argument> + <description> + Parse JSON text to a Variant (use [method typeof] to check if it is what you expect). + Be aware that the JSON specification does not define integer or float types, but only a number type. Therefore, parsing a JSON text will convert all numerical values to [float] types. + Note that JSON objects do not preserve key order like Godot dictionaries, thus you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements: + [codeblock] + p = parse_json('["a", "b", "c"]') + if typeof(p) == TYPE_ARRAY: + print(p[0]) # Prints a + else: + print("unexpected results") + [/codeblock] + </description> + </method> + <method name="polar2cartesian"> + <return type="Vector2"> + </return> + <argument index="0" name="r" type="float"> + </argument> + <argument index="1" name="th" type="float"> + </argument> + <description> + Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). + </description> + </method> + <method name="pow"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the result of [code]x[/code] raised to the power of [code]y[/code]. + [codeblock] + pow(2, 5) # Returns 32 + [/codeblock] + </description> + </method> + <method name="preload"> + <return type="Resource"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + Returns a resource from the filesystem that is loaded during script parsing. + [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". + [codeblock] + # Load a scene called main located in the root of the project directory + var main = preload("res://main.tscn") + [/codeblock] + </description> + </method> + <method name="print" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Converts one or more arguments to strings in the best way possible and prints them to the console. + [codeblock] + a = [1, 2, 3] + print("a", "b", a) # Prints ab[1, 2, 3] + [/codeblock] + </description> + </method> + <method name="print_debug" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Like [method print], but prints only when used in debug mode. + </description> + </method> + <method name="print_stack"> + <return type="void"> + </return> + <description> + Prints a stack track at code location, only works when running with debugger turned on. + Output in the console would look something like this: + [codeblock] + Frame 0 - res://test.gd:16 in function '_process' + [/codeblock] + </description> + </method> + <method name="printerr" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to strings in the best way possible to standard error line. + [codeblock] + printerr("prints to stderr") + [/codeblock] + </description> + </method> + <method name="printraw" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to strings in the best way possible to console. No newline is added at the end. + [codeblock] + printraw("A") + printraw("B") + # Prints AB + [/codeblock] + </description> + </method> + <method name="prints" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to the console with a space between each argument. + [codeblock] + prints("A", "B", "C") # Prints A B C + [/codeblock] + </description> + </method> + <method name="printt" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to the console with a tab between each argument. + [codeblock] + printt("A", "B", "C") # Prints A B C + [/codeblock] + </description> + </method> + <method name="push_error"> + <return type="void"> + </return> + <argument index="0" name="message" type="String"> + </argument> + <description> + Pushes an error message to Godot's built-in debugger and to the OS terminal. + [codeblock] + push_error("test error") # Prints "test error" to debugger and terminal as error call + [/codeblock] + </description> + </method> + <method name="push_warning"> + <return type="void"> + </return> + <argument index="0" name="message" type="String"> + </argument> + <description> + Pushes a warning message to Godot's built-in debugger and to the OS terminal. + [codeblock] + push_warning("test warning") # Prints "test warning" to debugger and terminal as warning call + [/codeblock] + </description> + </method> + <method name="rad2deg"> + <return type="float"> + </return> + <argument index="0" name="rad" type="float"> + </argument> + <description> + Converts from radians to degrees. + [codeblock] + rad2deg(0.523599) # Returns 30 + [/codeblock] + </description> + </method> + <method name="rand_range"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <description> + Random range, any floating point value between [code]from[/code] and [code]to[/code]. + [codeblock] + prints(rand_range(0, 1), rand_range(0, 1)) # Prints e.g. 0.135591 0.405263 + [/codeblock] + </description> + </method> + <method name="rand_seed"> + <return type="Array"> + </return> + <argument index="0" name="seed" type="int"> + </argument> + <description> + Random from seed: pass a [code]seed[/code], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits. + </description> + </method> + <method name="randf"> + <return type="float"> + </return> + <description> + Returns a random floating point value on the interval [code][0, 1][/code]. + [codeblock] + randf() # Returns e.g. 0.375671 + [/codeblock] + </description> + </method> + <method name="randi"> + <return type="int"> + </return> + <description> + Returns a random unsigned 32 bit integer. Use remainder to obtain a random value in the interval [code][0, N][/code] (where N is smaller than 2^32 -1). + [codeblock] + randi() # Returns random integer between 0 and 2^32 - 1 + randi() % 20 # Returns random integer between 0 and 19 + randi() % 100 # Returns random integer between 0 and 99 + randi() % 100 + 1 # Returns random integer between 1 and 100 + [/codeblock] + </description> + </method> + <method name="randomize"> + <return type="void"> + </return> + <description> + Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. + [codeblock] + func _ready(): + randomize() + [/codeblock] + </description> + </method> + <method name="range" qualifiers="vararg"> + <return type="Array"> + </return> + <description> + Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment). + [codeblock] + for i in range(4): + print(i) + for i in range(2, 5): + print(i) + for i in range(0, 6, 2): + print(i) + [/codeblock] + Output: + [codeblock] + 0 + 1 + 2 + 3 + + 2 + 3 + 4 + + 0 + 2 + 4 + [/codeblock] + </description> + </method> + <method name="range_lerp"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="istart" type="float"> + </argument> + <argument index="2" name="istop" type="float"> + </argument> + <argument index="3" name="ostart" type="float"> + </argument> + <argument index="4" name="ostop" type="float"> + </argument> + <description> + Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. + [codeblock] + range_lerp(75, 0, 100, -1, 1) # Returns 0.5 + [/codeblock] + </description> + </method> + <method name="round"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the integral value that is nearest to [code]s[/code], with halfway cases rounded away from zero. + [codeblock] + round(2.6) # Returns 3 + [/codeblock] + </description> + </method> + <method name="seed"> + <return type="void"> + </return> + <argument index="0" name="seed" type="int"> + </argument> + <description> + Sets seed for the random number generator. + [codeblock] + my_seed = "Godot Rocks" + seed(my_seed.hash()) + [/codeblock] + </description> + </method> + <method name="sign"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the sign of [code]s[/code]: -1 or 1. Returns 0 if [code]s[/code] is 0. + [codeblock] + sign(-6) # Returns -1 + sign(0) # Returns 0 + sign(6) # Returns 1 + [/codeblock] + </description> + </method> + <method name="sin"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the sine of angle [code]s[/code] in radians. + [codeblock] + sin(0.523599) # Returns 0.5 + [/codeblock] + </description> + </method> + <method name="sinh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic sine of [code]s[/code]. + [codeblock] + a = log(2.0) # Returns 0.693147 + sinh(a) # Returns 0.75 + [/codeblock] + </description> + </method> + <method name="smoothstep"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Returns a number smoothly interpolated between the [code]from[/code] and [code]to[/code], based on the [code]weight[/code]. Similar to [method lerp], but interpolates faster at the beginning and slower at the end. + [codeblock] + smoothstep(0, 2, 0.5) # Returns 0.15 + smoothstep(0, 2, 1.0) # Returns 0.5 + smoothstep(0, 2, 2.0) # Returns 1.0 + [/codeblock] + </description> + </method> + <method name="sqrt"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the square root of [code]s[/code]. + [codeblock] + sqrt(9) # Returns 3 + [/codeblock] + </description> + </method> + <method name="step_decimals"> + <return type="int"> + </return> + <argument index="0" name="step" type="float"> + </argument> + <description> + Returns the position of the first non-zero digit, after the decimal point. + [codeblock] + # n is 0 + n = step_decimals(5) + # n is 4 + n = step_decimals(1.0005) + # n is 9 + n = step_decimals(0.000000005) + [/codeblock] + </description> + </method> + <method name="stepify"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <argument index="1" name="step" type="float"> + </argument> + <description> + Snaps float value [code]s[/code] to a given [code]step[/code]. + </description> + </method> + <method name="str" qualifiers="vararg"> + <return type="String"> + </return> + <description> + Converts one or more arguments to string in the best way possible. + [codeblock] + var a = [10, 20, 30] + var b = str(a); + len(a) # Returns 3 + len(b) # Returns 12 + [/codeblock] + </description> + </method> + <method name="str2var"> + <return type="Variant"> + </return> + <argument index="0" name="string" type="String"> + </argument> + <description> + Converts a formatted string that was returned by [method var2str] to the original value. + [codeblock] + a = '{ "a": 1, "b": 2 }' + b = str2var(a) + print(b["a"]) # Prints 1 + [/codeblock] + </description> + </method> + <method name="tan"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the tangent of angle [code]s[/code] in radians. + [codeblock] + tan(deg2rad(45)) # Returns 1 + [/codeblock] + </description> + </method> + <method name="tanh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic tangent of [code]s[/code]. + [codeblock] + a = log(2.0) # Returns 0.693147 + tanh(a) # Returns 0.6 + [/codeblock] + </description> + </method> + <method name="to_json"> + <return type="String"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Converts a Variant [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network. + [codeblock] + a = { "a": 1, "b": 2 } + b = to_json(a) + print(b) # {"a":1, "b":2} + [/codeblock] + </description> + </method> + <method name="type_exists"> + <return type="bool"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <description> + Returns whether the given class exists in [ClassDB]. + [codeblock] + type_exists("Sprite") # Returns true + type_exists("Variant") # Returns false + [/codeblock] + </description> + </method> + <method name="typeof"> + <return type="int"> + </return> + <argument index="0" name="what" type="Variant"> + </argument> + <description> + Returns the internal type of the given Variant object, using the [code]TYPE_*[/code] enum in [@GlobalScope]. + [codeblock] + p = parse_json('["a", "b", "c"]') + if typeof(p) == TYPE_ARRAY: + print(p[0]) # Prints a + else: + print("unexpected results") + [/codeblock] + </description> + </method> + <method name="validate_json"> + <return type="String"> + </return> + <argument index="0" name="json" type="String"> + </argument> + <description> + Checks that [code]json[/code] is valid JSON data. Returns empty string if valid. Returns error message if not valid. + [codeblock] + j = to_json([1, 2, 3]) + v = validate_json(j) + if not v: + print("valid") + else: + prints("invalid", v) + [/codeblock] + </description> + </method> + <method name="var2bytes"> + <return type="PoolByteArray"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <argument index="1" name="full_objects" type="bool" default="false"> + </argument> + <description> + Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). + </description> + </method> + <method name="var2str"> + <return type="String"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Converts a Variant [code]var[/code] to a formatted string that can later be parsed using [method str2var]. + [codeblock] + a = { "a": 1, "b": 2 } + print(var2str(a)) + [/codeblock] + prints + [codeblock] + { + "a": 1, + "b": 2 + } + [/codeblock] + </description> + </method> + <method name="weakref"> + <return type="WeakRef"> + </return> + <argument index="0" name="obj" type="Object"> + </argument> + <description> + Returns a weak reference to an object. + A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it. + </description> + </method> + <method name="wrapf"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="min" type="float"> + </argument> + <argument index="2" name="max" type="float"> + </argument> + <description> + Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code]. + Usable for creating loop-alike behavior or infinite surfaces. + [codeblock] + # a is 0.5 + a = wrapf(10.5, 0.0, 10.0) + [/codeblock] + [codeblock] + # a is 9.5 + a = wrapf(-0.5, 0.0, 10.0) + [/codeblock] + [codeblock] + # Infinite loop between 0.0 and 0.99 + f = wrapf(f + 0.1, 0.0, 1.0) + [/codeblock] + [codeblock] + # Infinite rotation (in radians) + angle = wrapf(angle + 0.1, 0.0, TAU) + [/codeblock] + [b]Note:[/b] If you just want to wrap between 0.0 and [code]n[/code] (where [code]n[/code] is a positive floating-point value), it is better for performance to use the [method fmod] method like [code]fmod(number, n)[/code]. + [code]wrapf[/code] is more flexible than using the [method fmod] approach by giving the user a simple control over the minimum value. It also fully supports negative numbers, e.g. + [codeblock] + # Infinite rotation (in radians) + angle = wrapf(angle + 0.1, -PI, PI) + [/codeblock] + </description> + </method> + <method name="wrapi"> + <return type="int"> + </return> + <argument index="0" name="value" type="int"> + </argument> + <argument index="1" name="min" type="int"> + </argument> + <argument index="2" name="max" type="int"> + </argument> + <description> + Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code]. + Usable for creating loop-alike behavior or infinite surfaces. + [codeblock] + # a is 0 + a = wrapi(10, 0, 10) + [/codeblock] + [codeblock] + # a is 9 + a = wrapi(-1, 0, 10) + [/codeblock] + [codeblock] + # Infinite loop between 0 and 9 + frame = wrapi(frame + 1, 0, 10) + [/codeblock] + [b]Note:[/b] If you just want to wrap between 0 and [code]n[/code] (where [code]n[/code] is a positive integer value), it is better for performance to use the modulo operator like [code]number % n[/code]. + [code]wrapi[/code] is more flexible than using the modulo approach by giving the user a simple control over the minimum value. It also fully supports negative numbers, e.g. + [codeblock] + # result is -2 + var result = wrapi(-6, -5, -1) + [/codeblock] + </description> + </method> + <method name="yield"> + <return type="GDScriptFunctionState"> + </return> + <argument index="0" name="object" type="Object" default="null"> + </argument> + <argument index="1" name="signal" type="String" default=""""> + </argument> + <description> + Stops the function execution and returns the current suspended state to the calling function. + From the caller, call [method GDScriptFunctionState.resume] on the state to resume execution. This invalidates the state. Within the resumed function, [code]yield()[/code] returns whatever was passed to the [code]resume()[/code] function call. + If passed an object and a signal, the execution is resumed when the object emits the given signal. In this case, [code]yield()[/code] returns the argument passed to [code]emit_signal()[/code] if the signal takes only one argument, or an array containing all the arguments passed to [code]emit_signal()[/code] if the signal takes multiple arguments. + </description> + </method> + </methods> + <constants> + <constant name="PI" value="3.141593"> + Constant that represents how many times the diameter of a circle fits around its perimeter. + </constant> + <constant name="TAU" value="6.283185"> + The circle constant, the circumference of the unit circle. + </constant> + <constant name="INF" value="inf"> + A positive infinity. (For negative infinity, use -INF). + </constant> + <constant name="NAN" value="nan"> + Macro constant that expands to an expression of type float that represents a NaN. + The NaN values are used to identify undefined or non-representable values for floating-point elements, such as the square root of negative numbers or the result of 0/0. + </constant> + </constants> +</class> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 4cefdbd7cb..d606a41fab 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" category="Core" version="3.1"> +<class name="GDScript" inherits="Script" category="Core" version="3.2"> <brief_description> A script implemented in the GDScript programming language. </brief_description> @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_as_byte_code" qualifiers="const"> <return type="PoolByteArray"> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index c205cedef5..690953108f 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.1"> +<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.2"> <brief_description> State of a function call after yielding. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_valid" qualifiers="const"> <return type="bool"> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index 90935b5c22..70583d47a7 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.1"> +<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new"> <return type="Variant"> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index e4aee842ba..963b40529d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,6 +56,10 @@ static bool _is_hex_symbol(CharType c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } +static bool _is_bin_symbol(CharType c) { + return (c == '0' || c == '1'); +} + Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) { Map<int, TextEdit::HighlighterInfo> color_map; @@ -76,6 +80,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ bool in_member_variable = false; bool in_node_path = false; bool is_hex_notation = false; + bool is_bin_notation = false; bool expect_type = false; Color keyword_color; Color color; @@ -118,14 +123,26 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ is_hex_notation = false; } + // disallow anything not a 0 or 1 + if (is_bin_notation && (_is_bin_symbol(str[j]))) { + is_number = true; + } else if (is_bin_notation) { + is_bin_notation = false; + is_number = false; + } else { + is_bin_notation = false; + } + // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) { + if ((str[j] == '.' || str[j] == 'x' || str[j] == 'b' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) { is_number = true; is_symbol = false; is_char = false; if (str[j] == 'x' && str[j - 1] == '0') { is_hex_notation = true; + } else if (str[j] == 'b' && str[j - 1] == '0') { + is_bin_notation = true; } } @@ -330,7 +347,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ return color_map; } -String GDScriptSyntaxHighlighter::get_name() { +String GDScriptSyntaxHighlighter::get_name() const { return "GDScript"; } @@ -353,8 +370,8 @@ void GDScriptSyntaxHighlighter::_update_cache() { bool default_theme = text_editor_color_theme == "Default"; bool dark_theme = settings->is_dark_theme(); - function_definition_color = Color::html(default_theme ? "#01e1ff" : dark_theme ? "#01e1ff" : "#00a5ba"); - node_path_color = Color::html(default_theme ? "#64c15a" : dark_theme ? "64c15a" : "#518b4b"); + function_definition_color = default_theme ? Color(0.0, 0.88, 1.0) : dark_theme ? Color(0.0, 0.88, 1.0) : Color(0.0, 0.65, 0.73); + node_path_color = default_theme ? Color(0.39, 0.76, 0.35) : dark_theme ? Color(0.39, 0.76, 0.35) : Color(0.32, 0.55, 0.29); EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color); EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color); diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index b8cb4a65e9..9ba2c80552 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ public: virtual void _update_cache(); virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line); - virtual String get_name(); + virtual String get_name() const; virtual List<String> get_supported_languages(); }; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 538249c8e2..bc28f7009e 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,7 @@ #include "gdscript.h" +#include "core/core_string_names.h" #include "core/engine.h" #include "core/global_constants.h" #include "core/io/file_access_encrypted.h" @@ -68,7 +69,7 @@ Variant GDScriptNativeClass::_new() { Object *o = instance(); if (!o) { ERR_EXPLAIN("Class type: '" + String(name) + "' is not instantiable."); - ERR_FAIL_COND_V(!o, Variant()); + ERR_FAIL_V(Variant()); } Reference *ref = Object::cast_to<Reference>(o); @@ -152,12 +153,13 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Variant::CallErro } ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant()); - if (_baseptr->native.ptr()) { owner = _baseptr->native->instance(); } else { owner = memnew(Reference); //by default, no base means use reference } + ERR_EXPLAIN("Can't inherit from a virtual class"); + ERR_FAIL_COND_V(!owner, Variant()); Reference *r = Object::cast_to<Reference>(owner); if (r) { @@ -223,16 +225,21 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { - for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) { - GDScriptFunction *func = E->get(); - MethodInfo mi; - mi.name = E->key(); - for (int i = 0; i < func->get_argument_count(); i++) { - mi.arguments.push_back(func->get_argument_type(i)); + const GDScript *current = this; + while (current) { + for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) { + GDScriptFunction *func = E->get(); + MethodInfo mi; + mi.name = E->key(); + for (int i = 0; i < func->get_argument_count(); i++) { + mi.arguments.push_back(func->get_argument_type(i)); + } + + mi.return_val = func->get_return_type(); + p_list->push_back(mi); } - mi.return_val = func->get_return_type(); - p_list->push_back(mi); + current = current->_base; } } @@ -325,7 +332,7 @@ ScriptInstance *GDScript::instance_create(Object *p_this) { } Variant::CallError unchecked_error; - return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error); + return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this) != NULL, unchecked_error); } PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) { @@ -475,20 +482,15 @@ bool GDScript::_update_exports() { _signals[c->_signals[i].name] = c->_signals[i].arguments; } } else { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } - return false; - } - } else { - if (!valid) { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } + placeholder_fallback_enabled = true; return false; } + } else if (placeholder_fallback_enabled) { + return false; } + placeholder_fallback_enabled = false; + if (base_cache.is_valid()) { if (base_cache->_update_exports()) { changed = true; @@ -503,7 +505,6 @@ bool GDScript::_update_exports() { _update_exports_values(values, propnames); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(false); E->get()->update(propnames, values); } } @@ -597,7 +598,7 @@ Error GDScript::reload(bool p_keep_state) { return err; } } -#if DEBUG_ENABLED +#ifdef DEBUG_ENABLED for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { const GDScriptWarning &warning = E->get(); if (ScriptDebugger::get_singleton()) { @@ -648,7 +649,8 @@ Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p if (E) { if (!E->get()->is_static()) { - WARN_PRINT(String("Can't call non-static function: '" + String(p_method) + "' in script.").utf8().get_data()); + ERR_EXPLAIN("Can't call non-static function: '" + String(p_method) + "' in script."); + ERR_FAIL_V(Variant()); } return E->get()->call(NULL, p_args, p_argcount, r_error); @@ -907,6 +909,7 @@ GDScript::GDScript() : tool = false; #ifdef TOOLS_ENABLED source_changed_cache = false; + placeholder_fallback_enabled = false; #endif #ifdef DEBUG_ENABLED @@ -1062,7 +1065,7 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool } void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { - // exported members, not doen yet! + // exported members, not done yet! const GDScript *sptr = script.ptr(); List<PropertyInfo> props; @@ -1107,12 +1110,12 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const //instance a fake script for editing the values Vector<_GDScriptMemberSort> msort; - for (Map<StringName, PropertyInfo>::Element *E = sptr->member_info.front(); E; E = E->next()) { + for (Map<StringName, PropertyInfo>::Element *F = sptr->member_info.front(); F; F = F->next()) { _GDScriptMemberSort ms; - ERR_CONTINUE(!sptr->member_indices.has(E->key())); - ms.index = sptr->member_indices[E->key()].index; - ms.name = E->key(); + ERR_CONTINUE(!sptr->member_indices.has(F->key())); + ms.index = sptr->member_indices[F->key()].index; + ms.name = F->key(); msort.push_back(ms); } @@ -1232,6 +1235,27 @@ void GDScriptInstance::notification(int p_notification) { } } +String GDScriptInstance::to_string(bool *r_valid) { + if (has_method(CoreStringNames::get_singleton()->_to_string)) { + Variant::CallError ce; + Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce); + if (ce.error == Variant::CallError::CALL_OK) { + if (ret.get_type() != Variant::STRING) { + if (r_valid) + *r_valid = false; + ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String."); + ERR_FAIL_V(String()); + } + if (r_valid) + *r_valid = true; + return ret.operator String(); + } + } + if (r_valid) + *r_valid = false; + return String(); +} + Ref<Script> GDScriptInstance::get_script() const { return script; @@ -1675,6 +1699,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so //restore state if saved for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + List<Pair<StringName, Variant> > &saved_state = F->get(); + Object *obj = ObjectDB::get_instance(F->key()); if (!obj) continue; @@ -1684,16 +1710,26 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->set_script(RefPtr()); } obj->set_script(scr.get_ref_ptr()); - if (!obj->get_script_instance()) { + + ScriptInstance *script_instance = obj->get_script_instance(); + + if (!script_instance) { //failed, save reload state for next time if not saved if (!scr->pending_reload_state.has(obj->get_instance_id())) { - scr->pending_reload_state[obj->get_instance_id()] = F->get(); + scr->pending_reload_state[obj->get_instance_id()] = saved_state; } continue; } - for (List<Pair<StringName, Variant> >::Element *G = F->get().front(); G; G = G->next()) { - obj->get_script_instance()->set(G->get().first, G->get().second); + if (script_instance->is_placeholder() && scr->is_placeholder_fallback_enabled()) { + PlaceHolderScriptInstance *placeholder = static_cast<PlaceHolderScriptInstance *>(script_instance); + for (List<Pair<StringName, Variant> >::Element *G = saved_state.front(); G; G = G->next()) { + placeholder->property_set_fallback(G->get().first, G->get().second); + } + } else { + for (List<Pair<StringName, Variant> >::Element *G = saved_state.front(); G; G = G->next()) { + script_instance->set(G->get().first, G->get().second); + } } scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state @@ -1821,73 +1857,92 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b PoolVector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return String(); } - int len = f->get_len(); - sourcef.resize(len + 1); - PoolVector<uint8_t>::Write w = sourcef.write(); - int r = f->get_buffer(w.ptr(), len); - f->close(); - memdelete(f); - ERR_FAIL_COND_V(r != len, String()); - w[len] = 0; - - String s; - if (s.parse_utf8((const char *)w.ptr())) { - return String(); - } + String source = f->get_as_utf8_string(); GDScriptParser parser; - - parser.parse(s, p_path.get_base_dir(), true, p_path); + parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true); if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); + if (r_icon_path) { + if (c->icon_path.empty() || c->icon_path.is_abs_path()) + *r_icon_path = c->icon_path; + else if (c->icon_path.is_rel_path()) + *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); + } if (r_base_type) { - GDScriptParser::DataType base_type; - if (c->base_type.has_type) { - base_type = c->base_type; - while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) { - switch (base_type.kind) { - case GDScriptParser::DataType::CLASS: { - base_type = base_type.class_type->base_type; - } break; - case GDScriptParser::DataType::GDSCRIPT: { - Ref<GDScript> gds = base_type.script_type; - if (gds.is_valid()) { - base_type.kind = GDScriptParser::DataType::NATIVE; - base_type.native_type = gds->get_instance_base_type(); - } else { - base_type = GDScriptParser::DataType(); + + const GDScriptParser::ClassNode *subclass = c; + String path = p_path; + GDScriptParser subparser; + while (subclass) { + if (subclass->extends_used) { + if (subclass->extends_file) { + if (subclass->extends_class.size() == 0) { + get_global_class_name(subclass->extends_file, r_base_type); + subclass = NULL; + break; + } else { + Vector<StringName> extend_classes = subclass->extends_class; + + FileAccessRef subfile = FileAccess::open(subclass->extends_file, FileAccess::READ); + if (!subfile) { + break; } - } break; - default: { - base_type = GDScriptParser::DataType(); - } break; + String subsource = subfile->get_as_utf8_string(); + + if (subsource.empty()) { + break; + } + String subpath = subclass->extends_file; + if (subpath.is_rel_path()) { + subpath = path.get_base_dir().plus_file(subpath).simplify_path(); + } + + if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, NULL, true)) { + break; + } + path = subpath; + if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) { + break; + } + subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree()); + + while (extend_classes.size() > 0) { + bool found = false; + for (int i = 0; i < subclass->subclasses.size(); i++) { + const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i]; + if (inner_class->name == extend_classes[0]) { + extend_classes.remove(0); + found = true; + subclass = inner_class; + break; + } + } + if (!found) { + subclass = NULL; + break; + } + } + } + } else if (subclass->extends_class.size() == 1) { + *r_base_type = subclass->extends_class[0]; + subclass = NULL; + } else { + break; } - } - } - if (base_type.has_type) { - *r_base_type = base_type.native_type; - } else { - // Fallback - if (c->extends_used && c->extends_class.size() == 1) { - *r_base_type = c->extends_class[0]; - } else if (!c->extends_used) { + } else { *r_base_type = "Reference"; + subclass = NULL; } } } - if (r_icon_path) { - if (c->icon_path.empty() || c->icon_path.is_abs_path()) - *r_icon_path = c->icon_path; - else if (c->icon_path.is_rel_path()) - *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); - } return c->name; } @@ -1912,6 +1967,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return "The local variable '" + symbols[0] + "' is declared but never used in the block."; } break; + case SHADOWED_VARIABLE: { + CHECK_SYMBOLS(2); + return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + "."; + } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); return "The class variable '" + symbols[0] + "' is declared but never used in the script."; @@ -1932,7 +1991,7 @@ String GDScriptWarning::get_message() const { return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; } break; case NARROWING_CONVERSION: { - return "Narrowing coversion (float is converted to int and lose precision)."; + return "Narrowing conversion (float is converted to int and loses precision)."; } break; case FUNCTION_MAY_YIELD: { CHECK_SYMBOLS(1); @@ -2015,6 +2074,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNASSIGNED_VARIABLE", "UNASSIGNED_VARIABLE_OP_ASSIGN", "UNUSED_VARIABLE", + "SHADOWED_VARIABLE", "UNUSED_CLASS_VARIABLE", "UNUSED_ARGUMENT", "UNREACHABLE_CODE", @@ -2169,6 +2229,26 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con return ""; } +void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { + + FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND(!file); + + String source = file->get_as_utf8_string(); + if (source.empty()) { + return; + } + + GDScriptParser parser; + if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true)) { + return; + } + + for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) { + p_dependencies->push_back(E->get()); + } +} + Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { Ref<GDScript> sqscr = p_resource; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 752d660ffb..a5ad23c75d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -97,6 +97,7 @@ class GDScript : public Script { Ref<GDScript> base_cache; Set<ObjectID> inheriters_cache; bool source_changed_cache; + bool placeholder_fallback_enabled; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); #endif @@ -209,6 +210,10 @@ public: virtual void get_constants(Map<StringName, Variant> *p_constants); virtual void get_members(Set<StringName> *p_members); +#ifdef TOOLS_ENABLED + virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; } +#endif + GDScript(); ~GDScript(); }; @@ -246,6 +251,7 @@ public: Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; } virtual void notification(int p_notification); + String to_string(bool *r_valid); virtual Ref<Script> get_script() const; @@ -268,6 +274,7 @@ struct GDScriptWarning { UNASSIGNED_VARIABLE, // Variable used but never assigned UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) UNUSED_VARIABLE, // Local variable is declared but never used + SHADOWED_VARIABLE, // Variable name shadowed by other variable UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file UNUSED_ARGUMENT, // Function argument is never used UNREACHABLE_CODE, // Code after a return statement @@ -401,9 +408,10 @@ public: csi.resize(_debug_call_stack_pos); for (int i = 0; i < _debug_call_stack_pos; i++) { csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0; - if (_call_stack[i].function) + if (_call_stack[i].function) { csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name(); - csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); + csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); + } } return csi; } @@ -439,6 +447,7 @@ public: virtual void get_reserved_words(List<String> *p_words) const; virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; + virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); @@ -449,9 +458,9 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint); #ifdef TOOLS_ENABLED - virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result); + virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result); #endif virtual String _get_indentation() const; virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; @@ -505,6 +514,7 @@ public: virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); }; class ResourceFormatSaverGDScript : public ResourceFormatSaver { diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index caa7fbfeca..4c976bd2e0 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -139,17 +139,32 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { - result.kind = GDScriptDataType::GDSCRIPT; - if (!p_datatype.class_type->owner) { - result.script_type = Ref<GDScript>(main_script); - } else { - result.script_type = class_map[p_datatype.class_type->name]; + // Locate class by constructing the path to it and following that path + GDScriptParser::ClassNode *class_type = p_datatype.class_type; + List<StringName> names; + while (class_type->owner) { + names.push_back(class_type->name); + class_type = class_type->owner; } - result.native_type = result.script_type->get_instance_base_type(); + + Ref<GDScript> script = Ref<GDScript>(main_script); + while (names.back()) { + if (!script->subclasses.has(names.back()->get())) { + ERR_PRINT("Parser bug: Cannot locate datatype class."); + result.has_type = false; + return GDScriptDataType(); + } + script = script->subclasses[names.back()->get()]; + names.pop_back(); + } + + result.kind = GDScriptDataType::GDSCRIPT; + result.script_type = script; + result.native_type = script->get_instance_base_type(); } break; default: { ERR_PRINT("Parser bug: converting unresolved type."); - result.has_type = false; + return GDScriptDataType(); } } @@ -460,52 +475,30 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.alloc_stack(slevel); } - switch (cn->cast_type.kind) { - case GDScriptParser::DataType::BUILTIN: { + GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type); + + switch (cast_type.kind) { + case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN); - codegen.opcodes.push_back(cn->cast_type.builtin_type); + codegen.opcodes.push_back(cast_type.builtin_type); } break; - case GDScriptParser::DataType::NATIVE: { + case GDScriptDataType::NATIVE: { int class_idx; - if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) { + if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) { - class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type]; + class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type]; class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } else { - _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn); + _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn); return -1; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator codegen.opcodes.push_back(class_idx); // variable type } break; - case GDScriptParser::DataType::CLASS: { + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { - Variant script; - int idx = -1; - if (!cn->cast_type.class_type->owner) { - script = codegen.script; - } else { - StringName name = cn->cast_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { - idx = codegen.get_name_map_pos(name); - idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; - } else { - script = class_map[name]; - } - } - - if (idx < 0) { - idx = codegen.get_constant_pos(script); - idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) - } - - codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator - codegen.opcodes.push_back(idx); // variable type - } break; - case GDScriptParser::DataType::SCRIPT: - case GDScriptParser::DataType::GDSCRIPT: { - - Variant script = cn->cast_type.script_type; + Variant script = cast_type.script_type; int idx = codegen.get_constant_pos(script); idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) @@ -1080,7 +1073,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int set_index; bool named = false; - if (static_cast<const GDScriptParser::OperatorNode *>(op)->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) { + if (op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) { set_index = codegen.get_name_map_pos(static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name); named = true; @@ -1149,18 +1142,18 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: if (src_address_b < 0) return -1; - GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype(); + GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype()); if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) { // Typed assignment switch (assign_type.kind) { - case GDScriptParser::DataType::BUILTIN: { + case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator codegen.opcodes.push_back(assign_type.builtin_type); // variable type codegen.opcodes.push_back(dst_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 } break; - case GDScriptParser::DataType::NATIVE: { + case GDScriptDataType::NATIVE: { int class_idx; if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) { @@ -1175,34 +1168,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(dst_address_a); // argument 1 codegen.opcodes.push_back(src_address_b); // argument 2 } break; - case GDScriptParser::DataType::CLASS: { - - Variant script; - int idx = -1; - if (!assign_type.class_type->owner) { - script = codegen.script; - } else { - StringName name = assign_type.class_type->name; - if (class_map[name] == codegen.script->subclasses[name]) { - idx = codegen.get_name_map_pos(name); - idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS; - } else { - script = class_map[name]; - } - } - - if (idx < 0) { - idx = codegen.get_constant_pos(script); - idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) - } - - codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator - codegen.opcodes.push_back(idx); // variable type - codegen.opcodes.push_back(dst_address_a); // argument 1 - codegen.opcodes.push_back(src_address_b); // argument 2 - } break; - case GDScriptParser::DataType::SCRIPT: - case GDScriptParser::DataType::GDSCRIPT: { + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { Variant script = assign_type.script_type; int idx = codegen.get_constant_pos(script); @@ -1292,8 +1259,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ERR_FAIL_V(-1); //unreachable code } break; } - - ERR_FAIL_V(-1); //unreachable code } Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level, int p_break_addr, int p_continue_addr) { @@ -1360,15 +1325,15 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo // jump unconditionally // continue address // compile the condition - int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); - if (ret < 0) { + int ret2 = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); + if (ret2 < 0) { memdelete(id); memdelete(op); return ERR_PARSE_ERROR; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); codegen.opcodes.push_back(codegen.opcodes.size() + 3); int continue_addr = codegen.opcodes.size(); codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); @@ -1396,17 +1361,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo case GDScriptParser::ControlFlowNode::CF_IF: { -#ifdef DEBUG_ENABLED - codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE); - codegen.opcodes.push_back(cf->line); - codegen.current_line = cf->line; -#endif - int ret = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); int else_addr = codegen.opcodes.size(); codegen.opcodes.push_back(0); //temporary @@ -1421,9 +1381,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(0); codegen.opcodes.write[else_addr] = codegen.opcodes.size(); - Error err = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr); - if (err) - return err; + Error err2 = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr); + if (err2) + return err2; codegen.opcodes.write[end_addr] = codegen.opcodes.size(); } else { @@ -1444,14 +1404,14 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.push_stack_identifiers(); codegen.add_stack_identifier(static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0])->name, iter_stack_pos); - int ret = _parse_expression(codegen, cf->arguments[1], slevel, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, cf->arguments[1], slevel, false); + if (ret2 < 0) return ERR_COMPILATION_FAILED; //assign container codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); codegen.opcodes.push_back(container_pos); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); //begin loop codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN); @@ -1493,11 +1453,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(0); int continue_addr = codegen.opcodes.size(); - int ret = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); codegen.opcodes.push_back(break_addr); Error err = _parse_block(codegen, cf->body, p_stack_level, break_addr, continue_addr); if (err) @@ -1508,9 +1468,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size(); } break; - case GDScriptParser::ControlFlowNode::CF_SWITCH: { - - } break; case GDScriptParser::ControlFlowNode::CF_BREAK: { if (p_break_addr < 0) { @@ -1536,21 +1493,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo } break; case GDScriptParser::ControlFlowNode::CF_RETURN: { - int ret; + int ret2; if (cf->arguments.size()) { - ret = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); - if (ret < 0) + ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; } else { - ret = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS; + ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); } break; } @@ -1561,12 +1518,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s); - int ret = _parse_expression(codegen, as->condition, p_stack_level, false); - if (ret < 0) + int ret2 = _parse_expression(codegen, as->condition, p_stack_level, false); + if (ret2 < 0) return ERR_PARSE_ERROR; codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT); - codegen.opcodes.push_back(ret); + codegen.opcodes.push_back(ret2); #endif } break; case GDScriptParser::Node::TYPE_BREAKPOINT: { @@ -1593,8 +1550,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo } break; default: { //expression - int ret = _parse_expression(codegen, s, p_stack_level, true); - if (ret < 0) + int ret2 = _parse_expression(codegen, s, p_stack_level, true); + if (ret2 < 0) return ERR_PARSE_ERROR; } break; } @@ -1853,22 +1810,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser return OK; } -Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { + + parsing_classes.insert(p_script); if (p_class->owner && p_class->owner->owner) { // Owner is not root - StringName owner_name = p_class->owner->name; - if (!parsed_classes.has(owner_name)) { - if (parsing_classes.has(owner_name)) { - _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class); + if (!parsed_classes.has(p_script->_owner)) { + if (parsing_classes.has(p_script->_owner)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); return ERR_PARSE_ERROR; } - parsing_classes.insert(owner_name); - Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state); + Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state); if (err) { return err; } - parsing_classes.erase(owner_name); } } @@ -1886,47 +1842,39 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->_signals.clear(); p_script->initializer = NULL; - p_script->subclasses.clear(); - p_script->_owner = p_owner; p_script->tool = p_class->tool; p_script->name = p_class->name; Ref<GDScriptNativeClass> native; + GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); // Inheritance - switch (p_class->base_type.kind) { - case GDScriptParser::DataType::CLASS: { - StringName base_name = p_class->base_type.class_type->name; - // Make sure dependency is parsed first - if (!parsed_classes.has(base_name)) { - if (parsing_classes.has(base_name)) { - _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class); - return ERR_PARSE_ERROR; - } - parsing_classes.insert(base_name); - Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state); - if (err) { - return err; - } - parsing_classes.erase(base_name); - } - Ref<GDScript> base = class_map[base_name]; - p_script->base = base; - p_script->_base = p_script->base.ptr(); - p_script->member_indices = base->member_indices; - } break; - case GDScriptParser::DataType::GDSCRIPT: { - Ref<GDScript> base = p_class->base_type.script_type; - p_script->base = base; - p_script->_base = p_script->base.ptr(); - p_script->member_indices = base->member_indices; - } break; - case GDScriptParser::DataType::NATIVE: { - int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type]; + switch (base_type.kind) { + case GDScriptDataType::NATIVE: { + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; ERR_FAIL_COND_V(native.is_null(), ERR_BUG); p_script->native = native; } break; + case GDScriptDataType::GDSCRIPT: { + Ref<GDScript> base = base_type.script_type; + p_script->base = base; + p_script->_base = base.ptr(); + p_script->member_indices = base->member_indices; + + if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; + } + } + } + } break; default: { _set_error("Parser bug: invalid inheritance.", p_class); return ERR_BUG; @@ -2020,24 +1968,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->_signals[name] = p_class->_signals[i].arguments; } - if (p_class->owner) { - parsed_classes.insert(p_class->name); - if (parsing_classes.has(p_class->name)) { - parsing_classes.erase(p_class->name); - } - } + parsed_classes.insert(p_script); + parsing_classes.erase(p_script); //parse sub-classes for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - - Ref<GDScript> subclass = class_map[name]; + Ref<GDScript> &subclass = p_script->subclasses[name]; + GDScript *subclass_ptr = subclass.ptr(); // Subclass might still be parsing, just skip it - if (!parsed_classes.has(name) && !parsing_classes.has(name)) { - parsing_classes.insert(name); - Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state); + if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) { + Error err = _parse_class_level(subclass_ptr, p_class->subclasses[i], p_keep_state); if (err) return err; } @@ -2048,7 +1991,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner #endif p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants - p_script->subclasses.insert(name, subclass); } return OK; @@ -2119,8 +2061,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa instance->owner = E->get(); //needed for hot reloading - for (Map<StringName, GDScript::MemberInfo>::Element *E = p_script->member_indices.front(); E; E = E->next()) { - instance->member_indices_cache[E->key()] = E->get().index; + for (Map<StringName, GDScript::MemberInfo>::Element *F = p_script->member_indices.front(); F; F = F->next()) { + instance->member_indices_cache[F->key()] = F->get().index; } instance->owner->set_script_instance(instance); @@ -2147,9 +2089,9 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - Ref<GDScript> subclass = class_map[name]; + GDScript *subclass = p_script->subclasses[name].ptr(); - Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state); + Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state); if (err) { return err; } @@ -2159,7 +2101,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa return OK; } -void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { Map<StringName, Ref<GDScript> > old_subclasses; @@ -2167,6 +2109,8 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar old_subclasses = p_script->subclasses; } + p_script->subclasses.clear(); + for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; @@ -2178,10 +2122,10 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar subclass.instance(); } - subclass->_owner = const_cast<GDScript *>(p_script); - class_map.insert(name, subclass); + subclass->_owner = p_script; + p_script->subclasses.insert(name, subclass); - _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state); + _make_scripts(subclass.ptr(), p_class->subclasses[i], false); } } @@ -2200,7 +2144,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri // Create scripts for subclasses beforehand so they can be referenced _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); - Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); + p_script->_owner = NULL; + Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); if (err) return err; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 55f5e2b48e..2cf630ba72 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,9 +38,8 @@ class GDScriptCompiler { const GDScriptParser *parser; - Map<StringName, Ref<GDScript> > class_map; - Set<StringName> parsed_classes; - Set<StringName> parsing_classes; + Set<GDScript *> parsed_classes; + Set<GDScript *> parsing_classes; GDScript *main_script; struct CodeGen { @@ -149,9 +148,9 @@ class GDScriptCompiler { int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false); Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1); Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); - Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); int err_line; int err_column; StringName source; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 068e8d2d92..521b5ed538 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,12 +44,43 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const p_delimiters->push_back("#"); } + void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); p_delimiters->push_back("\"\"\" \"\"\""); } + +String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { + + String processed_template = p_template; + +#ifdef TOOLS_ENABLED + if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + processed_template = processed_template.replace("%INT_TYPE%", ": int"); + processed_template = processed_template.replace("%STRING_TYPE%", ": String"); + processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); + processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); + } else { + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); + } +#else + processed_template = processed_template.replace("%INT_TYPE%", ""); + processed_template = processed_template.replace("%STRING_TYPE%", ""); + processed_template = processed_template.replace("%FLOAT_TYPE%", ""); + processed_template = processed_template.replace("%VOID_RETURN%", ""); +#endif + + processed_template = processed_template.replace("%BASE%", p_base_class_name); + processed_template = processed_template.replace("%TS%", _get_indentation()); + + return processed_template; +} + Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String _template = "extends %BASE%\n" "\n" @@ -65,27 +96,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" "#%TS%pass\n"; -#ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - _template = _template.replace("%INT_TYPE%", ": int"); - _template = _template.replace("%STRING_TYPE%", ": String"); - _template = _template.replace("%FLOAT_TYPE%", ": float"); - _template = _template.replace("%VOID_RETURN%", " -> void"); - } else { - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); - } -#else - _template = _template.replace("%INT_TYPE%", ""); - _template = _template.replace("%STRING_TYPE%", ""); - _template = _template.replace("%FLOAT_TYPE%", ""); - _template = _template.replace("%VOID_RETURN%", ""); -#endif - - _template = _template.replace("%BASE%", p_base_class_name); - _template = _template.replace("%TS%", _get_indentation()); + _template = _get_processed_template(_template, p_base_class_name); Ref<GDScript> script; script.instance(); @@ -101,10 +112,8 @@ bool GDScriptLanguage::is_using_templates() { void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name); - src = src.replace("%TS%", _get_indentation()); - p_script->set_source_code(src); + String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); + p_script->set_source_code(_template); } bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { @@ -150,7 +159,11 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int & for (int i = 0; i < cl->subclasses.size(); i++) { for (int j = 0; j < cl->subclasses[i]->functions.size(); j++) { - funcs[cl->subclasses[i]->functions[j]->line] = String(cl->subclasses[i]->name) + "." + String(cl->subclasses[i]->functions[j]->name); + funcs[cl->subclasses[i]->functions[j]->line] = String(cl->subclasses[i]->name) + "." + cl->subclasses[i]->functions[j]->name; + } + for (int j = 0; j < cl->subclasses[i]->static_functions.size(); j++) { + + funcs[cl->subclasses[i]->static_functions[j]->line] = String(cl->subclasses[i]->name) + "." + cl->subclasses[i]->static_functions[j]->name; } } @@ -479,12 +492,15 @@ struct GDScriptCompletionContext { Object *base; String base_path; int line; + uint32_t depth; GDScriptCompletionContext() : _class(NULL), function(NULL), block(NULL), - base(NULL) {} + base(NULL), + line(0), + depth(0) {} }; struct GDScriptCompletionIdentifier { @@ -497,10 +513,14 @@ struct GDScriptCompletionIdentifier { assigned_expression(NULL) {} }; -static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) { +static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) { + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; for (int i = 0; i < p_dir->get_file_count(); i++) { - r_list.insert("\"" + p_dir->get_file_path(i) + "\""); + ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH); + option.insert_text = quote_style + option.display + quote_style; + r_list.insert(option.display, option); } for (int i = 0; i < p_dir->get_subdir_count(); i++) { @@ -615,6 +635,9 @@ static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_ ci.type.script_type = p_gdtype.script_type; switch (p_gdtype.kind) { + case GDScriptDataType::UNINITIALIZED: { + ERR_EXPLAIN("Uninitialized completion. Please report a bug."); + } break; case GDScriptDataType::BUILTIN: { ci.type.kind = GDScriptParser::DataType::BUILTIN; } break; @@ -631,12 +654,18 @@ static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_ return ci; } -static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); -static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); -static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); +static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); +static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); +static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); -static bool _guess_expression_type(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) { +static bool _guess_expression_type(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) { bool found = false; + + if (++p_context.depth > 100) { + print_error("Maximum _guess_expression_type depth limit reached. Please file a bugreport."); + return false; + } + switch (p_expression->type) { case GDScriptParser::Node::TYPE_CONSTANT: { const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression); @@ -773,12 +802,12 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c if (mb && mb->is_const()) { bool all_is_const = true; Vector<Variant> args; - GDScriptCompletionContext c = p_context; - c.line = op->line; + GDScriptCompletionContext c2 = p_context; + c2.line = op->line; for (int i = 2; all_is_const && i < op->arguments.size(); i++) { GDScriptCompletionIdentifier arg; - if (_guess_expression_type(c, op->arguments[i], arg)) { + if (_guess_expression_type(c2, op->arguments[i], arg)) { if (arg.type.has_type && arg.type.is_constant && arg.value.get_type() != Variant::OBJECT) { args.push_back(arg.value); } else { @@ -1049,7 +1078,8 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break; case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break; case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break; - default: {} + default: { + } } if (vop == Variant::OP_MAX) { @@ -1104,7 +1134,8 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c } break; } } break; - default: {} + default: { + } } // It may have found a null, but that's never useful @@ -1125,7 +1156,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c return found; } -static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { +static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { // Look in blocks first const GDScriptParser::BlockNode *blk = p_context.block; @@ -1181,7 +1212,11 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c c.line = op->line; c.block = blk; if (_guess_expression_type(p_context, op->arguments[1], r_type)) { - r_type.type.is_meta_type = false; + r_type.type.is_meta_type = false; // Right-hand of `is` will be a meta type, but the left-hand value is not + // Not an assignment, it shouldn't carry any value + r_type.value = Variant(); + r_type.assigned_expression = NULL; + return true; } } @@ -1266,9 +1301,9 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name == p_context.function->name) { MethodInfo &mi = E->get(); - for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) { - if (E->get().name == p_identifier) { - r_type = _type_from_property(E->get()); + for (List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next()) { + if (F->get().name == p_identifier) { + r_type = _type_from_property(F->get()); return true; } } @@ -1351,7 +1386,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c return false; } -static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { +static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; while (base_type.has_type) { @@ -1540,7 +1575,7 @@ static bool _find_last_return_in_block(const GDScriptCompletionContext &p_contex return false; } -static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) { +static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; @@ -1778,14 +1813,15 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio return arghint; } -static void _find_enumeration_candidates(const String p_enum_hint, Set<String> &r_result) { +static void _find_enumeration_candidates(const String p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) { if (p_enum_hint.find(".") == -1) { // Global constant StringName current_enum = p_enum_hint; for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { if (GlobalConstants::get_global_constant_enum(i) == current_enum) { - r_result.insert(GlobalConstants::get_global_constant_name(i)); + ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM); + r_result.insert(option.display, option); } } } else { @@ -1800,15 +1836,17 @@ static void _find_enumeration_candidates(const String p_enum_hint, Set<String> & ClassDB::get_enum_constants(class_name, enum_name, &enum_constants); for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) { String candidate = class_name + "." + E->get(); - r_result.insert(candidate); + ScriptCodeCompletionOption option(candidate, ScriptCodeCompletionOption::KIND_ENUM); + r_result.insert(option.display, option); } } } -static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Set<String> &r_result) { +static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) { for (Map<StringName, GDScriptParser::LocalVarNode *>::Element *E = p_context.block->variables.front(); E; E = E->next()) { if (E->get()->line < p_context.line) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_VARIABLE); + r_result.insert(option.display, option); } } if (p_context.block->parent_block) { @@ -1818,40 +1856,47 @@ static void _find_identifiers_in_block(const GDScriptCompletionContext &p_contex } } -static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result); +static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result); -static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Set<String> &r_result) { +static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result) { if (!p_parent_only) { if (!p_static && !p_only_functions) { for (int i = 0; i < p_context._class->variables.size(); i++) { - r_result.insert(p_context._class->variables[i].identifier); + ScriptCodeCompletionOption option(p_context._class->variables[i].identifier, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_context._class->constant_expressions.front(); E; E = E->next()) { - r_result.insert(E->key()); + ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } for (int i = 0; i < p_context._class->subclasses.size(); i++) { - r_result.insert(p_context._class->subclasses[i]->name); + ScriptCodeCompletionOption option(p_context._class->subclasses[i]->name, ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } for (int i = 0; i < p_context._class->static_functions.size(); i++) { + ScriptCodeCompletionOption option(p_context._class->static_functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (p_context._class->static_functions[i]->arguments.size()) { - r_result.insert(p_context._class->static_functions[i]->name.operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(p_context._class->static_functions[i]->name.operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } if (!p_static) { for (int i = 0; i < p_context._class->functions.size(); i++) { + ScriptCodeCompletionOption option(p_context._class->functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (p_context._class->functions[i]->arguments.size()) { - r_result.insert(p_context._class->functions[i]->name.operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(p_context._class->functions[i]->name.operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } } @@ -1869,12 +1914,14 @@ static void _find_identifiers_in_class(const GDScriptCompletionContext &p_contex _find_identifiers_in_base(c, base_type, p_only_functions, r_result); } -static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result) { +static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) { - r_result.insert("new("); + ScriptCodeCompletionOption option("new", ScriptCodeCompletionOption::KIND_FUNCTION); + option.insert_text += "("; + r_result.insert(option.display, option); } while (base_type.has_type) { @@ -1892,26 +1939,31 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (script.is_valid()) { if (!_static && !p_only_functions) { for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { - r_result.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) { if (!_static || E->get()->is_static()) { + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get()->get_argument_count()) { - r_result.insert(E->key().operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(E->key().operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } if (!p_only_functions) { for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } base_type = GDScriptParser::DataType(); @@ -1922,7 +1974,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context } else { base_type.has_type = script->get_instance_base_type() != StringName(); base_type.kind = GDScriptParser::DataType::NATIVE; - base_type.script_type = script->get_instance_base_type(); + base_type.native_type = script->get_instance_base_type(); } } else { return; @@ -1935,25 +1987,29 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<PropertyInfo> members; scr->get_script_property_list(&members); for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { Map<StringName, Variant> constants; scr->get_constants(&constants); for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } List<MethodInfo> methods; scr->get_script_method_list(&methods); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } Ref<Script> base_script = scr->get_base_script(); @@ -1980,7 +2036,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<String> constants; ClassDB::get_integer_constant_list(type, &constants); for (List<String>::Element *E = constants.front(); E; E = E->next()) { - r_result.insert(E->get()); + ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } if (!_static) { @@ -1993,7 +2050,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (E->get().name.find("/") != -1) { continue; } - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } } @@ -2006,11 +2064,13 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (E->get().name.begins_with("_")) { continue; } + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } @@ -2025,11 +2085,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!p_only_functions) { List<PropertyInfo> members; - tmp.get_property_list(&members); + p_base.value.get_property_list(&members); for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { if (String(E->get().name).find("/") == -1) { - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } } @@ -2037,11 +2098,13 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<MethodInfo> methods; tmp.get_method_list(&methods); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } return; @@ -2053,7 +2116,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context } } -static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Set<String> &r_result) { +static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { const GDScriptParser::BlockNode *block = p_context.block; @@ -2062,7 +2125,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p const GDScriptParser::FunctionNode *f = p_context.function; for (int i = 0; i < f->arguments.size(); i++) { - r_result.insert(f->arguments[i].operator String()); + ScriptCodeCompletionOption option(f->arguments[i].operator String(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_result.insert(option.display, option); } } @@ -2087,11 +2151,13 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { MethodInfo mi = GDScriptFunctions::get_info(GDScriptFunctions::Function(i)); + ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION); if (mi.arguments.size() || (mi.flags & METHOD_FLAG_VARARG)) { - r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "("); + option.insert_text += "("; } else { - r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } static const char *_type_names[Variant::VARIANT_MAX] = { @@ -2101,7 +2167,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p }; for (int i = 0; i < Variant::VARIANT_MAX; i++) { - r_result.insert(_type_names[i]); + ScriptCodeCompletionOption option(_type_names[i], ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } static const char *_keywords[] = { @@ -2115,7 +2182,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p const char **kw = _keywords; while (*kw) { - r_result.insert(*kw); + ScriptCodeCompletionOption option(*kw, ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_result.insert(option.display, option); kw++; } @@ -2129,7 +2197,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p } String path = ProjectSettings::get_singleton()->get(s); if (path.begins_with("*")) { - r_result.insert(s.get_slice("/", 1)); + ScriptCodeCompletionOption option(s.get_slice("/", 1), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } @@ -2137,19 +2206,22 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p List<StringName> named_scripts; ScriptServer::get_global_class_list(&named_scripts); for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) { - r_result.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } // Native classes for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } -static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Set<String> &r_result, String &r_arghint) { +static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptCodeCompletionOption> &r_result, String &r_arghint) { Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; - bool _static = false; + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; while (base_type.has_type) { switch (base_type.kind) { @@ -2160,18 +2232,18 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con return; } } - if (!_static) { - for (int i = 0; i < base_type.class_type->functions.size(); i++) { - if (base_type.class_type->functions[i]->name == p_method) { - r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx); - return; - } + for (int i = 0; i < base_type.class_type->functions.size(); i++) { + if (base_type.class_type->functions[i]->name == p_method) { + r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx); + return; } } if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - r_result.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\""); + ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2184,7 +2256,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; gds->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().name + "\""); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } Ref<GDScript> base_script = gds->get_base_script(); @@ -2221,8 +2295,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con if (obj) { List<String> options; obj->get_argument_options(p_method, p_argidx, &options); - for (List<String>::Element *E = options.front(); E; E = E->next()) { - r_result.insert(E->get()); + for (List<String>::Element *F = options.front(); F; F = F->next()) { + ScriptCodeCompletionOption option(F->get(), ScriptCodeCompletionOption::KIND_FUNCTION); + r_result.insert(option.display, option); } } } @@ -2243,7 +2318,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().name + "\""); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2258,7 +2335,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert("\"/root/" + name + "\""); + ScriptCodeCompletionOption option("/root/" + name, ScriptCodeCompletionOption::KIND_NODE_PATH); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2272,7 +2351,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert("\"" + name + "\""); + ScriptCodeCompletionOption option(name, ScriptCodeCompletionOption::KIND_CONSTANT); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2305,7 +2386,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con } } -static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) { +static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) { return; @@ -2421,30 +2504,38 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con _find_call_arguments(p_context, ci, function, p_argidx, _static, r_result, r_arghint); if (function == "connect" && p_argidx == 2) { - Set<String> methods; + Map<String, ScriptCodeCompletionOption> methods; _find_identifiers_in_base(p_context, connect_base, true, methods); - for (Set<String>::Element *E = methods.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().replace("(", "").replace(")", "") + "\""); + for (Map<String, ScriptCodeCompletionOption>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption &option = E->value(); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } r_forced = r_result.size() > 0; } -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; GDScriptParser parser; - parser.parse(p_code, p_base_path, false, "", true); + parser.parse(p_code, p_path.get_base_dir(), false, p_path, true); r_forced = false; - Set<String> options; + Map<String, ScriptCodeCompletionOption> options; GDScriptCompletionContext context; context._class = parser.get_completion_class(); context.block = parser.get_completion_block(); context.function = parser.get_completion_function(); - context.base = p_owner; - context.base_path = p_base_path; context.line = parser.get_completion_line(); + + if (!context._class || context._class->owner == NULL) { + context.base = p_owner; + context.base_path = p_path.get_base_dir(); + } + bool is_function = false; switch (parser.get_completion_type()) { @@ -2454,7 +2545,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base List<StringName> constants; Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants); for (List<StringName>::Element *E = constants.front(); E; E = E->next()) { - options.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + options.insert(option.display, option); } } break; case GDScriptParser::COMPLETION_PARENT_FUNCTION: { @@ -2462,7 +2554,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } break; case GDScriptParser::COMPLETION_FUNCTION: { is_function = true; - } // fallthrough + FALLTHROUGH; + } case GDScriptParser::COMPLETION_IDENTIFIER: { _find_identifiers(context, is_function, options); } break; @@ -2478,9 +2571,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base r_forced = true; String idopt = opt.unquote(); if (idopt.replace("/", "_").is_valid_identifier()) { - options.insert(idopt); + ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } else { - options.insert(opt); + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } } } @@ -2495,13 +2590,15 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base continue; } String name = s.get_slice("/", 1); - options.insert("\"/root/" + name + "\""); + ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } } } break; case GDScriptParser::COMPLETION_METHOD: { is_function = true; - } // fallthrough + FALLTHROUGH; + } case GDScriptParser::COMPLETION_INDEX: { const GDScriptParser::Node *node = parser.get_completion_node(); if (node->type != GDScriptParser::Node::TYPE_OPERATOR) { @@ -2617,7 +2714,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } method_hint += ":"; - options.insert(method_hint); + ScriptCodeCompletionOption option(method_hint, ScriptCodeCompletionOption::KIND_FUNCTION); + options.insert(option.display, option); } } break; case GDScriptParser::COMPLETION_YIELD: { @@ -2635,7 +2733,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - options.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\""); + ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + options.insert(option.display, option); } base_type = base_type.class_type->base_type; } break; @@ -2646,7 +2746,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base List<MethodInfo> signals; scr->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert("\"" + E->get().name + "\""); + ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL); + options.insert(option.display, option); } Ref<Script> base_script = scr->get_base_script(); if (base_script.is_valid()) { @@ -2673,7 +2774,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert("\"" + E->get().name + "\""); + ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL); + options.insert(option.display, option); } } break; default: { @@ -2710,18 +2812,21 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base c.line = E->value().expression->line; if (_guess_expression_type(c, E->value().expression, constant)) { if (constant.type.has_type && constant.type.is_meta_type) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } } for (int i = 0; i < clss->subclasses.size(); i++) { if (clss->subclasses[i]->name != StringName()) { - options.insert(clss->subclasses[i]->name.operator String()); + ScriptCodeCompletionOption option(clss->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } clss = clss->owner; for (int i = 0; i < Variant::VARIANT_MAX; i++) { - options.insert(Variant::get_type_name((Variant::Type)i)); + ScriptCodeCompletionOption option(Variant::get_type_name((Variant::Type)i), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } @@ -2735,18 +2840,21 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (Engine::get_singleton()->has_singleton(class_name)) { continue; } - options.insert(class_name); + ScriptCodeCompletionOption option(class_name, ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } // Named scripts List<StringName> named_scripts; ScriptServer::get_global_class_list(&named_scripts); for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) { - options.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } if (parser.get_completion_identifier_is_function()) { - options.insert("void"); + ScriptCodeCompletionOption option("void", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + options.insert(option.display, option); } r_forced = true; } break; @@ -2786,20 +2894,22 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (base_type.class_type) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = base_type.class_type->constant_expressions.front(); E; E = E->next()) { GDScriptCompletionIdentifier constant; - GDScriptCompletionContext c = context; - c._class = base_type.class_type; - c.function = NULL; - c.block = NULL; - c.line = E->value().expression->line; - if (_guess_expression_type(c, E->value().expression, constant)) { + GDScriptCompletionContext c2 = context; + c2._class = base_type.class_type; + c2.function = NULL; + c2.block = NULL; + c2.line = E->value().expression->line; + if (_guess_expression_type(c2, E->value().expression, constant)) { if (constant.type.has_type && constant.type.is_meta_type) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } } for (int i = 0; i < base_type.class_type->subclasses.size(); i++) { if (base_type.class_type->subclasses[i]->name != StringName()) { - options.insert(base_type.class_type->subclasses[i]->name.operator String()); + ScriptCodeCompletionOption option(base_type.class_type->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } @@ -2817,7 +2927,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { Ref<Script> const_scr = E->value(); if (const_scr.is_valid()) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } Ref<Script> base_script = scr->get_base_script(); @@ -2839,7 +2950,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } break; } - for (Set<String>::Element *E = options.front(); E; E = E->next()) { + for (Map<String, ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) { r_options->push_back(E->get()); } @@ -2848,7 +2959,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base #else -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { return OK; } @@ -2859,7 +2970,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base String GDScriptLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0); + bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", false); if (use_space_indentation) { int indent_size = EDITOR_DEF("text_editor/indent/size", 4); @@ -2977,8 +3088,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } } + base_type = base_type.class_type->base_type; } - base_type = base_type.class_type->base_type; } break; case GDScriptParser::DataType::SCRIPT: case GDScriptParser::DataType::GDSCRIPT: { @@ -3119,7 +3230,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co return ERR_CANT_RESOLVE; } -Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) { +Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { //before parsing, try the usual stuff if (ClassDB::class_exists(p_symbol)) { @@ -3161,7 +3272,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } GDScriptParser parser; - parser.parse(p_code, p_base_path, false, "", true); + parser.parse(p_code, p_path.get_base_dir(), false, p_path, true); if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) { return ERR_CANT_RESOLVE; @@ -3173,7 +3284,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol context.block = parser.get_completion_block(); context.line = parser.get_completion_line(); context.base = p_owner; - context.base_path = p_base_path; + context.base_path = p_path.get_base_dir(); if (context._class && context._class->extends_class.size() > 0) { bool success = false; @@ -3198,7 +3309,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol case GDScriptParser::COMPLETION_PARENT_FUNCTION: case GDScriptParser::COMPLETION_FUNCTION: { is_function = true; - } // fallthrough + FALLTHROUGH; + } case GDScriptParser::COMPLETION_IDENTIFIER: { if (!is_function) { @@ -3329,7 +3441,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } break; case GDScriptParser::COMPLETION_METHOD: { is_function = true; - } // fallthrough + FALLTHROUGH; + } case GDScriptParser::COMPLETION_INDEX: { const GDScriptParser::Node *node = parser.get_completion_node(); if (node->type != GDScriptParser::Node::TYPE_OPERATOR) { @@ -3351,7 +3464,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; - default: {} + default: { + } } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index cd6c21a629..42f349ffc0 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -133,35 +133,13 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta return NULL; } -String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const { - - String err_text; - - if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - int errorarg = p_err.argument; - err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + "."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { - err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { - err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { - err_text = "Invalid call. Nonexistent " + p_where + "."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { - err_text = "Attempt to call " + p_where + " on a null instance."; - } else { - err_text = "Bug, call error: #" + itos(p_err.error); - } - - return err_text; -} - #ifdef DEBUG_ENABLED -static String _get_var_type(const Variant *p_type) { +static String _get_var_type(const Variant *p_var) { String basestr; - if (p_type->get_type() == Variant::OBJECT) { - Object *bobj = *p_type; + if (p_var->get_type() == Variant::OBJECT) { + Object *bobj = *p_var; if (!bobj) { basestr = "null instance"; } else { @@ -176,12 +154,42 @@ static String _get_var_type(const Variant *p_type) { } } else { - basestr = Variant::get_type_name(p_type->get_type()); + basestr = Variant::get_type_name(p_var->get_type()); } return basestr; } -#endif +#endif // DEBUG_ENABLED + +String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const { + + String err_text; + + if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + int errorarg = p_err.argument; + // Handle the Object to Object case separately as we don't have further class details. +#ifdef DEBUG_ENABLED + if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) { + err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class."; + } else +#endif // DEBUG_ENABLED + { + err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + "."; + } + } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { + err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { + err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { + err_text = "Invalid call. Nonexistent " + p_where + "."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + err_text = "Attempt to call " + p_where + " on a null instance."; + } else { + err_text = "Bug, call error: #" + itos(p_err.error); + } + + return err_text; +} #if defined(__GNUC__) #define OPCODES_TABLE \ @@ -329,10 +337,15 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (!argument_types[i].is_type(*p_args[i], true)) { - r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); + if (argument_types[i].is_type(Variant(), true)) { + memnew_placement(&stack[i], Variant); + continue; + } else { + r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + return Variant(); + } } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err); @@ -1083,7 +1096,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (argc >= 1) { methodstr = String(*argptrs[0]) + " (via call)"; if (err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - err.argument -= 1; + err.argument += 1; } } } else if (methodstr == "free") { @@ -1543,14 +1556,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a //error // function, file, line, error, explanation String err_file; - if (p_instance) + if (p_instance && p_instance->script->is_valid() && p_instance->script->path != "") err_file = p_instance->script->path; else if (script) err_file = script->path; if (err_file == "") err_file = "<built-in>"; String err_func = name; - if (p_instance && p_instance->script->name != "") + if (p_instance && p_instance->script->is_valid() && p_instance->script->name != "") err_func = p_instance->script->name + "." + err_func; int err_line = line; if (err_text == "") { @@ -1578,15 +1591,26 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } - if (ScriptDebugger::get_singleton()) - GDScriptLanguage::get_singleton()->exit_function(); + bool yielded = retvalue.is_ref() && Object::cast_to<GDScriptFunctionState>(retvalue); + + // Check if this is the last time the function is resuming from yield + // Will be true if never yielded as well + // When it's the last resume it will postpone the exit from stack, + // so the debugger knows which function triggered the resume of the next function (if any) + if (!p_state || yielded) { + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); #endif - if (_stack_size) { - //free stack - for (int i = 0; i < _stack_size; i++) - stack[i].~Variant(); + if (_stack_size) { + //free stack + for (int i = 0; i < _stack_size; i++) + stack[i].~Variant(); + } + +#ifdef DEBUG_ENABLED } +#endif return retvalue; } @@ -1760,20 +1784,9 @@ GDScriptFunction::~GDScriptFunction() { Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { -#ifdef DEBUG_ENABLED - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); - ERR_FAIL_V(Variant()); -#else - return Variant(); -#endif - } - Variant arg; r_error.error = Variant::CallError::CALL_OK; - ERR_FAIL_COND_V(!function, Variant()); - if (p_argcount == 0) { r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = 1; @@ -1799,33 +1812,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar return Variant(); } - state.result = arg; - Variant ret = function->call(NULL, NULL, 0, r_error, &state); - - bool completed = true; - - // If the return value is a GDScriptFunctionState reference, - // then the function did yield again after resuming. - if (ret.is_ref()) { - GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); - if (gdfs && gdfs->function == function) { - completed = false; - gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this); - } - } - - function = NULL; //cleaned up; - state.result = Variant(); - - if (completed) { - if (first_state.is_valid()) { - first_state->emit_signal("completed", ret); - } else { - emit_signal("completed", ret); - } - } - - return ret; + return resume(arg); } bool GDScriptFunctionState::is_valid(bool p_extended_check) const { @@ -1847,7 +1834,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { #ifdef DEBUG_ENABLED - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); + ERR_EXPLAIN("Resumed function '" + String(function->get_name()) + "()' after yield, but class instance is gone. At script: " + state.script->get_path() + ":" + itos(state.line)); ERR_FAIL_V(Variant()); #else return Variant(); @@ -1879,6 +1866,17 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { } else { emit_signal("completed", ret); } + +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); + if (state.stack_size) { + //free stack + Variant *stack = (Variant *)state.stack.ptr(); + for (int i = 0; i < state.stack_size; i++) + stack[i].~Variant(); + } +#endif } return ret; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 5509edf24a..cefc28d77f 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "core/reference.h" #include "core/script_language.h" #include "core/self_list.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/variant.h" class GDScriptInstance; @@ -45,10 +45,11 @@ class GDScript; struct GDScriptDataType { bool has_type; enum { + UNINITIALIZED, BUILTIN, NATIVE, SCRIPT, - GDSCRIPT + GDSCRIPT, } kind; Variant::Type builtin_type; StringName native_type; @@ -58,6 +59,8 @@ struct GDScriptDataType { if (!has_type) return true; // Can't type check switch (kind) { + case UNINITIALIZED: + break; case BUILTIN: { Variant::Type var_type = p_variant.get_type(); bool valid = builtin_type == var_type; @@ -74,8 +77,14 @@ struct GDScriptDataType { return false; } Object *obj = p_variant.operator Object *(); - if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) { - return false; + if (obj) { + if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) { + // Try with underscore prefix + StringName underscore_native_type = "_" + native_type; + if (!ClassDB::is_parent_class(obj->get_class_name(), underscore_native_type)) { + return false; + } + } } return true; } break; @@ -107,6 +116,8 @@ struct GDScriptDataType { PropertyInfo info; if (has_type) { switch (kind) { + case UNINITIALIZED: + break; case BUILTIN: { info.type = builtin_type; } break; @@ -128,7 +139,9 @@ struct GDScriptDataType { } GDScriptDataType() : - has_type(false) {} + has_type(false), + kind(UNINITIALIZED), + builtin_type(Variant::NIL) {} }; class GDScriptFunction { diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 9ff33594ce..0736f3d010 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,12 +68,17 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "exp", "is_nan", "is_inf", + "is_equal_approx", + "is_zero_approx", "ease", "decimals", + "step_decimals", "stepify", "lerp", "inverse_lerp", "range_lerp", + "smoothstep", + "move_toward", "dectime", "randomize", "randi", @@ -315,6 +320,17 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(0); r_ret = Math::is_inf((double)*p_args[0]); } break; + case MATH_ISEQUALAPPROX: { + VALIDATE_ARG_COUNT(2); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]); + } break; + case MATH_ISZEROAPPROX: { + VALIDATE_ARG_COUNT(1); + VALIDATE_ARG_NUM(0); + r_ret = Math::is_zero_approx((real_t)*p_args[0]); + } break; case MATH_EASE: { VALIDATE_ARG_COUNT(2); VALIDATE_ARG_NUM(0); @@ -325,6 +341,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_COUNT(1); VALIDATE_ARG_NUM(0); r_ret = Math::step_decimals((double)*p_args[0]); + ERR_EXPLAIN("GDScript method 'decimals' is deprecated and has been renamed to 'step_decimals', please update your code accordingly."); + WARN_DEPRECATED; + } break; + case MATH_STEP_DECIMALS: { + VALIDATE_ARG_COUNT(1); + VALIDATE_ARG_NUM(0); + r_ret = Math::step_decimals((double)*p_args[0]); } break; case MATH_STEPIFY: { VALIDATE_ARG_COUNT(2); @@ -369,6 +392,20 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(4); r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]); } break; + case MATH_SMOOTHSTEP: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; + case MATH_MOVE_TOWARD: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_COUNT(3); VALIDATE_ARG_NUM(0); @@ -768,11 +805,30 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ (void)VariantParser::parse(&ss, r_ret, errs, line); } break; case VAR_TO_BYTES: { - VALIDATE_ARG_COUNT(1); + bool full_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + full_objects = *p_args[1]; + } PoolByteArray barr; int len; - Error err = encode_variant(*p_args[0], NULL, len); + Error err = encode_variant(*p_args[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -784,15 +840,35 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_args[0], w.ptr(), len); + encode_variant(*p_args[0], w.ptr(), len, full_objects); } r_ret = barr; } break; case BYTES_TO_VAR: { - VALIDATE_ARG_COUNT(1); + bool allow_objects = false; + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } else if (p_arg_count > 2) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = 2; + r_ret = Variant(); + } else if (p_arg_count == 2) { + if (p_args[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + r_ret = Variant(); + return; + } + allow_objects = *p_args[1]; + } + if (p_args[0]->get_type() != Variant::POOL_BYTE_ARRAY) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; + r_error.argument = 1; r_error.expected = Variant::POOL_BYTE_ARRAY; r_ret = Variant(); return; @@ -802,7 +878,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_ret = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -882,7 +958,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ int incr = *p_args[2]; if (incr == 0) { - r_ret = RTR("step argument is zero!"); + r_ret = RTR("Step argument is zero!"); r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; return; } @@ -1392,10 +1468,13 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_ISINF: case MATH_EASE: case MATH_DECIMALS: + case MATH_STEP_DECIMALS: case MATH_STEPIFY: case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1548,6 +1627,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::BOOL; return mi; } break; + case MATH_ISEQUALAPPROX: { + MethodInfo mi("is_equal_approx", PropertyInfo(Variant::REAL, "a"), PropertyInfo(Variant::REAL, "b")); + mi.return_val.type = Variant::BOOL; + return mi; + } break; + case MATH_ISZEROAPPROX: { + MethodInfo mi("is_zero_approx", PropertyInfo(Variant::REAL, "s")); + mi.return_val.type = Variant::BOOL; + return mi; + } break; case MATH_EASE: { MethodInfo mi("ease", PropertyInfo(Variant::REAL, "s"), PropertyInfo(Variant::REAL, "curve")); mi.return_val.type = Variant::REAL; @@ -1555,7 +1644,12 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case MATH_DECIMALS: { MethodInfo mi("decimals", PropertyInfo(Variant::REAL, "step")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::INT; + return mi; + } break; + case MATH_STEP_DECIMALS: { + MethodInfo mi("step_decimals", PropertyInfo(Variant::REAL, "step")); + mi.return_val.type = Variant::INT; return mi; } break; case MATH_STEPIFY: { @@ -1565,7 +1659,8 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case MATH_LERP: { MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::REAL, "weight")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::NIL; + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; } break; case MATH_INVERSE_LERP: { @@ -1578,6 +1673,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_SMOOTHSTEP: { + MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight")); + mi.return_val.type = Variant::REAL; + return mi; + } break; + case MATH_MOVE_TOWARD: { + MethodInfo mi("move_toward", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "delta")); + mi.return_val.type = Variant::REAL; + return mi; + } break; case MATH_DECTIME: { MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step")); mi.return_val.type = Variant::REAL; @@ -1804,13 +1909,15 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case VAR_TO_BYTES: { - MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); + MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::POOL_BYTE_ARRAY; return mi; } break; case BYTES_TO_VAR: { - MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes")); + MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects")); + mi.default_arguments.push_back(false); mi.return_val.type = Variant::NIL; mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; return mi; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index 33d5f27230..6ad70f2eb4 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,12 +59,17 @@ public: MATH_EXP, MATH_ISNAN, MATH_ISINF, + MATH_ISEQUALAPPROX, + MATH_ISZEROAPPROX, MATH_EASE, MATH_DECIMALS, + MATH_STEP_DECIMALS, MATH_STEPIFY, MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_SMOOTHSTEP, + MATH_MOVE_TOWARD, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0926c1a1ab..fa430b5364 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -125,7 +125,7 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } } -bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete) { +bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete, bool p_parsing_constant) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { tokenizer->advance(); @@ -149,7 +149,7 @@ bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bo return false; } - Node *arg = _parse_expression(p_parent, p_static); + Node *arg = _parse_expression(p_parent, p_static, false, p_parsing_constant); if (!arg) { return false; } @@ -282,7 +282,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s switch (tokenizer->get_token()) { case GDScriptTokenizer::TK_CURSOR: { - completion_cursor = StringName(); completion_type = COMPLETION_GET_NODE; completion_class = current_class; completion_function = current_function; @@ -464,7 +463,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } if (!path.is_abs_path() && base_path != "") - path = base_path + "/" + path; + path = base_path.plus_file(path); path = path.replace("///", "//").simplify_path(); if (path == self_path) { @@ -473,29 +472,31 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } Ref<Resource> res; - if (!validating) { + dependencies.push_back(path); + if (!dependencies_only) { + if (!validating) { - //this can be too slow for just validating code - if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) { - res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); - } else if (!for_completion || FileAccess::exists(path)) { - res = ResourceLoader::load(path); - } - } else { + //this can be too slow for just validating code + if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); + } else if (!for_completion || FileAccess::exists(path)) { + res = ResourceLoader::load(path); + } + } else { - if (!FileAccess::exists(path)) { + if (!FileAccess::exists(path)) { + _set_error("Can't preload resource at path: " + path); + return NULL; + } else if (ScriptCodeCompletionCache::get_singleton()) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); + } + } + if (!res.is_valid()) { _set_error("Can't preload resource at path: " + path); return NULL; - } else if (ScriptCodeCompletionCache::get_singleton()) { - res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); } } - if (!res.is_valid()) { - _set_error("Can't preload resource at path: " + path); - return NULL; - } - if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' after 'preload' path"); return NULL; @@ -503,7 +504,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s Ref<GDScript> gds = res; if (gds.is_valid() && !gds->is_valid()) { - _set_error("Could not fully preload the script, possible cyclic reference or compilation error."); + _set_error("Could not fully preload the script, possible cyclic reference or compilation error. Use 'load()' instead if a cyclic reference is intended."); return NULL; } @@ -638,7 +639,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s id->name = identifier; op->arguments.push_back(id); - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; @@ -730,7 +731,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s completion_node = op; } if (!replaced) { - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; } @@ -775,7 +776,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String()); } - } // fallthrough + FALLTHROUGH; + } case GDScriptTokenizer::TK_OP_ASSIGN: { lv->assignments += 1; lv->usages--; // Assignment is not really usage @@ -811,6 +813,34 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = constant; bfn = true; } + + if (!dependencies_only) { + if (!bfn && ScriptServer::is_global_class(identifier)) { + Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(identifier)); + if (scr.is_valid() && scr->is_valid()) { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = scr; + expr = constant; + bfn = true; + } + } + + // Check parents for the constant + if (!bfn) { + // Using current_class instead of cln here, since cln is const* + _determine_inheritance(current_class, false); + if (cln->base_type.has_type && cln->base_type.kind == DataType::GDSCRIPT && cln->base_type.script_type->is_valid()) { + Map<StringName, Variant> parent_constants; + current_class->base_type.script_type->get_constants(&parent_constants); + if (parent_constants.has(identifier)) { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = parent_constants[identifier]; + expr = constant; + bfn = true; + } + } + } + } } if (!bfn) { @@ -818,24 +848,11 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (current_function) { int arg_idx = current_function->arguments.find(identifier); if (arg_idx != -1) { - switch (tokenizer->get_token()) { - case GDScriptTokenizer::TK_OP_ASSIGN_ADD: - case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: - case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR: - case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR: - case GDScriptTokenizer::TK_OP_ASSIGN_DIV: - case GDScriptTokenizer::TK_OP_ASSIGN_MOD: - case GDScriptTokenizer::TK_OP_ASSIGN_MUL: - case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: - case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: - case GDScriptTokenizer::TK_OP_ASSIGN_SUB: - case GDScriptTokenizer::TK_OP_ASSIGN: { - // Assignment is not really usage - current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1; - } break; - default: { - current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; - } + if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { + // Assignment is not really usage + current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1; + } else { + current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; } } } @@ -858,7 +875,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case GDScriptTokenizer::TK_OP_SUB: e.op = OperatorNode::OP_NEG; break; case GDScriptTokenizer::TK_OP_NOT: e.op = OperatorNode::OP_NOT; break; case GDScriptTokenizer::TK_OP_BIT_INVERT: e.op = OperatorNode::OP_BIT_INVERT; break; - default: {} + default: { + } } tokenizer->advance(); @@ -1095,7 +1113,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } } else { tokenizer->advance(); - if (!_parse_arguments(op, op->arguments, p_static)) { + if (!_parse_arguments(op, op->arguments, p_static, false, p_parsing_constant)) { return NULL; } } @@ -1120,7 +1138,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (!expr) { ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is..."); - ERR_FAIL_COND_V(!expr, NULL); + ERR_FAIL_V(NULL); } /******************/ @@ -1147,22 +1165,14 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s tokenizer->advance(); IdentifierNode *id = alloc_node<IdentifierNode>(); - if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_FUNC) { - //small hack so built in funcs don't obfuscate methods - - id->name = GDScriptFunctions::get_func_name(tokenizer->get_token_built_in_func()); - tokenizer->advance(); - - } else { - StringName identifier; - if (_get_completable_identifier(COMPLETION_METHOD, identifier)) { - completion_node = op; - //indexing stuff - } - - id->name = identifier; + StringName identifier; + if (_get_completable_identifier(COMPLETION_METHOD, identifier)) { + completion_node = op; + //indexing stuff } + id->name = identifier; + op->arguments.push_back(expr); // call what op->arguments.push_back(id); // call func //get arguments @@ -1171,7 +1181,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s _make_completable_call(0); completion_node = op; } - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; @@ -1463,7 +1473,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (next_op == -1) { _set_error("Yet another parser bug...."); - ERR_FAIL_COND_V(next_op == -1, NULL); + ERR_FAIL_V(NULL); } // OK! create operator.. @@ -1845,7 +1855,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to } } break; - default: { break; } + default: { + break; + } } //now se if all are constants if (!all_constants) @@ -1958,10 +1970,11 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to return op->arguments[2]; } } break; - default: { ERR_FAIL_V(op); } + default: { + ERR_FAIL_V(op); + } } - ERR_FAIL_V(op); } break; default: { return p_node; @@ -2067,7 +2080,7 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) { return NULL; } pattern->pt_type = GDScriptParser::PatternNode::PT_BIND; - pattern->bind = tokenizer->get_token_identifier(); + pattern->bind = tokenizer->get_token_literal(); // Check if variable name is already used BlockNode *bl = current_block; while (bl) { @@ -2196,6 +2209,10 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) { void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static) { int indent_level = tab_level.back()->get(); + p_block->has_return = true; + + bool catch_all_appeared = false; + while (true) { while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline()) @@ -2206,7 +2223,7 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran return; if (indent_level > tab_level.back()->get()) { - return; // go back a level + break; // go back a level } if (pending_newline != -1) { @@ -2221,12 +2238,20 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran branch->patterns.push_back(_parse_pattern(p_static)); if (!branch->patterns[0]) { - return; + break; } bool has_binding = branch->patterns[0]->pt_type == PatternNode::PT_BIND; bool catch_all = has_binding || branch->patterns[0]->pt_type == PatternNode::PT_WILDCARD; +#ifdef DEBUG_ENABLED + // Branches after a wildcard or binding are unreachable + if (catch_all_appeared && !current_function->has_unreachable_code) { + _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String()); + current_function->has_unreachable_code = true; + } +#endif + while (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { tokenizer->advance(); branch->patterns.push_back(_parse_pattern(p_static)); @@ -2244,6 +2269,8 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran catch_all = catch_all || pt == PatternNode::PT_WILDCARD; } + catch_all_appeared = catch_all_appeared || catch_all; + if (!_enter_indent_block()) { _set_error("Expected block in pattern branch"); return; @@ -2253,12 +2280,17 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran current_block = p_block; - if (catch_all && branch->body->has_return) { - p_block->has_return = true; + if (!branch->body->has_return) { + p_block->has_return = false; } p_branches.push_back(branch); } + + // Even if all branches return, there is possibility of default fallthrough + if (!catch_all_appeared) { + p_block->has_return = false; + } } void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings) { @@ -2625,14 +2657,14 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { local_var->assign = e->value(); local_var->set_datatype(local_var->assign->get_datatype()); - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->name = local_var->name; - id->declared_block = branch->body; - id->set_datatype(local_var->assign->get_datatype()); + IdentifierNode *id2 = alloc_node<IdentifierNode>(); + id2->name = local_var->name; + id2->declared_block = branch->body; + id2->set_datatype(local_var->assign->get_datatype()); OperatorNode *op = alloc_node<OperatorNode>(); op->op = OperatorNode::OP_ASSIGN; - op->arguments.push_back(id); + op->arguments.push_back(id2); op->arguments.push_back(local_var->assign); branch->body->statements.push_front(op); @@ -2679,9 +2711,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { if (pending_newline != -1) { - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = pending_newline; - p_block->statements.push_back(nl); + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = pending_newline; + p_block->statements.push_back(nl2); pending_newline = -1; } @@ -2712,6 +2744,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } break; case GDScriptTokenizer::TK_NEWLINE: { + int line = tokenizer->get_token_line(); + if (!_parse_newline()) { if (!error_set) { p_block->end_line = tokenizer->get_token_line(); @@ -2720,9 +2754,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = tokenizer->get_token_line(); - p_block->statements.push_back(nl); + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = line; + p_block->statements.push_back(nl2); } break; case GDScriptTokenizer::TK_CF_PASS: { @@ -2831,8 +2865,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { lv->assign_op = op; lv->assign = assigned; - lv->assign_op = op; - if (!_end_statement()) { _set_error("Expected end of statement (var)"); return; @@ -2907,14 +2939,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { cf_else->cf_type = ControlFlowNode::CF_IF; //condition - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { + Node *condition2 = _parse_and_reduce_expression(p_block, p_static); + if (!condition2) { if (_recover_from_completion()) { break; } return; } - cf_else->arguments.push_back(condition); + cf_else->arguments.push_back(condition2); cf_else->cf_type = ControlFlowNode::CF_IF; cf_if->body_else->statements.push_back(cf_else); @@ -2977,8 +3009,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { case GDScriptTokenizer::TK_CF_WHILE: { tokenizer->advance(); - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { + Node *condition2 = _parse_and_reduce_expression(p_block, p_static); + if (!condition2) { if (_recover_from_completion()) { break; } @@ -2988,7 +3020,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { ControlFlowNode *cf_while = alloc_node<ControlFlowNode>(); cf_while->cf_type = ControlFlowNode::CF_WHILE; - cf_while->arguments.push_back(condition); + cf_while->arguments.push_back(condition2); cf_while->body = alloc_node<BlockNode>(); cf_while->body->parent_block = p_block; @@ -3038,7 +3070,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } DataType iter_type; - iter_type.is_constant = true; if (container->type == Node::TYPE_OPERATOR) { @@ -3334,7 +3365,7 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) { return; } - if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty()) { + if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty() || p_class->classname_used) { _set_error("'extends' must be used before anything else."); return; @@ -3363,6 +3394,13 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) { p_class->extends_file = constant; tokenizer->advance(); + // Add parent script as a dependency + String parent = constant; + if (parent.is_rel_path()) { + parent = base_path.plus_file(parent).simplify_path(); + } + dependencies.push_back(parent); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) { return; } else @@ -3455,11 +3493,21 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("'class_name' is only valid for the main class namespace."); return; } + if (self_path.begins_with("res://") && self_path.find("::") != -1) { + _set_error("'class_name' not allowed in built-in scripts."); + return; + } if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) { _set_error("'class_name' syntax: 'class_name <UniqueName>'"); return; } + if (p_class->classname_used) { + _set_error("'class_name' already used for this class."); + return; + } + + p_class->classname_used = true; p_class->name = tokenizer->get_token_identifier(1); @@ -3479,16 +3527,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) { - Variant constant = tokenizer->get_token_constant(); - String icon_path = constant.operator String(); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + Variant constant = tokenizer->get_token_constant(); + String icon_path = constant.operator String(); - String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path; - if (!FileAccess::exists(abs_icon_path)) { - _set_error("No class icon found at: " + abs_icon_path); - return; - } + String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path; + if (!FileAccess::exists(abs_icon_path)) { + _set_error("No class icon found at: " + abs_icon_path); + return; + } - p_class->icon_path = icon_path; + p_class->icon_path = icon_path; + } +#endif tokenizer->advance(); } else { @@ -3593,7 +3645,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - }; //fallthrough to function + FALLTHROUGH; + } case GDScriptTokenizer::TK_PR_FUNCTION: { bool _static = false; @@ -3636,6 +3689,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); } } + for (int i = 0; i < p_class->subclasses.size(); i++) { + if (p_class->subclasses[i]->name == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + } #endif // DEBUG_ENABLED if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -3963,7 +4021,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { ERR_EXPLAIN("Exporting bit flags hint requires string constants."); - WARN_DEPRECATED + WARN_DEPRECATED; break; } if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) { @@ -4006,6 +4064,50 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { break; } + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_RENDER") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 2D render hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_2D_RENDER; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_PHYSICS") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 2D physics hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_2D_PHYSICS; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 3D render hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_3D_RENDER; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_PHYSICS") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 3D physics hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_3D_PHYSICS; + break; + } + if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) { //enumeration current_export.hint = PROPERTY_HINT_ENUM; @@ -4043,7 +4145,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { break; } - }; //fallthrough to use the same + FALLTHROUGH; + } case Variant::REAL: { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EASE") { @@ -4468,6 +4571,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { #ifdef DEBUG_ENABLED _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); #endif + FALLTHROUGH; case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so @@ -4535,9 +4639,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { continue; } break; case GDScriptTokenizer::TK_PR_VAR: { - //variale declaration and (eventual) initialization + // variable declaration and (eventual) initialization ClassNode::Member member; + bool autoexport = tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_EXPORT; if (current_export.type != Variant::NIL) { member._export = current_export; @@ -4559,6 +4664,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.line = tokenizer->get_token_line(); member.usages = 0; member.rpc_mode = rpc_mode; +#ifdef TOOLS_ENABLED + Variant::CallError ce; + member.default_value = Variant::construct(member._export.type, NULL, 0, ce); +#endif if (current_class->constant_expressions.has(member.identifier)) { _set_error("A constant named '" + String(member.identifier) + "' already exists in this class (at line: " + @@ -4573,6 +4682,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } + + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == member.identifier) { + _set_error("A class named '" + String(member.identifier) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } #ifdef DEBUG_ENABLED for (int i = 0; i < current_class->functions.size(); i++) { if (current_class->functions[i]->name == member.identifier) { @@ -4663,10 +4779,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } #ifdef TOOLS_ENABLED - if (subexpr->type == Node::TYPE_CONSTANT && member._export.type != Variant::NIL) { + if (subexpr->type == Node::TYPE_CONSTANT && (member._export.type != Variant::NIL || member.data_type.has_type)) { ConstantNode *cn = static_cast<ConstantNode *>(subexpr); if (cn->value.get_type() != Variant::NIL) { + if (member._export.type != Variant::NIL && cn->value.get_type() != member._export.type) { + if (Variant::can_convert(cn->value.get_type(), member._export.type)) { + Variant::CallError err; + const Variant *args = &cn->value; + cn->value = Variant::construct(member._export.type, &args, 1, err); + } else { + _set_error("Cannot convert the provided value to the export type."); + return; + } + } member.default_value = cn->value; } } @@ -4681,12 +4807,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { op->arguments.push_back(subexpr); #ifdef DEBUG_ENABLED - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = line; + NewLineNode *nl2 = alloc_node<NewLineNode>(); + nl2->line = line; if (onready) - p_class->ready->statements.push_back(nl); + p_class->ready->statements.push_back(nl2); else - p_class->initializer->statements.push_back(nl); + p_class->initializer->statements.push_back(nl2); #endif if (onready) p_class->ready->statements.push_back(op); @@ -4701,6 +4827,36 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Type-less export needs a constant expression assigned to infer type."); return; } + + Variant::Type initial_type = member.data_type.has_type ? member.data_type.builtin_type : member._export.type; + + if (initial_type != Variant::NIL && initial_type != Variant::OBJECT) { + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = member.identifier; + + Node *expr; + + // Make sure arrays and dictionaries are not shared + if (initial_type == Variant::ARRAY) { + expr = alloc_node<ArrayNode>(); + } else if (initial_type == Variant::DICTIONARY) { + expr = alloc_node<DictionaryNode>(); + } else { + ConstantNode *cn = alloc_node<ConstantNode>(); + Variant::CallError ce2; + cn->value = Variant::construct(initial_type, NULL, 0, ce2); + expr = cn; + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_INIT_ASSIGN; + op->arguments.push_back(id); + op->arguments.push_back(expr); + + p_class->initializer->statements.push_back(op); + + member.initial_assignment = op; + } } if (autoexport && member.data_type.has_type) { @@ -4788,6 +4944,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { @@ -4858,6 +5021,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == enum_name) { + _set_error("A class named '" + String(enum_name) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -4924,6 +5094,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { tokenizer->advance(); + } else if (tokenizer->is_token_literal(0, true)) { + _set_error("Unexpected identifier"); + return; } if (enum_name != "") { @@ -4943,6 +5116,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + ClassNode::Constant constant; constant.type.has_type = true; constant.type.kind = DataType::BUILTIN; @@ -4991,9 +5171,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } -void GDScriptParser::_determine_inheritance(ClassNode *p_class) { +void GDScriptParser::_determine_inheritance(ClassNode *p_class, bool p_recursive) { - if (p_class->extends_used) { + if (p_class->base_type.has_type) { + // Already determined + } else if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; @@ -5058,7 +5240,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (ScriptServer::is_global_class(base)) { base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base)); if (!base_script.is_valid()) { - _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line); + _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic dependency).", p_class->line); return; } p = NULL; @@ -5117,6 +5299,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (base_script.is_valid()) { String ident = base; + Ref<GDScript> find_subclass = base_script; for (int i = extend_iter; i < p_class->extends_class.size(); i++) { @@ -5126,7 +5309,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (base_script->get_subclasses().has(subclass)) { - base_script = base_script->get_subclasses()[subclass]; + find_subclass = base_script->get_subclasses()[subclass]; } else if (base_script->get_constants().has(subclass)) { Ref<GDScript> new_base_class = base_script->get_constants()[subclass]; @@ -5134,7 +5317,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { _set_error("Constant is not a class: " + ident, p_class->line); return; } - base_script = new_base_class; + find_subclass = new_base_class; } else { _set_error("Could not find subclass: " + ident, p_class->line); @@ -5142,7 +5325,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { } } - script = base_script; + script = find_subclass; } else if (!base_class) { @@ -5187,9 +5370,11 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { p_class->base_type.native_type = "Reference"; } - // Recursively determine subclasses - for (int i = 0; i < p_class->subclasses.size(); i++) { - _determine_inheritance(p_class->subclasses[i]); + if (p_recursive) { + // Recursively determine subclasses + for (int i = 0; i < p_class->subclasses.size(); i++) { + _determine_inheritance(p_class->subclasses[i], p_recursive); + } } } @@ -5213,7 +5398,8 @@ String GDScriptParser::DataType::to_string() const { if (!gds_class.empty()) { return gds_class; } - } // fallthrough + FALLTHROUGH; + } case SCRIPT: { if (is_meta_type) { return script_type->get_class_name().operator String(); @@ -5381,13 +5567,13 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, String script_path = ScriptServer::get_global_class_path(id); if (script_path == self_path) { result.kind = DataType::CLASS; - result.class_type = current_class; + result.class_type = static_cast<ClassNode *>(head); } else { Ref<Script> script = ResourceLoader::load(script_path); Ref<GDScript> gds = script; if (gds.is_valid()) { if (!gds->is_valid()) { - _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic inheritance).", p_line); + _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic dependency).", p_line); return DataType(); } result.kind = DataType::GDSCRIPT; @@ -5434,6 +5620,12 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, // Inner classes ClassNode *outer_class = p; while (outer_class) { + if (outer_class->name == id) { + found = true; + result.kind = DataType::CLASS; + result.class_type = outer_class; + break; + } for (int i = 0; i < outer_class->subclasses.size(); i++) { if (outer_class->subclasses[i] == p) { continue; @@ -5474,10 +5666,10 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, result.script_type = gds; found = true; } else { - Ref<Script> scr = constants[id]; - if (scr.is_valid()) { + Ref<Script> scr2 = constants[id]; + if (scr2.is_valid()) { result.kind = DataType::SCRIPT; - result.script_type = scr; + result.script_type = scr2; found = true; } } @@ -5569,6 +5761,9 @@ GDScriptParser::DataType GDScriptParser::_type_from_gdtype(const GDScriptDataTyp result.script_type = p_gdtype.script_type; switch (p_gdtype.kind) { + case GDScriptDataType::UNINITIALIZED: { + ERR_EXPLAIN("Uninitialized datatype. Please report a bug."); + } break; case GDScriptDataType::BUILTIN: { result.kind = DataType::BUILTIN; } break; @@ -5746,18 +5941,20 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) { bool valid = p_container.builtin_type == p_expression.builtin_type; if (p_allow_implicit_conversion) { - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH); - valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL); + valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type); } return valid; } + if (p_container.kind == DataType::BUILTIN && p_container.builtin_type == Variant::OBJECT) { + // Object built-in is a special case, it's compatible with any object and with null + if (p_expression.kind == DataType::BUILTIN) { + return p_expression.builtin_type == Variant::NIL; + } + // If it's not a built-in, must be an object + return true; + } + if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) { // Can't mix built-ins with objects return false; @@ -5868,7 +6065,11 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data } GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { +#ifdef DEBUG_ENABLED + if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) { +#else if (p_node->get_datatype().has_type) { +#endif return p_node->get_datatype(); } @@ -5927,7 +6128,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { int idx = current_function->arguments.find(id->name); node_type = current_function->argument_types[idx]; } else { - node_type = _reduce_identifier_type(NULL, id->name, id->line); + node_type = _reduce_identifier_type(NULL, id->name, id->line, false); } } break; case Node::TYPE_CAST: { @@ -6088,8 +6289,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { return DataType(); } #ifdef DEBUG_ENABLED - if (var_op == Variant::OP_DIVIDE && argument_a_type.has_type && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT && - argument_b_type.has_type && argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) { + if (var_op == Variant::OP_DIVIDE && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT && + argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) { _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line); } #endif // DEBUG_ENABLED @@ -6177,7 +6378,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { result.is_constant = false; node_type = result; } else { - node_type = _reduce_identifier_type(&base_type, member_id->name, op->line); + node_type = _reduce_identifier_type(&base_type, member_id->name, op->line, true); #ifdef DEBUG_ENABLED if (!node_type.has_type) { _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string()); @@ -6219,7 +6420,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { if (check_types && index_type.has_type) { if (base_type.kind == DataType::BUILTIN) { // Check if indexing is valid - bool error = index_type.kind != DataType::BUILTIN; + bool error = index_type.kind != DataType::BUILTIN && base_type.builtin_type != Variant::DICTIONARY; if (!error) { switch (base_type.builtin_type) { // Expect int or real as index @@ -6255,7 +6456,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Variant::COLOR: { error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; } break; - default: {} + default: { + } } } if (error) { @@ -6373,7 +6575,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; - default: {} + default: { + } } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -6589,7 +6792,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat bool match = false; List<MethodInfo> constructors; Variant::get_constructor_list(tn->vtype, &constructors); - PropertyInfo return_type; + PropertyInfo return_type2; for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) { MethodInfo &mi = E->get(); @@ -6628,13 +6831,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (types_match) { match = true; - return_type = mi.return_val; + return_type2 = mi.return_val; break; } } if (match) { - return _type_from_property(return_type, false); + return _type_from_property(return_type2, false); } else if (check_types) { String err = "No constructor of '"; err += Variant::get_type_name(tn->vtype); @@ -6762,10 +6965,10 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat valid = _get_function_signature(base_type, callee_name, return_type, arg_types, default_args_count, is_static, is_vararg); - if (valid) { - return_type = original_type; - return_type.is_meta_type = false; - } + return_type = original_type; + return_type.is_meta_type = false; + + valid = true; // There's always an initializer, we can assume this is true } if (!valid) { @@ -6798,10 +7001,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat #ifdef DEBUG_ENABLED if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) { - if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) { - _set_error("Can't call non-static function from a static function.", p_call->line); - return DataType(); - } + _set_error("Can't call non-static function from a static function.", p_call->line); + return DataType(); } if (check_types && !is_static && !is_initializer && base_type.is_meta_type) { @@ -6824,6 +7025,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } break; } +#ifdef DEBUG_ENABLED if (!check_types) { return return_type; } @@ -6849,11 +7051,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (!par_type.has_type) { _mark_line_as_unsafe(p_call->line); -#ifdef DEBUG_ENABLED if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) { _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i]))); } -#endif // DEBUG_ENABLED } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { // Supertypes are acceptable for dynamic compliance if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) { @@ -6866,14 +7066,14 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat _mark_line_as_unsafe(p_call->line); } } else { -#ifdef DEBUG_ENABLED if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::REAL) { _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name); } -#endif // DEBUG_ENABLED } } +#endif // DEBUG_ENABLED + return return_type; } @@ -6894,9 +7094,9 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN if (!base_type.is_meta_type) { for (int i = 0; i < base->variables.size(); i++) { - ClassNode::Member m = base->variables[i]; - if (m.identifier == p_member) { - r_member_type = m.data_type; + if (base->variables[i].identifier == p_member) { + r_member_type = base->variables[i].data_type; + base->variables.write[i].usages += 1; return true; } } @@ -7091,43 +7291,33 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN return false; } -GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line) { +GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing) { if (p_base_type && !p_base_type->has_type) { return DataType(); } DataType base_type; + DataType member_type; - // Check classes in current file - ClassNode *base = NULL; if (!p_base_type) { - base = current_class; base_type.has_type = true; base_type.is_constant = true; base_type.kind = DataType::CLASS; - base_type.class_type = base; + base_type.class_type = current_class; } else { base_type = DataType(*p_base_type); - if (base_type.kind == DataType::CLASS) { - base = base_type.class_type; - } - } - - DataType member_type; - - for (int i = 0; i < current_class->variables.size(); i++) { - if (current_class->variables[i].identifier == p_identifier) { - member_type = current_class->variables[i].data_type; - current_class->variables.write[i].usages += 1; - return member_type; - } } if (_get_member_type(base_type, p_identifier, member_type)) { return member_type; } + if (p_is_indexing) { + // Don't look for globals since this is an indexed identifier + return DataType(); + } + if (!p_base_type) { // Possibly this is a global, check before failing @@ -7181,11 +7371,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType DataType result; result.has_type = true; result.script_type = scr; + result.is_constant = true; result.is_meta_type = true; Ref<GDScript> gds = scr; if (gds.is_valid()) { if (!gds->is_valid()) { - _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic inheritance)."); + _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic dependency)."); return DataType(); } result.kind = DataType::GDSCRIPT; @@ -7231,6 +7422,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType if (singleton.is_valid()) { DataType result; result.has_type = true; + result.is_constant = true; result.script_type = singleton; Ref<GDScript> gds = singleton; @@ -7279,7 +7471,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { DataType cont = _resolve_type(c.type, c.expression->line); DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line); - if (!_is_type_compatible(cont, expr)) { + if (check_types && !_is_type_compatible(cont, expr)) { _set_error("Constant value type (" + expr.to_string() + ") is not compatible with declared type (" + cont.to_string() + ").", c.expression->line); return; @@ -7323,7 +7515,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { if (v.expression) { DataType expr_type = _reduce_node_type(v.expression); - if (!_is_type_compatible(v.data_type, expr_type)) { + if (check_types && !_is_type_compatible(v.data_type, expr_type)) { // Try supertype test if (_is_type_compatible(expr_type, v.data_type)) { _mark_line_as_unsafe(v.line); @@ -7336,7 +7528,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = v.line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7365,30 +7557,6 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { v.data_type = expr_type; v.data_type.is_constant = false; } - } else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) { - // Create default value based on the type - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->line = v.line; - id->name = v.identifier; - - ConstantNode *init = alloc_node<ConstantNode>(); - init->line = v.line; - Variant::CallError err; - init->value = Variant::construct(v.data_type.builtin_type, NULL, 0, err); - - OperatorNode *op = alloc_node<OperatorNode>(); - op->line = v.line; - op->op = OperatorNode::OP_INIT_ASSIGN; - op->arguments.push_back(id); - op->arguments.push_back(init); - - p_class->initializer->statements.push_front(op); - v.initial_assignment = op; -#ifdef DEBUG_ENABLED - NewLineNode *nl = alloc_node<NewLineNode>(); - nl->line = v.line - 1; - p_class->initializer->statements.push_front(nl); -#endif } // Check export hint @@ -7412,7 +7580,8 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { found_setter = true; FunctionNode *setter = p_class->functions[j]; - if (setter->arguments.size() != 1) { + if (setter->get_required_argument_count() != 1 && + !(setter->get_required_argument_count() == 0 && setter->default_values.size() > 0)) { _set_error("Setter function needs to receive exactly 1 argument. See '" + setter->name + "()' definition at line " + itos(setter->line) + ".", v.line); @@ -7431,7 +7600,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { found_getter = true; FunctionNode *getter = p_class->functions[j]; - if (getter->arguments.size() != 0) { + if (getter->get_required_argument_count() != 0) { _set_error("Getter function can't receive arguments. See '" + getter->name + "()' definition at line " + itos(getter->line) + ".", v.line); @@ -7526,6 +7695,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) { _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); } + for (int j = 0; j < current_class->variables.size(); j++) { + if (current_class->variables[j].identifier == p_function->arguments[i]) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line)); + } + } #endif // DEBUG_ENABLED } @@ -7557,7 +7731,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { } parent_signature += " " + p_function->name + "("; if (arg_types.size()) { - int i = 0; + int j = 0; for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) { if (E != arg_types.front()) { parent_signature += ", "; @@ -7567,11 +7741,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { arg = "Variant"; } parent_signature += arg; - if (i == arg_types.size() - default_arg_count) { + if (j == arg_types.size() - default_arg_count) { parent_signature += "=default"; } - i++; + j++; } } parent_signature += ")"; @@ -7599,6 +7773,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { p_function->return_type.has_type = false; p_function->return_type.may_yield = true; } + +#ifdef DEBUG_ENABLED + for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == lv->name) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line)); + } + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { @@ -7707,14 +7892,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (_is_type_compatible(assign_type, lv->datatype)) { _mark_line_as_unsafe(lv->line); } else { - // Try implict conversion + // Try implicit conversion if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) { _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" + lv->datatype.to_string() + ").", lv->line); return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = lv->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7785,13 +7970,16 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { return; } - if (!lh_type.has_type) { - if (op->arguments[0]->type == Node::TYPE_OPERATOR) { - _mark_line_as_unsafe(op->line); + if (check_types) { + if (!lh_type.has_type) { + if (op->arguments[0]->type == Node::TYPE_OPERATOR) { + _mark_line_as_unsafe(op->line); + } + } + if (lh_type.is_constant) { + _set_error("Cannot assign a new value to a constant.", op->line); + return; } - } else if (lh_type.is_constant) { - _set_error("Cannot assign a new value to a constant.", op->line); - return; } DataType rh_type; @@ -7807,7 +7995,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { bool valid = false; rh_type = _get_operation_type(oper, lh_type, arg_type, valid); - if (!valid) { + if (check_types && !valid) { _set_error("Invalid operand types ('" + lh_type.to_string() + "' and '" + arg_type.to_string() + "') to assignment operator '" + Variant::get_operator_name(oper) + "'.", op->line); @@ -7830,19 +8018,19 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } #endif // DEBUG_ENABLED - if (!_is_type_compatible(lh_type, rh_type)) { + if (check_types && !_is_type_compatible(lh_type, rh_type)) { // Try supertype test if (_is_type_compatible(rh_type, lh_type)) { _mark_line_as_unsafe(op->line); } else { - // Try implict conversion + // Try implicit conversion if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) { _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" + lh_type.to_string() + ").", op->line); return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = op->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7866,9 +8054,11 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { #endif // DEBUG_ENABLED } } +#ifdef DEBUG_ENABLED if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) { _mark_line_as_unsafe(op->line); } +#endif // DEBUG_ENABLED } break; case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { @@ -7958,7 +8148,8 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (cn->value.get_type() == Variant::STRING) { break; } - } // falthrough + FALLTHROUGH; + } default: { _mark_line_as_safe(statement->line); _reduce_node_type(statement); // Test for safety anyway @@ -8090,6 +8281,10 @@ Error GDScriptParser::_parse(const String &p_base_path) { return ERR_PARSE_ERROR; } + if (dependencies_only) { + return OK; + } + _determine_inheritance(main_class); if (error_set) { @@ -8120,6 +8315,7 @@ Error GDScriptParser::_parse(const String &p_base_path) { } #ifdef DEBUG_ENABLED + // Resolve warning ignores Vector<Pair<int, String> > warning_skips = tokenizer->get_warning_skips(); bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize(); @@ -8167,7 +8363,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St return ret; } -Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) { +Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines, bool p_dependencies_only) { clear(); @@ -8177,6 +8373,7 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo validating = p_just_validate; for_completion = p_for_completion; + dependencies_only = p_dependencies_only; #ifdef DEBUG_ENABLED safe_lines = r_safe_lines; #endif // DEBUG_ENABLED @@ -8233,6 +8430,8 @@ void GDScriptParser::clear() { parenthesis = 0; current_export.type = Variant::NIL; check_types = true; + dependencies_only = false; + dependencies.clear(); error = ""; #ifdef DEBUG_ENABLED safe_lines = NULL; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 8121fb7f85..62d7bdb393 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -95,6 +95,7 @@ public: } DataType() : + kind(UNRESOLVED), has_type(false), is_constant(false), is_meta_type(false), @@ -148,6 +149,7 @@ public: bool tool; StringName name; bool extends_used; + bool classname_used; StringName extends_file; Vector<StringName> extends_class; DataType base_type; @@ -168,6 +170,7 @@ public: MultiplayerAPI::RPCMode rpc_mode; int usages; }; + struct Constant { Node *expression; DataType type; @@ -196,6 +199,7 @@ public: tool = false; type = TYPE_CLASS; extends_used = false; + classname_used = false; end_line = -1; owner = NULL; } @@ -219,6 +223,7 @@ public: virtual DataType get_datatype() const { return return_type; } virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; } + int get_required_argument_count() { return arguments.size() - default_values.size(); } FunctionNode() { type = TYPE_FUNCTION; @@ -443,7 +448,6 @@ public: CF_IF, CF_FOR, CF_WHILE, - CF_SWITCH, CF_BREAK, CF_CONTINUE, CF_RETURN, @@ -531,6 +535,8 @@ private: int error_line; int error_column; bool check_types; + bool dependencies_only; + List<String> dependencies; #ifdef DEBUG_ENABLED Set<int> *safe_lines; #endif // DEBUG_ENABLED @@ -576,7 +582,7 @@ private: #endif // DEBUG_ENABLED bool _recover_from_completion(); - bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false); + bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false, bool p_parsing_constant = false); bool _enter_indent_block(BlockNode *p_block = NULL); bool _parse_newline(); Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false); @@ -593,7 +599,7 @@ private: void _parse_class(ClassNode *p_class); bool _end_statement(); - void _determine_inheritance(ClassNode *p_class); + void _determine_inheritance(ClassNode *p_class, bool p_recursive = true); bool _parse_type(DataType &r_type, bool p_can_be_void = false); DataType _resolve_type(const DataType &p_source, int p_line); DataType _type_from_variant(const Variant &p_value) const; @@ -607,7 +613,7 @@ private: DataType _reduce_node_type(Node *p_node); DataType _reduce_function_call_type(const OperatorNode *p_call); - DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line); + DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing); void _check_class_level_types(ClassNode *p_class); void _check_class_blocks_types(ClassNode *p_class); void _check_function_types(FunctionNode *p_function); @@ -632,7 +638,7 @@ public: #ifdef DEBUG_ENABLED const List<GDScriptWarning> &get_warnings() const { return warnings; } #endif // DEBUG_ENABLED - Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL); + Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL, bool p_dependencies_only = false); Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = ""); bool is_tool_script() const; @@ -651,6 +657,8 @@ public: int get_completion_argument_index(); int get_completion_identifier_is_function(); + const List<String> &get_dependencies() const { return dependencies; } + void clear(); GDScriptParser(); ~GDScriptParser(); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index c37142b3c1..95715ab648 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,10 +80,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "elif", "else", "for", - "do", "while", - "switch (reserved)", - "case (reserved)", "break", "continue", "pass", @@ -224,9 +221,6 @@ static const _kws _keyword_list[] = { { GDScriptTokenizer::TK_CF_ELSE, "else" }, { GDScriptTokenizer::TK_CF_FOR, "for" }, { GDScriptTokenizer::TK_CF_WHILE, "while" }, - { GDScriptTokenizer::TK_CF_DO, "do" }, - { GDScriptTokenizer::TK_CF_SWITCH, "switch" }, - { GDScriptTokenizer::TK_CF_CASE, "case" }, { GDScriptTokenizer::TK_CF_BREAK, "break" }, { GDScriptTokenizer::TK_CF_CONTINUE, "continue" }, { GDScriptTokenizer::TK_CF_RETURN, "return" }, @@ -291,9 +285,6 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const case TK_CF_ELSE: case TK_CF_FOR: case TK_CF_WHILE: - case TK_CF_DO: - case TK_CF_SWITCH: - case TK_CF_CASE: case TK_CF_BREAK: case TK_CF_CONTINUE: case TK_CF_RETURN: @@ -348,7 +339,8 @@ StringName GDScriptTokenizer::get_token_literal(int p_offset) const { return "null"; case Variant::BOOL: return value ? "true" : "false"; - default: {} + default: { + } } } case TK_OP_AND: @@ -384,6 +376,11 @@ static bool _is_hex(CharType c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } +static bool _is_bin(CharType c) { + + return (c == '0' || c == '1'); +} + void GDScriptTokenizerText::_make_token(Token p_type) { TokenData &tk = tk_rb[tk_rb_pos]; @@ -543,13 +540,14 @@ void GDScriptTokenizerText::_advance() { } } #ifdef DEBUG_ENABLED - if (comment.begins_with("#warning-ignore:")) { - String code = comment.get_slice(":", 1); + String comment_content = comment.trim_prefix("#").trim_prefix(" "); + if (comment_content.begins_with("warning-ignore:")) { + String code = comment_content.get_slice(":", 1); warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower())); - } else if (comment.begins_with("#warning-ignore-all:")) { - String code = comment.get_slice(":", 1); + } else if (comment_content.begins_with("warning-ignore-all:")) { + String code = comment_content.get_slice(":", 1); warning_global_skips.insert(code.strip_edges().to_lower()); - } else if (comment.strip_edges() == "#warnings-disable") { + } else if (comment_content.strip_edges() == "warnings-disable") { ignore_warnings = true; } #endif // DEBUG_ENABLED @@ -753,7 +751,7 @@ void GDScriptTokenizerText::_advance() { } INCPOS(1); is_node_path = true; - + FALLTHROUGH; case '\'': case '"': { @@ -884,6 +882,7 @@ void GDScriptTokenizerText::_advance() { bool period_found = false; bool exponent_found = false; bool hexa_found = false; + bool bin_found = false; bool sign_found = false; String str; @@ -894,16 +893,28 @@ void GDScriptTokenizerText::_advance() { if (period_found || exponent_found) { _make_error("Invalid numeric constant at '.'"); return; + } else if (bin_found) { + _make_error("Invalid binary constant at '.'"); + return; + } else if (hexa_found) { + _make_error("Invalid hexadecimal constant at '.'"); + return; } period_found = true; } else if (GETCHAR(i) == 'x') { - if (hexa_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) { + if (hexa_found || bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) { _make_error("Invalid numeric constant at 'x'"); return; } hexa_found = true; + } else if (GETCHAR(i) == 'b') { + if (hexa_found || bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) { + _make_error("Invalid numeric constant at 'b'"); + return; + } + bin_found = true; } else if (!hexa_found && GETCHAR(i) == 'e') { - if (hexa_found || exponent_found) { + if (exponent_found || bin_found) { _make_error("Invalid numeric constant at 'e'"); return; } @@ -912,6 +923,8 @@ void GDScriptTokenizerText::_advance() { //all ok } else if (hexa_found && _is_hex(GETCHAR(i))) { + } else if (bin_found && _is_bin(GETCHAR(i))) { + } else if ((GETCHAR(i) == '-' || GETCHAR(i) == '+') && exponent_found) { if (sign_found) { _make_error("Invalid numeric constant at '-'"); @@ -937,6 +950,9 @@ void GDScriptTokenizerText::_advance() { if (hexa_found) { int64_t val = str.hex_to_int64(); _make_constant(val); + } else if (bin_found) { + int64_t val = str.bin_to_int64(); + _make_constant(val); } else if (period_found || exponent_found) { double val = str.to_double(); _make_constant(val); @@ -998,11 +1014,11 @@ void GDScriptTokenizerText::_advance() { //built in func? - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { + for (int j = 0; j < GDScriptFunctions::FUNC_MAX; j++) { - if (str == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) { + if (str == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(j))) { - _make_built_in_func(GDScriptFunctions::Function(i)); + _make_built_in_func(GDScriptFunctions::Function(j)); found = true; break; } @@ -1173,7 +1189,7 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) int version = decode_uint32(&buf[4]); if (version > BYTECODE_VERSION) { ERR_EXPLAIN("Bytecode is too New! Please use a newer engine version."); - ERR_FAIL_COND_V(version > BYTECODE_VERSION, ERR_INVALID_DATA); + ERR_FAIL_V(ERR_INVALID_DATA); } int identifier_count = decode_uint32(&buf[8]); int constant_count = decode_uint32(&buf[12]); @@ -1208,7 +1224,8 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) Variant v; int len; - Error err = decode_variant(v, b, total_len, &len); + // An object cannot be constant, never decode objects + Error err = decode_variant(v, b, total_len, &len, false); if (err) return err; b += len; @@ -1286,7 +1303,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) } break; case TK_CONSTANT: { - Variant c = tt.get_token_constant(); + const Variant &c = tt.get_token_constant(); if (!constant_map.has(c)) { int idx = constant_map.size(); constant_map[c] = idx; @@ -1310,7 +1327,8 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) ERR_FAIL_V(Vector<uint8_t>()); } break; - default: {} + default: { + } }; token_array.push_back(token); @@ -1376,11 +1394,12 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) for (Map<int, Variant>::Element *E = rev_constant_map.front(); E; E = E->next()) { int len; - Error err = encode_variant(E->get(), NULL, len); + // Objects cannot be constant, never encode objects + Error err = encode_variant(E->get(), NULL, len, false); ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); int pos = buf.size(); buf.resize(pos + len); - encode_variant(E->get(), &buf.write[pos], len); + encode_variant(E->get(), &buf.write[pos], len, false); } for (Map<int, uint32_t>::Element *E = rev_line_map.front(); E; E = E->next()) { @@ -1426,7 +1445,7 @@ StringName GDScriptTokenizerBuffer::get_token_identifier(int p_offset) const { ERR_FAIL_INDEX_V(offset, tokens.size(), StringName()); uint32_t identifier = tokens[offset] >> TOKEN_BITS; - ERR_FAIL_INDEX_V(identifier, (uint32_t)identifiers.size(), StringName()); + ERR_FAIL_UNSIGNED_INDEX_V(identifier, (uint32_t)identifiers.size(), StringName()); return identifiers[identifier]; } @@ -1482,7 +1501,7 @@ const Variant &GDScriptTokenizerBuffer::get_token_constant(int p_offset) const { int offset = token + p_offset; ERR_FAIL_INDEX_V(offset, tokens.size(), nil); uint32_t constant = tokens[offset] >> TOKEN_BITS; - ERR_FAIL_INDEX_V(constant, (uint32_t)constants.size(), nil); + ERR_FAIL_UNSIGNED_INDEX_V(constant, (uint32_t)constants.size(), nil); return constants[constant]; } String GDScriptTokenizerBuffer::get_token_error(int p_offset) const { diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index cc894fb101..7b977ff67c 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #define GDSCRIPT_TOKENIZER_H #include "core/pair.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "core/ustring.h" #include "core/variant.h" #include "core/vmap.h" @@ -86,10 +86,7 @@ public: TK_CF_ELIF, TK_CF_ELSE, TK_CF_FOR, - TK_CF_DO, TK_CF_WHILE, - TK_CF_SWITCH, - TK_CF_CASE, TK_CF_BREAK, TK_CF_CONTINUE, TK_CF_PASS, @@ -118,7 +115,7 @@ public: TK_PR_REMOTE, TK_PR_SYNC, TK_PR_MASTER, - TK_PR_SLAVE, + TK_PR_SLAVE, // Deprecated by TK_PR_PUPPET, to remove in 4.0 TK_PR_PUPPET, TK_PR_REMOTESYNC, TK_PR_MASTERSYNC, @@ -176,7 +173,7 @@ public: #ifdef DEBUG_ENABLED virtual const Vector<Pair<int, String> > &get_warning_skips() const = 0; virtual const Set<String> &get_warning_global_skips() const = 0; - virtual const bool is_ignoring_warnings() const = 0; + virtual bool is_ignoring_warnings() const = 0; #endif // DEBUG_ENABLED virtual ~GDScriptTokenizer(){}; @@ -248,7 +245,7 @@ public: #ifdef DEBUG_ENABLED virtual const Vector<Pair<int, String> > &get_warning_skips() const { return warning_skips; } virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; } - virtual const bool is_ignoring_warnings() const { return ignore_warnings; } + virtual bool is_ignoring_warnings() const { return ignore_warnings; } #endif // DEBUG_ENABLED }; @@ -292,7 +289,7 @@ public: static Set<String> s; return s; } - virtual const bool is_ignoring_warnings() const { return true; } + virtual bool is_ignoring_warnings() const { return true; } #endif // DEBUG_ENABLED GDScriptTokenizerBuffer(); }; diff --git a/modules/gdscript/icons/icon_g_d_script.svg b/modules/gdscript/icons/icon_g_d_script.svg new file mode 100644 index 0000000000..953bb9ae9e --- /dev/null +++ b/modules/gdscript/icons/icon_g_d_script.svg @@ -0,0 +1,5 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539h2l0.56445-2.2578a5 5 0 0 0 0.6875 -0.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 0.28516 -0.68555l2.2539-0.5625v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" fill="#e0e0e0"/> +</g> +</svg> diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 26dcbdcf89..b8a13ed91b 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,8 +38,8 @@ #include "gdscript_tokenizer.h" GDScriptLanguage *script_language_gd = NULL; -ResourceFormatLoaderGDScript *resource_loader_gd = NULL; -ResourceFormatSaverGDScript *resource_saver_gd = NULL; +Ref<ResourceFormatLoaderGDScript> resource_loader_gd; +Ref<ResourceFormatSaverGDScript> resource_saver_gd; #ifdef TOOLS_ENABLED @@ -54,20 +54,74 @@ class EditorExportGDScript : public EditorExportPlugin { public: virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { - if (!p_path.ends_with(".gd")) + int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED; + String script_key; + + const Ref<EditorExportPreset> &preset = get_export_preset(); + + if (preset.is_valid()) { + script_mode = preset->get_script_export_mode(); + script_key = preset->get_script_encryption_key().to_lower(); + } + + if (!p_path.ends_with(".gd") || script_mode == EditorExportPreset::MODE_SCRIPT_TEXT) return; Vector<uint8_t> file = FileAccess::get_file_as_array(p_path); if (file.empty()) return; + String txt; txt.parse_utf8((const char *)file.ptr(), file.size()); file = GDScriptTokenizerBuffer::parse_code_string(txt); - if (file.empty()) - return; - - add_file(p_path.get_basename() + ".gdc", file, true); + if (!file.empty()) { + + if (script_mode == EditorExportPreset::MODE_SCRIPT_ENCRYPTED) { + + String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("script.gde"); + FileAccess *fa = FileAccess::open(tmp_path, FileAccess::WRITE); + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < 32; i++) { + int v = 0; + if (i * 2 < script_key.length()) { + CharType ct = script_key[i * 2]; + if (ct >= '0' && ct <= '9') + ct = ct - '0'; + else if (ct >= 'a' && ct <= 'f') + ct = 10 + ct - 'a'; + v |= ct << 4; + } + + if (i * 2 + 1 < script_key.length()) { + CharType ct = script_key[i * 2 + 1]; + if (ct >= '0' && ct <= '9') + ct = ct - '0'; + else if (ct >= 'a' && ct <= 'f') + ct = 10 + ct - 'a'; + v |= ct; + } + key.write[i] = v; + } + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_WRITE_AES256); + + if (err == OK) { + fae->store_buffer(file.ptr(), file.size()); + } + + memdelete(fae); + + file = FileAccess::get_file_as_array(tmp_path); + add_file(p_path.get_basename() + ".gde", file, true); + + } else { + + add_file(p_path.get_basename() + ".gdc", file, true); + } + } } }; @@ -87,9 +141,11 @@ void register_gdscript_types() { script_language_gd = memnew(GDScriptLanguage); ScriptServer::register_language(script_language_gd); - resource_loader_gd = memnew(ResourceFormatLoaderGDScript); + + resource_loader_gd.instance(); ResourceLoader::add_resource_format_loader(resource_loader_gd); - resource_saver_gd = memnew(ResourceFormatSaverGDScript); + + resource_saver_gd.instance(); ResourceSaver::add_resource_format_saver(resource_saver_gd); #ifdef TOOLS_ENABLED @@ -104,8 +160,10 @@ void unregister_gdscript_types() { if (script_language_gd) memdelete(script_language_gd); - if (resource_loader_gd) - memdelete(resource_loader_gd); - if (resource_saver_gd) - memdelete(resource_saver_gd); + + ResourceLoader::remove_resource_format_loader(resource_loader_gd); + resource_loader_gd.unref(); + + ResourceSaver::remove_resource_format_saver(resource_saver_gd); + resource_saver_gd.unref(); } diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index 2b88a67c7e..0f01a4cdab 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 9b9088dd82..1bd3d72066 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridMap" inherits="Spatial" category="Core" version="3.1"> +<class name="GridMap" inherits="Spatial" category="Core" version="3.2"> <brief_description> Node for 3D tile-based maps. </brief_description> @@ -12,8 +12,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> @@ -52,7 +50,7 @@ <argument index="2" name="z" type="int"> </argument> <description> - The [MeshLibrary] item index located at the grid-based X, Y and Z coordinates. If the cell is empty, [INVALID_CELL_ITEM] will be returned. + The [MeshLibrary] item index located at the grid-based X, Y and Z coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned. </description> </method> <method name="get_cell_item_orientation" qualifiers="const"> @@ -88,14 +86,14 @@ <return type="Array"> </return> <description> - Array of [Transform] and [Mesh] references corresponding to the non empty cells in the grid. The transforms are specified in world space. + Array of [Transform] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space. </description> </method> <method name="get_used_cells" qualifiers="const"> <return type="Array"> </return> <description> - Array of [Vector3] with the non empty cell coordinates in the grid map. + Array of [Vector3] with the non-empty cell coordinates in the grid map. </description> </method> <method name="make_baked_meshes"> @@ -191,26 +189,26 @@ </method> </methods> <members> - <member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x"> - If [code]true[/code] grid items are centered on the X axis. + <member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true"> + If [code]true[/code], grid items are centered on the X axis. </member> - <member name="cell_center_y" type="bool" setter="set_center_y" getter="get_center_y"> - If [code]true[/code] grid items are centered on the Y axis. + <member name="cell_center_y" type="bool" setter="set_center_y" getter="get_center_y" default="true"> + If [code]true[/code], grid items are centered on the Y axis. </member> - <member name="cell_center_z" type="bool" setter="set_center_z" getter="get_center_z"> - If [code]true[/code] grid items are centered on the Z axis. + <member name="cell_center_z" type="bool" setter="set_center_z" getter="get_center_z" default="true"> + If [code]true[/code], grid items are centered on the Z axis. </member> - <member name="cell_octant_size" type="int" setter="set_octant_size" getter="get_octant_size"> + <member name="cell_octant_size" type="int" setter="set_octant_size" getter="get_octant_size" default="8"> The size of each octant measured in number of cells. This applies to all three axis. </member> - <member name="cell_scale" type="float" setter="set_cell_scale" getter="get_cell_scale"> + <member name="cell_scale" type="float" setter="set_cell_scale" getter="get_cell_scale" default="1.0"> </member> - <member name="cell_size" type="Vector3" setter="set_cell_size" getter="get_cell_size"> + <member name="cell_size" type="Vector3" setter="set_cell_size" getter="get_cell_size" default="Vector3( 2, 2, 2 )"> The dimensions of the grid's cells. </member> - <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> + <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1"> </member> - <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> + <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> </member> <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library"> The assigned [MeshLibrary]. diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 274a2f0249..994a84fbc4 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -197,7 +197,7 @@ bool GridMap::get_collision_layer_bit(int p_bit) const { void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) { ERR_EXPLAIN("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead."); - WARN_DEPRECATED + WARN_DEPRECATED; set_mesh_library(p_theme); } @@ -205,7 +205,7 @@ void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) { Ref<MeshLibrary> GridMap::get_theme() const { ERR_EXPLAIN("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead."); - WARN_DEPRECATED + WARN_DEPRECATED; return get_mesh_library(); } @@ -241,6 +241,7 @@ Vector3 GridMap::get_cell_size() const { void GridMap::set_octant_size(int p_size) { + ERR_FAIL_COND(p_size == 0); octant_size = p_size; _recreate_octant_data(); } @@ -517,7 +518,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { Ref<NavigationMesh> navmesh = mesh_library->get_item_navmesh(c.item); if (navmesh.is_valid()) { Octant::NavMesh nm; - nm.xform = xform; + nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item); if (navigation) { nm.id = navigation->navmesh_add(navmesh, xform, this); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 3d8be5c9c7..f4407099f5 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 126b49832a..5a21833ffa 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -67,9 +67,6 @@ void GridMapEditor::_menu_option(int p_option) { floor->set_value(floor->get_value() + 1); } break; - case MENU_OPTION_CONFIGURE: { - - } break; case MENU_OPTION_LOCK_VIEW: { int index = options->get_popup()->get_item_index(MENU_OPTION_LOCK_VIEW); @@ -121,14 +118,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_Y: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -137,12 +135,12 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_X: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(1, 0, 0), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } @@ -154,12 +152,12 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_Z: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(0, 0, 1), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } @@ -171,6 +169,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_Y: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(0, 1, 0), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 1, 0), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -179,6 +186,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_X: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(1, 0, 0), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(1, 0, 0), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -187,6 +203,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_Z: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(0, 0, 1), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 0, 1), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -194,10 +219,10 @@ void GridMapEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CURSOR_CLEAR_ROTATION: { - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - selection.duplicate_rot = 0; - _update_duplicate_indicator(); + paste_indicator.orientation = 0; + _update_paste_indicator(); break; } @@ -205,28 +230,33 @@ void GridMapEditor::_menu_option(int p_option) { _update_cursor_transform(); } break; - case MENU_OPTION_DUPLICATE_SELECTS: { - int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); + case MENU_OPTION_PASTE_SELECTS: { + int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS); options->get_popup()->set_item_checked(idx, !options->get_popup()->is_item_checked(idx)); } break; + case MENU_OPTION_SELECTION_DUPLICATE: + case MENU_OPTION_SELECTION_CUT: { if (!(selection.active && input_action == INPUT_NONE)) - return; - if (last_mouseover == Vector3(-1, -1, -1)) //nono mouseovering anythin break; - last_mouseover = selection.begin; - VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], Transform(Basis(), grid_ofs)); + _set_clipboard_data(); - input_action = INPUT_DUPLICATE; - selection.click = last_mouseover; - selection.current = last_mouseover; - selection.duplicate_rot = 0; - _update_duplicate_indicator(); - break; + if (p_option == MENU_OPTION_SELECTION_CUT) { + _delete_selection(); + } + + input_action = INPUT_PASTE; + paste_indicator.click = selection.begin; + paste_indicator.current = selection.begin; + paste_indicator.begin = selection.begin; + paste_indicator.end = selection.end; + paste_indicator.orientation = 0; + _update_paste_indicator(); + } break; case MENU_OPTION_SELECTION_CLEAR: { if (!selection.active) - return; + break; _delete_selection(); @@ -249,7 +279,7 @@ void GridMapEditor::_update_cursor_transform() { cursor_transform = Transform(); cursor_transform.origin = cursor_origin; cursor_transform.basis.set_orthogonal_index(cursor_rot); - cursor_transform = node->get_transform() * cursor_transform; + cursor_transform = node->get_global_transform() * cursor_transform; if (cursor_instance.is_valid()) { VisualServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform); @@ -289,11 +319,11 @@ void GridMapEditor::_update_selection_transform() { scale *= node->get_cell_size(); pos *= node->get_cell_size(); - Transform xf; - xf.basis.scale(scale); - xf.origin = pos; + Transform xf2; + xf2.basis.scale(scale); + xf2.origin = pos; - VisualServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf); + VisualServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf2); } } } @@ -315,17 +345,28 @@ void GridMapEditor::_validate_selection() { _update_selection_transform(); } +void GridMapEditor::_set_selection(bool p_active, const Vector3 &p_begin, const Vector3 &p_end) { + + selection.active = p_active; + selection.begin = p_begin; + selection.end = p_end; + selection.click = p_begin; + selection.current = p_end; + + _update_selection_transform(); +} + bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click) { if (!spatial_editor) return false; - if (selected_palette < 0 && input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE) + if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) return false; Ref<MeshLibrary> mesh_library = node->get_mesh_library(); if (mesh_library.is_null()) return false; - if (input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE && !mesh_library->has_item(selected_palette)) + if (input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) return false; Camera *camera = p_camera; @@ -379,20 +420,24 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo } last_mouseover = Vector3(cell[0], cell[1], cell[2]); - VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], Transform(Basis(), grid_ofs)); + VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], node->get_global_transform() * edit_grid_xform); if (cursor_instance.is_valid()) { cursor_origin = (Vector3(cell[0], cell[1], cell[2]) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size(); cursor_visible = true; + if (input_action == INPUT_SELECT || input_action == INPUT_PASTE) { + cursor_visible = false; + } + _update_cursor_transform(); } - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - selection.current = Vector3(cell[0], cell[1], cell[2]); - _update_duplicate_indicator(); + paste_indicator.current = Vector3(cell[0], cell[1], cell[2]); + _update_paste_indicator(); } else if (input_action == INPUT_SELECT) { @@ -403,7 +448,7 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo _validate_selection(); return true; - } else if (input_action == INPUT_COPY) { + } else if (input_action == INPUT_PICK) { int item = node->get_cell_item(cell[0], cell[1], cell[2]); if (item >= 0) { @@ -456,10 +501,9 @@ void GridMapEditor::_delete_selection() { } } } + undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->commit_action(); - - selection.active = false; - _validate_selection(); } void GridMapEditor::_fill_selection() { @@ -479,97 +523,124 @@ void GridMapEditor::_fill_selection() { } } } + undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->commit_action(); +} - selection.active = false; - _validate_selection(); +void GridMapEditor::_clear_clipboard_data() { + + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { + + VisualServer::get_singleton()->free(E->get().instance); + } + + clipboard_items.clear(); } -void GridMapEditor::_update_duplicate_indicator() { +void GridMapEditor::_set_clipboard_data() { + + _clear_clipboard_data(); + + Ref<MeshLibrary> meshLibrary = node->get_mesh_library(); + + for (int i = selection.begin.x; i <= selection.end.x; i++) { + + for (int j = selection.begin.y; j <= selection.end.y; j++) { + + for (int k = selection.begin.z; k <= selection.end.z; k++) { + + int itm = node->get_cell_item(i, j, k); + if (itm == GridMap::INVALID_CELL_ITEM) + continue; + + Ref<Mesh> mesh = meshLibrary->get_item_mesh(itm); - if (!selection.active || input_action != INPUT_DUPLICATE) { + ClipboardItem item; + item.cell_item = itm; + item.grid_offset = Vector3(i, j, k) - selection.begin; + item.orientation = node->get_cell_item_orientation(i, j, k); + item.instance = VisualServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world()->get_scenario()); + + clipboard_items.push_back(item); + } + } + } +} + +void GridMapEditor::_update_paste_indicator() { + + if (input_action != INPUT_PASTE) { Transform xf; xf.basis.set_zero(); - VisualServer::get_singleton()->instance_set_transform(duplicate_instance, xf); + VisualServer::get_singleton()->instance_set_transform(paste_instance, xf); return; } + Vector3 center = 0.5 * Vector3(float(node->get_center_x()), float(node->get_center_y()), float(node->get_center_z())); + Vector3 scale = (Vector3(1, 1, 1) + (paste_indicator.end - paste_indicator.begin)) * node->get_cell_size(); Transform xf; - xf.scale(Vector3(1, 1, 1) * (Vector3(1, 1, 1) + (selection.end - selection.begin)) * node->get_cell_size()); - xf.origin = (selection.begin + (selection.current - selection.click)) * node->get_cell_size(); + xf.scale(scale); + xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size(); Basis rot; - rot.set_orthogonal_index(selection.duplicate_rot); + rot.set_orthogonal_index(paste_indicator.orientation); xf.basis = rot * xf.basis; + xf.translate((-center * node->get_cell_size()) / scale); - VisualServer::get_singleton()->instance_set_transform(duplicate_instance, node->get_global_transform() * xf); -} + VisualServer::get_singleton()->instance_set_transform(paste_instance, node->get_global_transform() * xf); -struct __Item { - Vector3 pos; - int rot; - int item; -}; -void GridMapEditor::_duplicate_paste() { + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { - if (!selection.active) - return; + ClipboardItem &item = E->get(); - int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); - bool reselect = options->get_popup()->is_item_checked(idx); + xf = Transform(); + xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size(); + xf.basis = rot * xf.basis; + xf.translate(item.grid_offset * node->get_cell_size()); + + Basis item_rot; + item_rot.set_orthogonal_index(item.orientation); + xf.basis = item_rot * xf.basis; + + VisualServer::get_singleton()->instance_set_transform(item.instance, node->get_global_transform() * xf); + } +} + +void GridMapEditor::_do_paste() { - List<__Item> items; + int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS); + bool reselect = options->get_popup()->is_item_checked(idx); Basis rot; - rot.set_orthogonal_index(selection.duplicate_rot); + rot.set_orthogonal_index(paste_indicator.orientation); - for (int i = selection.begin.x; i <= selection.end.x; i++) { + Vector3 ofs = paste_indicator.current - paste_indicator.click; + undo_redo->create_action(TTR("GridMap Paste Selection")); - for (int j = selection.begin.y; j <= selection.end.y; j++) { + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { - for (int k = selection.begin.z; k <= selection.end.z; k++) { + ClipboardItem &item = E->get(); - int itm = node->get_cell_item(i, j, k); - if (itm == GridMap::INVALID_CELL_ITEM) - continue; - int orientation = node->get_cell_item_orientation(i, j, k); - __Item item; - Vector3 rel = Vector3(i, j, k) - selection.begin; - rel = rot.xform(rel); - - Basis orm; - orm.set_orthogonal_index(orientation); - orm = rot * orm; - - item.pos = selection.begin + rel; - item.item = itm; - item.rot = orm.get_orthogonal_index(); - items.push_back(item); - } - } - } + Vector3 pos = rot.xform(item.grid_offset) + paste_indicator.begin + ofs; - Vector3 ofs = selection.current - selection.click; - if (items.size()) { - undo_redo->create_action(TTR("GridMap Duplicate Selection")); - for (List<__Item>::Element *E = items.front(); E; E = E->next()) { - __Item &it = E->get(); - Vector3 pos = it.pos + ofs; + Basis orm; + orm.set_orthogonal_index(item.orientation); + orm = rot * orm; - undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, it.item, it.rot); - undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z)); - } - undo_redo->commit_action(); + undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, item.cell_item, orm.get_orthogonal_index()); + undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z)); } if (reselect) { - selection.begin += ofs; - selection.end += ofs; - selection.click = selection.begin; - selection.current = selection.end; - _validate_selection(); + undo_redo->add_do_method(this, "_set_selection", true, paste_indicator.begin + ofs, paste_indicator.end + ofs); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); } + + undo_redo->commit_action(); + + _clear_clipboard_data(); } bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<InputEvent> &p_event) { @@ -593,31 +664,36 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu } if (mb->is_pressed()) { + SpatialEditorViewport::NavigationScheme nav_scheme = (SpatialEditorViewport::NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + if ((nav_scheme == SpatialEditorViewport::NAVIGATION_MAYA || nav_scheme == SpatialEditorViewport::NAVIGATION_MODO) && mb->get_alt()) { + input_action = INPUT_NONE; + } else if (mb->get_button_index() == BUTTON_LEFT) { - if (mb->get_button_index() == BUTTON_LEFT) { - - if (input_action == INPUT_DUPLICATE) { - //paste - _duplicate_paste(); + if (input_action == INPUT_PASTE) { + _do_paste(); input_action = INPUT_NONE; - _update_duplicate_indicator(); + _update_paste_indicator(); } else if (mb->get_shift()) { input_action = INPUT_SELECT; + last_selection = selection; } else if (mb->get_command()) { - input_action = INPUT_COPY; + input_action = INPUT_PICK; } else { input_action = INPUT_PAINT; set_items.clear(); } } else if (mb->get_button_index() == BUTTON_RIGHT) { - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { + _clear_clipboard_data(); input_action = INPUT_NONE; - _update_duplicate_indicator(); - } else if (mb->get_shift()) { + _update_paste_indicator(); + return true; + } else if (selection.active) { + _set_selection(false); + return true; + } else { input_action = INPUT_ERASE; set_items.clear(); - } else { - return false; } } else { return false; @@ -631,7 +707,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { - undo_redo->create_action("GridMap Paint"); + undo_redo->create_action(TTR("GridMap Paint")); for (List<SetItem>::Element *E = set_items.front(); E; E = E->next()) { const SetItem &si = E->get(); @@ -650,13 +726,21 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return set_items.size() > 0; } + if (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_SELECT) { + + undo_redo->create_action("GridMap Selection"); + undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end); + undo_redo->commit_action(); + } + if (mb->get_button_index() == BUTTON_LEFT && input_action != INPUT_NONE) { set_items.clear(); input_action = INPUT_NONE; return true; } - if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_DUPLICATE)) { + if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) { input_action = INPUT_NONE; return true; } @@ -670,6 +754,45 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return do_input_action(p_camera, mm->get_position(), false); } + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (k->is_pressed()) { + if (k->get_scancode() == KEY_ESCAPE) { + + if (input_action == INPUT_PASTE) { + _clear_clipboard_data(); + input_action = INPUT_NONE; + _update_paste_indicator(); + return true; + } else if (selection.active) { + _set_selection(false); + return true; + } else { + selected_palette = -1; + mesh_library_palette->unselect_all(); + update_palette(); + _update_cursor_instance(); + return true; + } + } + + if (k->get_shift() && selection.active && input_action != INPUT_PASTE) { + + if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) { + selection.click[edit_axis]--; + _validate_selection(); + return true; + } + if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) { + selection.click[edit_axis]++; + _validate_selection(); + return true; + } + } + } + } + Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { @@ -717,6 +840,26 @@ void GridMapEditor::_set_display_mode(int p_mode) { update_palette(); } +void GridMapEditor::_text_changed(const String &p_text) { + update_palette(); +} + +void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) { + + Ref<InputEventKey> k = p_ie; + + if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_PAGEUP || k->get_scancode() == KEY_PAGEDOWN)) { + + mesh_library_palette->call("_gui_input", k); + search_box->accept_event(); + } +} + +void GridMapEditor::_icon_size_changed(float p_value) { + mesh_library_palette->set_icon_scale(p_value); + update_palette(); +} + void GridMapEditor::update_palette() { int selected = mesh_library_palette->get_current(); @@ -730,8 +873,9 @@ void GridMapEditor::update_palette() { } float min_size = EDITOR_DEF("editors/grid_map/preview_size", 64); + min_size *= EDSCALE; mesh_library_palette->set_fixed_icon_size(Size2(min_size, min_size)); - mesh_library_palette->set_fixed_column_width(min_size * 3 / 2); + mesh_library_palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1.5)); mesh_library_palette->set_max_text_lines(2); Ref<MeshLibrary> mesh_library = node->get_mesh_library(); @@ -754,23 +898,28 @@ void GridMapEditor::update_palette() { } il.sort(); + String filter = search_box->get_text().strip_edges(); + int item = 0; for (List<_CGMEItemSort>::Element *E = il.front(); E; E = E->next()) { int id = E->get().id; - - mesh_library_palette->add_item(""); - String name = mesh_library->get_item_name(id); Ref<Texture> preview = mesh_library->get_item_preview(id); + if (name == "") { + name = "#" + itos(id); + } + + if (filter != "" && !filter.is_subsequence_ofi(name)) + continue; + + mesh_library_palette->add_item(""); if (!preview.is_null()) { mesh_library_palette->set_item_icon(item, preview); mesh_library_palette->set_item_tooltip(item, name); } - if (name != "") { - mesh_library_palette->set_item_text(item, name); - } + mesh_library_palette->set_item_text(item, name); mesh_library_palette->set_item_metadata(item, id); item++; @@ -792,7 +941,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { input_action = INPUT_NONE; selection.active = false; _update_selection_transform(); - _update_duplicate_indicator(); + _update_paste_indicator(); spatial_editor = Object::cast_to<SpatialEditorPlugin>(editor->get_editor_plugin_screen()); @@ -927,13 +1076,15 @@ void GridMapEditor::_notification(int p_what) { } selection_instance = VisualServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world()->get_scenario()); - duplicate_instance = VisualServer::get_singleton()->instance_create2(duplicate_mesh, get_tree()->get_root()->get_world()->get_scenario()); + paste_instance = VisualServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world()->get_scenario()); _update_selection_transform(); - _update_duplicate_indicator(); + _update_paste_indicator(); } break; case NOTIFICATION_EXIT_TREE: { + _clear_clipboard_data(); + for (int i = 0; i < 3; i++) { VS::get_singleton()->free(grid_instance[i]); @@ -944,9 +1095,9 @@ void GridMapEditor::_notification(int p_what) { } VisualServer::get_singleton()->free(selection_instance); - VisualServer::get_singleton()->free(duplicate_instance); + VisualServer::get_singleton()->free(paste_instance); selection_instance = RID(); - duplicate_instance = RID(); + paste_instance = RID(); } break; case NOTIFICATION_PROCESS: { @@ -985,6 +1136,7 @@ void GridMapEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { options->set_icon(get_icon("GridMap", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); } break; } } @@ -1029,12 +1181,21 @@ void GridMapEditor::_floor_changed(float p_value) { _update_selection_transform(); } +void GridMapEditor::_floor_mouse_exited() { + floor->get_line_edit()->release_focus(); +} + void GridMapEditor::_bind_methods() { + ClassDB::bind_method("_text_changed", &GridMapEditor::_text_changed); + ClassDB::bind_method("_sbox_input", &GridMapEditor::_sbox_input); + ClassDB::bind_method("_icon_size_changed", &GridMapEditor::_icon_size_changed); ClassDB::bind_method("_menu_option", &GridMapEditor::_menu_option); ClassDB::bind_method("_configure", &GridMapEditor::_configure); ClassDB::bind_method("_item_selected_cbk", &GridMapEditor::_item_selected_cbk); ClassDB::bind_method("_floor_changed", &GridMapEditor::_floor_changed); + ClassDB::bind_method("_floor_mouse_exited", &GridMapEditor::_floor_mouse_exited); + ClassDB::bind_method("_set_selection", &GridMapEditor::_set_selection); ClassDB::bind_method(D_METHOD("_set_display_mode", "mode"), &GridMapEditor::_set_display_mode); } @@ -1067,6 +1228,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { spatial_editor_hb->add_child(floor); floor->connect("value_changed", this, "_floor_changed"); + floor->connect("mouse_exited", this, "_floor_mouse_exited"); + floor->get_line_edit()->connect("mouse_exited", this, "_floor_mouse_exited"); spatial_editor_hb->add_child(memnew(VSeparator)); @@ -1098,15 +1261,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KEY_MASK_SHIFT + KEY_D); options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, KEY_W); options->get_popup()->add_separator(); - options->get_popup()->add_check_item("Duplicate Selects", MENU_OPTION_DUPLICATE_SELECTS); + options->get_popup()->add_check_item("Paste Selects", MENU_OPTION_PASTE_SELECTS); options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Area"), MENU_OPTION_SELECTION_MAKE_AREA, KEY_CONTROL + KEY_C); - options->get_popup()->add_item(TTR("Create Exterior Connector"), MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR); - options->get_popup()->add_item(TTR("Erase Area"), MENU_OPTION_REMOVE_AREA); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C); - options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X); - options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F); + options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_CTRL + KEY_C); + options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KEY_MASK_CTRL + KEY_X); + options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_DELETE); + options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_CTRL + KEY_F); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS); @@ -1132,6 +1292,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { add_child(hb); hb->set_h_size_flags(SIZE_EXPAND_FILL); + search_box = memnew(LineEdit); + search_box->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(search_box); + search_box->connect("text_changed", this, "_text_changed"); + search_box->connect("gui_input", this, "_sbox_input"); + mode_thumbnail = memnew(ToolButton); mode_thumbnail->set_toggle_mode(true); mode_thumbnail->set_pressed(true); @@ -1146,6 +1312,15 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { hb->add_child(mode_list); mode_list->connect("pressed", this, "_set_display_mode", varray(DISPLAY_LIST)); + size_slider = memnew(HSlider); + size_slider->set_h_size_flags(SIZE_EXPAND_FILL); + size_slider->set_min(0.1f); + size_slider->set_max(4.0f); + size_slider->set_step(0.1f); + size_slider->set_value(1.0f); + size_slider->connect("value_changed", this, "_icon_size_changed"); + add_child(size_slider); + EDITOR_DEF("editors/grid_map/preview_size", 64); display_mode = DISPLAY_THUMBNAIL; @@ -1166,7 +1341,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { last_mouseover = Vector3(-1, -1, -1); selection_mesh = VisualServer::get_singleton()->mesh_create(); - duplicate_mesh = VisualServer::get_singleton()->mesh_create(); + paste_mesh = VisualServer::get_singleton()->mesh_create(); { //selection mesh create @@ -1274,12 +1449,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { VisualServer::get_singleton()->mesh_surface_set_material(selection_mesh, 1, outer_mat->get_rid()); d[VS::ARRAY_VERTEX] = triangles; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_TRIANGLES, d); - VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 0, inner_mat->get_rid()); + VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_TRIANGLES, d); + VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 0, inner_mat->get_rid()); d[VS::ARRAY_VERTEX] = lines; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_LINES, d); - VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 1, outer_mat->get_rid()); + VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_LINES, d); + VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 1, outer_mat->get_rid()); for (int i = 0; i < 3; i++) { d[VS::ARRAY_VERTEX] = square[i]; @@ -1296,6 +1471,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { GridMapEditor::~GridMapEditor() { + _clear_clipboard_data(); + for (int i = 0; i < 3; i++) { if (grid[i].is_valid()) @@ -1314,9 +1491,9 @@ GridMapEditor::~GridMapEditor() { if (selection_instance.is_valid()) VisualServer::get_singleton()->free(selection_instance); - VisualServer::get_singleton()->free(duplicate_mesh); - if (duplicate_instance.is_valid()) - VisualServer::get_singleton()->free(duplicate_instance); + VisualServer::get_singleton()->free(paste_mesh); + if (paste_instance.is_valid()) + VisualServer::get_singleton()->free(paste_instance); } void GridMapEditorPlugin::_notification(int p_what) { diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 663274f46e..b9be925ff7 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,9 +54,9 @@ class GridMapEditor : public VBoxContainer { INPUT_NONE, INPUT_PAINT, INPUT_ERASE, - INPUT_COPY, + INPUT_PICK, INPUT_SELECT, - INPUT_DUPLICATE, + INPUT_PASTE, }; enum ClipMode { @@ -79,6 +79,8 @@ class GridMapEditor : public VBoxContainer { double accumulated_floor_delta; ToolButton *mode_thumbnail; ToolButton *mode_list; + LineEdit *search_box; + HSlider *size_slider; HBoxContainer *spatial_editor_hb; ConfirmationDialog *settings_dialog; VBoxContainer *settings_vbc; @@ -114,8 +116,17 @@ class GridMapEditor : public VBoxContainer { RID selection_instance; RID selection_level_mesh[3]; RID selection_level_instance[3]; - RID duplicate_mesh; - RID duplicate_instance; + RID paste_mesh; + RID paste_instance; + + struct ClipboardItem { + int cell_item; + Vector3 grid_offset; + int orientation; + RID instance; + }; + + List<ClipboardItem> clipboard_items; Ref<SpatialMaterial> indicator_mat; Ref<SpatialMaterial> inner_mat; @@ -130,9 +141,19 @@ class GridMapEditor : public VBoxContainer { Vector3 current; Vector3 begin; Vector3 end; - int duplicate_rot; bool active; } selection; + Selection last_selection; + + struct PasteIndicator { + + Vector3 click; + Vector3 current; + Vector3 begin; + Vector3 end; + int orientation; + }; + PasteIndicator paste_indicator; bool cursor_visible; Transform cursor_transform; @@ -146,7 +167,6 @@ class GridMapEditor : public VBoxContainer { enum Menu { - MENU_OPTION_CONFIGURE, MENU_OPTION_NEXT_LEVEL, MENU_OPTION_PREV_LEVEL, MENU_OPTION_LOCK_VIEW, @@ -163,13 +183,11 @@ class GridMapEditor : public VBoxContainer { MENU_OPTION_CURSOR_BACK_ROTATE_X, MENU_OPTION_CURSOR_BACK_ROTATE_Z, MENU_OPTION_CURSOR_CLEAR_ROTATION, - MENU_OPTION_DUPLICATE_SELECTS, - MENU_OPTION_SELECTION_MAKE_AREA, - MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR, + MENU_OPTION_PASTE_SELECTS, MENU_OPTION_SELECTION_DUPLICATE, + MENU_OPTION_SELECTION_CUT, MENU_OPTION_SELECTION_CLEAR, MENU_OPTION_SELECTION_FILL, - MENU_OPTION_REMOVE_AREA, MENU_OPTION_GRIDMAP_SETTINGS }; @@ -193,12 +211,21 @@ class GridMapEditor : public VBoxContainer { void _update_cursor_instance(); void _update_clip(); - void _update_duplicate_indicator(); - void _duplicate_paste(); + void _text_changed(const String &p_text); + void _sbox_input(const Ref<InputEvent> &p_ie); + + void _icon_size_changed(float p_value); + + void _clear_clipboard_data(); + void _set_clipboard_data(); + void _update_paste_indicator(); + void _do_paste(); void _update_selection_transform(); void _validate_selection(); + void _set_selection(bool p_active, const Vector3 &p_begin = Vector3(), const Vector3 &p_end = Vector3()); void _floor_changed(float p_value); + void _floor_mouse_exited(); void _delete_selection(); void _fill_selection(); diff --git a/modules/gridmap/icons/icon_grid_map.svg b/modules/gridmap/icons/icon_grid_map.svg new file mode 100644 index 0000000000..eafe1211f2 --- /dev/null +++ b/modules/gridmap/icons/icon_grid_map.svg @@ -0,0 +1,5 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/> +</g> +</svg> diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp index 030286d651..03db5d323e 100644 --- a/modules/gridmap/register_types.cpp +++ b/modules/gridmap/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h index 4ff107bec8..5a07ea84d9 100644 --- a/modules/gridmap/register_types.h +++ b/modules/gridmap/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index fc6779ce86..f75a4a926a 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,6 @@ #include "core/os/os.h" #include "core/print_string.h" -#include "thirdparty/tinyexr/tinyexr.h" - Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { String header = f->get_token(); diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index 7175b38cc8..8ebf52def7 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp index 8688daad69..8a2b6f14d1 100644 --- a/modules/hdr/register_types.cpp +++ b/modules/hdr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/hdr/register_types.h b/modules/hdr/register_types.h index e064f5d945..57a9058a89 100644 --- a/modules/hdr/register_types.h +++ b/modules/hdr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub index d5f87905eb..96e8e704dd 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -13,7 +13,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_jpg.Append(CPPPATH=[thirdparty_dir]) +env_jpg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_jpg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 24d40111cf..dcd8b8aebd 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -96,7 +96,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p else fmt = Image::FORMAT_RGB8; - dw = PoolVector<uint8_t>::Write(); + dw.release(); p_image->create(image_width, image_height, 0, fmt, data); return OK; @@ -117,8 +117,6 @@ Error ImageLoaderJPG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force Error err = jpeg_load_image_from_buffer(p_image.ptr(), w.ptr(), src_image_len); - w = PoolVector<uint8_t>::Write(); - return err; } diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h index b5f0637c9b..9a96fe008d 100644 --- a/modules/jpg/image_loader_jpegd.h +++ b/modules/jpg/image_loader_jpegd.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp index 450ac065f4..1febfdebf5 100644 --- a/modules/jpg/register_types.cpp +++ b/modules/jpg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h index 6f10c18c12..633160d941 100644 --- a/modules/jpg/register_types.h +++ b/modules/jpg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp index 8548275eec..121ed5eb02 100755 --- a/modules/mbedtls/register_types.cpp +++ b/modules/mbedtls/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h index 3da0b1f1a0..535dae7999 100755 --- a/modules/mbedtls/register_types.h +++ b/modules/mbedtls/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index 5c81f32e9e..4bb7557150 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* stream_peer_openssl.cpp */ +/* stream_peer_mbed_tls.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,6 @@ #include "core/io/stream_peer_tcp.h" #include "core/os/file_access.h" -#include <mbedtls/platform_util.h> - static void my_debug(void *ctx, int level, const char *file, int line, const char *str) { @@ -122,6 +120,8 @@ Error StreamPeerMbedTLS::_do_handshake() { Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) { + ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); + base = p_base; int ret = 0; int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; @@ -270,7 +270,10 @@ void StreamPeerMbedTLS::poll() { return; } - int ret = mbedtls_ssl_read(&ssl, NULL, 0); + // We could pass NULL as second parameter, but some behaviour sanitizers doesn't seem to like that. + // Passing a 1 byte buffer to workaround it. + uint8_t byte; + int ret = mbedtls_ssl_read(&ssl, &byte, 0); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // Nothing to read/write (non blocking IO) @@ -313,7 +316,7 @@ void StreamPeerMbedTLS::disconnect_from_stream() { Ref<StreamPeerTCP> tcp = base; if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) { - // We are still connected on the socket, try to send close notity. + // We are still connected on the socket, try to send close notify. mbedtls_ssl_close_notify(&ssl); } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index abf87b79cc..ab87b779c1 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* stream_peer_openssl.h */ +/* stream_peer_mbed_tls.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,6 @@ #include <mbedtls/ctr_drbg.h> #include <mbedtls/debug.h> #include <mbedtls/entropy.h> -#include <mbedtls/net.h> #include <mbedtls/ssl.h> #include <stdio.h> diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 359d654433..a132333a87 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,35 +1,42 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.1"> +<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.2"> <brief_description> - Generic mobile VR implementation + Generic mobile VR implementation. </brief_description> <description> - This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect you do need a mobile phone with a gyroscope and accelerometer. - Note that even though there is no positional tracking the camera will assume the headset is at a height of 1.85 meters. + This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect, you need a mobile phone with a gyroscope and accelerometer. + Note that even though there is no positional tracking, the camera will assume the headset is at a height of 1.85 meters. You can change this by setting [member eye_height]. + You can initialise this interface as follows: + [codeblock] + var interface = ARVRServer.find_interface("Native mobile") + if interface and interface.initialize(): + get_viewport().arvr = true + [/codeblock] </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="display_to_lens" type="float" setter="set_display_to_lens" getter="get_display_to_lens"> + <member name="display_to_lens" type="float" setter="set_display_to_lens" getter="get_display_to_lens" default="4.0"> The distance between the display and the lenses inside of the device in centimeters. </member> - <member name="display_width" type="float" setter="set_display_width" getter="get_display_width"> + <member name="display_width" type="float" setter="set_display_width" getter="get_display_width" default="14.5"> The width of the display in centimeters. </member> - <member name="iod" type="float" setter="set_iod" getter="get_iod"> + <member name="eye_height" type="float" setter="set_eye_height" getter="get_eye_height" default="1.85"> + The height at which the camera is placed in relation to the ground (i.e. [ARVROrigin] node). + </member> + <member name="iod" type="float" setter="set_iod" getter="get_iod" default="6.0"> The interocular distance, also known as the interpupillary distance. The distance between the pupils of the left and right eye. </member> - <member name="k1" type="float" setter="set_k1" getter="get_k1"> + <member name="k1" type="float" setter="set_k1" getter="get_k1" default="0.215"> The k1 lens factor is one of the two constants that define the strength of the lens used and directly influences the lens distortion effect. </member> - <member name="k2" type="float" setter="set_k2" getter="get_k2"> + <member name="k2" type="float" setter="set_k2" getter="get_k2" default="0.215"> The k2 lens factor, see k1. </member> - <member name="oversample" type="float" setter="set_oversample" getter="get_oversample"> + <member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5"> The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance. </member> </members> diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 87e4ddd900..fe107d3683 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* mobile_interface.cpp */ +/* mobile_vr_interface.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #include "mobile_vr_interface.h" #include "core/os/input.h" #include "core/os/os.h" -#include "servers/visual/visual_server_global.h" +#include "servers/visual/visual_server_globals.h" StringName MobileVRInterface::get_name() const { return "Native mobile"; @@ -200,6 +200,9 @@ void MobileVRInterface::set_position_from_sensors() { }; void MobileVRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_eye_height", "eye_height"), &MobileVRInterface::set_eye_height); + ClassDB::bind_method(D_METHOD("get_eye_height"), &MobileVRInterface::get_eye_height); + ClassDB::bind_method(D_METHOD("set_iod", "iod"), &MobileVRInterface::set_iod); ClassDB::bind_method(D_METHOD("get_iod"), &MobileVRInterface::get_iod); @@ -218,6 +221,7 @@ void MobileVRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_k2", "k"), &MobileVRInterface::set_k2); ClassDB::bind_method(D_METHOD("get_k2"), &MobileVRInterface::get_k2); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "eye_height", PROPERTY_HINT_RANGE, "0.0,3.0,0.1"), "set_eye_height", "get_eye_height"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens"); @@ -226,6 +230,14 @@ void MobileVRInterface::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2"); } +void MobileVRInterface::set_eye_height(const real_t p_eye_height) { + eye_height = p_eye_height; +} + +real_t MobileVRInterface::get_eye_height() const { + return eye_height; +} + void MobileVRInterface::set_iod(const real_t p_iod) { intraocular_dist = p_iod; }; @@ -328,6 +340,7 @@ Size2 MobileVRInterface::get_render_targetsize() { // we use half our window size Size2 target_size = OS::get_singleton()->get_window_size(); + target_size.x *= 0.5 * oversample; target_size.y *= oversample; @@ -427,6 +440,12 @@ void MobileVRInterface::process() { }; }; +void MobileVRInterface::notification(int p_what){ + _THREAD_SAFE_METHOD_ + + // nothing to do here, I guess we could pauze our sensors... +} + MobileVRInterface::MobileVRInterface() { initialized = false; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index 05b6331f94..7fa51eecb7 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* mobile_interface.h */ +/* mobile_vr_interface.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -107,6 +107,9 @@ protected: static void _bind_methods(); public: + void set_eye_height(const real_t p_eye_height); + real_t get_eye_height() const; + void set_iod(const real_t p_iod); real_t get_iod() const; @@ -139,6 +142,7 @@ public: virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); + virtual void notification(int p_what); MobileVRInterface(); ~MobileVRInterface(); diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index edddaa87e1..2ff7987cae 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h index 621f8dea80..340a7a4b2a 100644 --- a/modules/mobile_vr/register_types.h +++ b/modules/mobile_vr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/SCsub b/modules/mono/SCsub index e1f5e2ef28..cc60e64a11 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -1,74 +1,13 @@ #!/usr/bin/env python +import build_scripts.tls_configure as tls_configure +import build_scripts.mono_configure as mono_configure + Import('env') Import('env_modules') env_mono = env_modules.Clone() -# TODO move functions to their own modules - -def make_cs_files_header(src, dst, version_dst): - from compat import byte_to_str - - with open(dst, 'w') as header: - header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - header.write('#ifndef CS_COMPRESSED_H\n') - header.write('#define CS_COMPRESSED_H\n\n') - header.write('#ifdef TOOLS_ENABLED\n\n') - header.write('#include "core/map.h"\n') - header.write('#include "core/ustring.h"\n') - inserted_files = '' - import os - latest_mtime = 0 - cs_file_count = 0 - for root, _, files in os.walk(src): - files = [f for f in files if f.endswith('.cs')] - for file in files: - cs_file_count += 1 - filepath = os.path.join(root, file) - filepath_src_rel = os.path.relpath(filepath, src) - mtime = os.path.getmtime(filepath) - latest_mtime = mtime if mtime > latest_mtime else latest_mtime - with open(filepath, 'rb') as f: - buf = f.read() - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - name = str(cs_file_count) - header.write('\n') - header.write('// ' + filepath_src_rel + '\n') - header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') - header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') - header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') - for i, buf_idx in enumerate(range(len(buf))): - if i > 0: - header.write(', ') - header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ - 'CompressedFile(_cs_' + name + '_compressed_size, ' \ - '_cs_' + name + '_uncompressed_size, ' \ - '_cs_' + name + '_compressed));\n' - header.write(' };\n') - header.write('\nstruct CompressedFile\n' '{\n' - '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' - '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' - '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' - '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' - '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' - ) - header.write('\n#endif // TOOLS_ENABLED\n') - header.write('\n#endif // CS_COMPRESSED_H\n') - - glue_version = int(latest_mtime) # The latest modified time will do for now - - with open(version_dst, 'w') as version_header: - version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') - version_header.write('#ifndef CS_GLUE_VERSION_H\n') - version_header.write('#define CS_GLUE_VERSION_H\n\n') - version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') - version_header.write('\n#endif // CS_GLUE_VERSION_H\n') - - env_mono.add_source_files(env.modules_sources, '*.cpp') env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp') @@ -77,281 +16,49 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp') if env['tools']: env_mono.add_source_files(env.modules_sources, 'editor/*.cpp') # NOTE: It is safe to generate this file here, since this is still executed serially - make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h') - -vars = Variables() -vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) -vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) -vars.Update(env_mono) + import build_scripts.make_cs_compressed_header as make_cs_compressed_header + make_cs_compressed_header.generate_header( + 'glue/Managed/Files', + 'glue/cs_compressed.gen.h', + 'glue/cs_glue_version.gen.h' + ) # Glue sources if env_mono['mono_glue']: env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) -# Configure TLS checks - -import tls_configure -conf = Configure(env_mono) -tls_configure.configure(conf) -env_mono = conf.Finish() - - -# Build GodotSharpTools solution - - -import os - - -def find_nuget_unix(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' - - return None - - -def find_nuget_windows(): - import os - - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - - if mono_root: - mono_bin_dir = os.path.join(mono_root, 'bin') - nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') - - if os.path.isfile(nuget_mono): - return nuget_mono - - # Standalone NuGet - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - - return None - - -def find_msbuild_unix(filename): import os.path - import sys - - hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == 'darwin': - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs - - for hint_dir in hint_dirs: - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path): - return hint_path - elif os.path.isfile(hint_path + '.exe'): - return hint_path + '.exe' - - for hint_dir in os.environ['PATH'].split(os.pathsep): - hint_dir = hint_dir.strip('"') - hint_path = os.path.join(hint_dir, filename) - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): - return hint_path + '.exe' - - return None - - -def find_msbuild_windows(): - import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) + if not os.path.isfile('glue/mono_glue.gen.cpp'): + raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?") - if not mono_root: - raise RuntimeError('Cannot find mono root directory') +if env_mono['tools'] or env_mono['target'] != 'release': + env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD']) - framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_bin_dir = os.path.join(mono_root, 'bin') - msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') +# Configure Thread Local Storage - if os.path.isfile(msbuild_mono): - # The (Csc/Vbc/Fsc)ToolExe environment variables are required when - # building with Mono's MSBuild. They must point to the batch files - # in Mono's bin directory to make sure they are executed with Mono. - mono_msbuild_env = { - 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), - 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), - 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') - } - return (msbuild_mono, framework_path, mono_msbuild_env) - - msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() - - if msbuild_tools_path: - return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) - - return None +conf = Configure(env_mono) +tls_configure.configure(conf) +env_mono = conf.Finish() +# Configure Mono -def mono_build_solution(source, target, env): - import subprocess - import mono_reg_utils as monoreg - from shutil import copyfile +mono_configure.configure(env, env_mono) - sln_path = os.path.abspath(str(source[0])) - target_path = os.path.abspath(str(target[0])) +# Build Godot API solution - framework_path = '' - msbuild_env = os.environ.copy() +if env_mono['tools'] and env_mono['mono_glue']: + import build_scripts.api_solution_build as api_solution_build + api_solution_build.build(env_mono) - # Needed when running from Developer Command Prompt for VS - if 'PLATFORM' in msbuild_env: - del msbuild_env['PLATFORM'] +# Build GodotTools - # Find MSBuild - if os.name == 'nt': - msbuild_info = find_msbuild_windows() - if msbuild_info is None: - raise RuntimeError('Cannot find MSBuild executable') - msbuild_path = msbuild_info[0] - framework_path = msbuild_info[1] - msbuild_env.update(msbuild_info[2]) +if env_mono['tools']: + import build_scripts.godot_tools_build as godot_tools_build + if env_mono['mono_glue']: + godot_tools_build.build(env_mono) else: - msbuild_path = find_msbuild_unix('msbuild') - if msbuild_path is None: - xbuild_fallback = env['xbuild_fallback'] - - if xbuild_fallback and os.name == 'nt': - print('Option \'xbuild_fallback\' not supported on Windows') - xbuild_fallback = False - - if xbuild_fallback: - print('Cannot find MSBuild executable, trying with xbuild') - print('Warning: xbuild is deprecated') - - msbuild_path = find_msbuild_unix('xbuild') - - if msbuild_path is None: - raise RuntimeError('Cannot find xbuild executable') - else: - raise RuntimeError('Cannot find MSBuild executable') - - print('MSBuild path: ' + msbuild_path) - - # Find NuGet - nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() - if nuget_path is None: - raise RuntimeError('Cannot find NuGet executable') - - print('NuGet path: ' + nuget_path) - - # Do NuGet restore - - try: - subprocess.check_call([nuget_path, 'restore', sln_path]) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: NuGet restore failed') - - # Build solution - - build_config = 'Release' - - msbuild_args = [ - msbuild_path, - sln_path, - '/p:Configuration=' + build_config, - ] - - if framework_path: - msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] - - try: - subprocess.check_call(msbuild_args, env=msbuild_env) - except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools: Build failed') - - # Copy files - - src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) - asm_file = 'GodotSharpTools.dll' - - if not os.path.isdir(dst_dir): - if os.path.exists(dst_dir): - raise RuntimeError('Target directory is a file') - os.makedirs(dst_dir) - - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) - - # Dependencies - copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) - -if env['tools']: - output_dir = Dir('#bin').abspath - editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') - - mono_sln_builder = Builder(action=mono_build_solution) - env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) - env_mono.MonoBuildSolution( - os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), - 'editor/GodotSharpTools/GodotSharpTools.sln' - ) + # Building without the glue sources so the Godot API solution may be missing. + # GodotTools depends on the Godot API solution. As such, we will only build + # GodotTools.ProjectEditor which doesn't depend on the Godot API solution and + # is required by the bindings generator in order to be able to generated it. + godot_tools_build.build_project_editor_only(env_mono) diff --git a/modules/mono/__init__.py b/modules/mono/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/mono/__init__.py diff --git a/modules/mono/build_scripts/__init__.py b/modules/mono/build_scripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/mono/build_scripts/__init__.py diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py new file mode 100644 index 0000000000..1fe00a3028 --- /dev/null +++ b/modules/mono/build_scripts/api_solution_build.py @@ -0,0 +1,66 @@ +# Build the Godot API solution + +import os + +from SCons.Script import Dir + + +def build_api_solution(source, target, env): + # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str + + module_dir = env['module_dir'] + + solution_path = os.path.join(module_dir, 'glue/Managed/Generated/GodotSharp.sln') + + if not os.path.isfile(solution_path): + raise RuntimeError("Godot API solution not found. Did you forget to run '--generate-mono-glue'?") + + build_config = env['solution_build_config'] + + extra_msbuild_args = ['/p:NoWarn=1591'] # Ignore missing documentation warnings + + from .solution_builder import build_solution + build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args) + + # Copy targets + + core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharp', 'bin', build_config)) + editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharpEditor', 'bin', build_config)) + + dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + + if not os.path.isdir(dst_dir): + assert not os.path.isfile(dst_dir) + os.makedirs(dst_dir) + + def copy_target(target_path): + from shutil import copy + filename = os.path.basename(target_path) + + src_path = os.path.join(core_src_dir, filename) + if not os.path.isfile(src_path): + src_path = os.path.join(editor_src_dir, filename) + + copy(src_path, target_path) + + for scons_target in target: + copy_target(str(scons_target)) + + +def build(env_mono): + assert env_mono['tools'] + + target_filenames = [ + 'GodotSharp.dll', 'GodotSharp.pdb', 'GodotSharp.xml', + 'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml' + ] + + for build_config in ['Debug', 'Release']: + output_dir = Dir('#bin').abspath + editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config) + + targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames] + + cmd = env_mono.CommandNoCache(targets, [], build_api_solution, + module_dir=os.getcwd(), solution_build_config=build_config) + env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py new file mode 100644 index 0000000000..c47cfc8a38 --- /dev/null +++ b/modules/mono/build_scripts/godot_tools_build.py @@ -0,0 +1,112 @@ +# Build GodotTools solution + +import os + +from SCons.Script import Dir + + +def build_godot_tools(source, target, env): + # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str + + module_dir = env['module_dir'] + + solution_path = os.path.join(module_dir, 'editor/GodotTools/GodotTools.sln') + build_config = 'Debug' if env['target'] == 'debug' else 'Release' + + from . solution_builder import build_solution, nuget_restore + nuget_restore(env, solution_path) + build_solution(env, solution_path, build_config) + + # Copy targets + + solution_dir = os.path.abspath(os.path.join(solution_path, os.pardir)) + + src_dir = os.path.join(solution_dir, 'GodotTools', 'bin', build_config) + dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + + if not os.path.isdir(dst_dir): + assert not os.path.isfile(dst_dir) + os.makedirs(dst_dir) + + def copy_target(target_path): + from shutil import copy + filename = os.path.basename(target_path) + copy(os.path.join(src_dir, filename), target_path) + + for scons_target in target: + copy_target(str(scons_target)) + + +def build_godot_tools_project_editor(source, target, env): + # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str + + module_dir = env['module_dir'] + + project_name = 'GodotTools.ProjectEditor' + + csproj_dir = os.path.join(module_dir, 'editor/GodotTools', project_name) + csproj_path = os.path.join(csproj_dir, project_name + '.csproj') + build_config = 'Debug' if env['target'] == 'debug' else 'Release' + + from . solution_builder import build_solution, nuget_restore + + # Make sure to restore NuGet packages in the project directory for the project to find it + nuget_restore(env, os.path.join(csproj_dir, 'packages.config'), '-PackagesDirectory', + os.path.join(csproj_dir, 'packages')) + + build_solution(env, csproj_path, build_config) + + # Copy targets + + src_dir = os.path.join(csproj_dir, 'bin', build_config) + dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + + if not os.path.isdir(dst_dir): + assert not os.path.isfile(dst_dir) + os.makedirs(dst_dir) + + def copy_target(target_path): + from shutil import copy + filename = os.path.basename(target_path) + copy(os.path.join(src_dir, filename), target_path) + + for scons_target in target: + copy_target(str(scons_target)) + + +def build(env_mono): + assert env_mono['tools'] + + output_dir = Dir('#bin').abspath + editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') + editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug') + + source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll'] + sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames] + + target_filenames = ['GodotTools.dll', 'GodotTools.BuildLogger.dll', 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll'] + + if env_mono['target'] == 'debug': + target_filenames += ['GodotTools.pdb', 'GodotTools.BuildLogger.pdb', 'GodotTools.ProjectEditor.pdb', 'GodotTools.Core.pdb'] + + targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames] + + cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd()) + env_mono.AlwaysBuild(cmd) + + +def build_project_editor_only(env_mono): + assert env_mono['tools'] + + output_dir = Dir('#bin').abspath + editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') + + target_filenames = ['GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll'] + + if env_mono['target'] == 'debug': + target_filenames += ['GodotTools.ProjectEditor.pdb', 'GodotTools.Core.pdb'] + + targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames] + + cmd = env_mono.CommandNoCache(targets, [], build_godot_tools_project_editor, module_dir=os.getcwd()) + env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py new file mode 100644 index 0000000000..cd9210897d --- /dev/null +++ b/modules/mono/build_scripts/make_android_mono_config.py @@ -0,0 +1,69 @@ + +def generate_compressed_config(config_src, output_dir): + import os.path + from compat import byte_to_str + + # Header file + with open(os.path.join(output_dir, 'android_mono_config.gen.h'), 'w') as header: + header.write('''/* THIS FILE IS GENERATED DO NOT EDIT */ +#ifndef ANDROID_MONO_CONFIG_GEN_H +#define ANDROID_MONO_CONFIG_GEN_H + +#ifdef ANDROID_ENABLED + +#include "core/ustring.h" + +String get_godot_android_mono_config(); + +#endif // ANDROID_ENABLED + +#endif // ANDROID_MONO_CONFIG_GEN_H +''') + + # Source file + with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp: + with open(config_src, 'rb') as f: + buf = f.read() + decompr_size = len(buf) + import zlib + buf = zlib.compress(buf) + compr_size = len(buf) + + bytes_seq_str = '' + for i, buf_idx in enumerate(range(compr_size)): + if i > 0: + bytes_seq_str += ', ' + bytes_seq_str += byte_to_str(buf[buf_idx]) + + cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */ +#include "android_mono_config.gen.h" + +#ifdef ANDROID_ENABLED + +#include "core/io/compression.h" +#include "core/pool_vector.h" + +namespace { + +// config +static const int config_compressed_size = %d; +static const int config_uncompressed_size = %d; +static const unsigned char config_compressed_data[] = { %s }; + +} // namespace + +String get_godot_android_mono_config() { + PoolVector<uint8_t> data; + data.resize(config_uncompressed_size); + PoolVector<uint8_t>::Write w = data.write(); + Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data, + config_compressed_size, Compression::MODE_DEFLATE); + String s; + if (s.parse_utf8((const char *)w.ptr(), data.size())) { + ERR_FAIL_V(String()); + } + return s; +} + +#endif // ANDROID_ENABLED +''' % (compr_size, decompr_size, bytes_seq_str)) diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py new file mode 100644 index 0000000000..ed49db5bb2 --- /dev/null +++ b/modules/mono/build_scripts/make_cs_compressed_header.py @@ -0,0 +1,62 @@ + +def generate_header(src, dst, version_dst): + from compat import byte_to_str + + with open(dst, 'w') as header: + header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + header.write('#ifndef CS_COMPRESSED_H\n') + header.write('#define CS_COMPRESSED_H\n\n') + header.write('#ifdef TOOLS_ENABLED\n\n') + header.write('#include "core/map.h"\n') + header.write('#include "core/ustring.h"\n') + inserted_files = '' + import os + latest_mtime = 0 + cs_file_count = 0 + for root, _, files in os.walk(src): + files = [f for f in files if f.endswith('.cs')] + for file in files: + cs_file_count += 1 + filepath = os.path.join(root, file) + filepath_src_rel = os.path.relpath(filepath, src) + mtime = os.path.getmtime(filepath) + latest_mtime = mtime if mtime > latest_mtime else latest_mtime + with open(filepath, 'rb') as f: + buf = f.read() + decompr_size = len(buf) + import zlib + buf = zlib.compress(buf) + compr_size = len(buf) + name = str(cs_file_count) + header.write('\n') + header.write('// ' + filepath_src_rel + '\n') + header.write('static const int _cs_' + name + '_compressed_size = ' + str(compr_size) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decompr_size) + ';\n') + header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') + for i, buf_idx in enumerate(range(compr_size)): + if i > 0: + header.write(', ') + header.write(byte_to_str(buf[buf_idx])) + header.write(' };\n') + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ + 'GodotCsCompressedFile(_cs_' + name + '_compressed_size, ' \ + '_cs_' + name + '_uncompressed_size, ' \ + '_cs_' + name + '_compressed));\n' + header.write('\nstruct GodotCsCompressedFile\n' '{\n' + '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' + '\n\tGodotCsCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tGodotCsCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map<String, GodotCsCompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' + ) + header.write('\n#endif // TOOLS_ENABLED\n') + header.write('\n#endif // CS_COMPRESSED_H\n') + + glue_version = int(latest_mtime) # The latest modified time will do for now + + with open(version_dst, 'w') as version_header: + version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + version_header.write('#ifndef CS_GLUE_VERSION_H\n') + version_header.write('#define CS_GLUE_VERSION_H\n\n') + version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + version_header.write('\n#endif // CS_GLUE_VERSION_H\n') diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py new file mode 100644 index 0000000000..9f0eb58896 --- /dev/null +++ b/modules/mono/build_scripts/mono_configure.py @@ -0,0 +1,407 @@ +import os +import os.path +import sys +import subprocess + +from SCons.Script import Dir, Environment + +if os.name == 'nt': + from . import mono_reg_utils as monoreg + + +android_arch_dirs = { + 'armv7': 'armeabi-v7a', + 'arm64v8': 'arm64-v8a', + 'x86': 'x86', + 'x86_64': 'x86_64' +} + + +def get_android_out_dir(env): + return os.path.join(Dir('#platform/android/java/libs').abspath, + 'release' if env['target'] == 'release' else 'debug', + android_arch_dirs[env['android_arch']]) + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + +def copy_file(src_dir, dst_dir, name): + from shutil import copy + + src_path = os.path.join(Dir(src_dir).abspath, name) + dst_dir = Dir(dst_dir).abspath + + if not os.path.isdir(dst_dir): + os.makedirs(dst_dir) + + copy(src_path, dst_dir) + + +def configure(env, env_mono): + bits = env['bits'] + is_android = env['platform'] == 'android' + + tools_enabled = env['tools'] + mono_static = env['mono_static'] + copy_mono_root = env['copy_mono_root'] + + mono_prefix = env['mono_prefix'] + + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + + is_travis = os.environ.get('TRAVIS') == 'true' + + if is_travis: + # Travis CI may have a Mono version lower than 5.12 + env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS']) + + if is_android and not env['android_arch'] in android_arch_dirs: + raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch']) + + if is_android and tools_enabled: + # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders. + raise RuntimeError('This module does not currently support building for android with tools enabled') + + if is_android and mono_static: + # When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0 + raise RuntimeError('Linking Mono statically is not currently supported on Android') + + if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix: + print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead") + + if env['platform'] == 'windows': + mono_root = mono_prefix + + if not mono_root and os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) + + if not mono_root: + raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter") + + print('Found Mono root directory: ' + mono_root) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + + if env.msvc: + mono_static_lib_name = 'libmono-static-sgen' + else: + mono_static_lib_name = 'libmonosgen-2.0' + + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) + + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) + + env.Append(LIBS='psapi') + env.Append(LIBS='version') + else: + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') + + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + + copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll') + else: + is_apple = (sys.platform == 'darwin' or "osxcross" in env) + + sharedlib_ext = '.dylib' if is_apple else '.so' + + mono_root = mono_prefix + mono_lib_path = '' + mono_so_name = '' + + if not mono_root and is_android: + raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter") + + if not mono_root and is_apple: + # Try with some known directories under OSX + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] + for hint_dir in hint_dirs: + if os.path.isdir(hint_dir): + mono_root = hint_dir + break + + # We can't use pkg-config to link mono statically, + # but we can still use it to find the mono root directory + if not mono_root and mono_static: + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) + if not mono_root: + raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \ + "specify one manually with the 'mono_prefix' SCons parameter") + + if mono_root: + print('Found Mono root directory: ' + mono_root) + + mono_lib_path = os.path.join(mono_root, 'lib') + + env.Append(LIBPATH=mono_lib_path) + env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + + mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') + + if not mono_lib: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + env_mono.Append(CPPDEFINES=['_REENTRANT']) + + if mono_static: + mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') + + if is_apple: + env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) + else: + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) + else: + env.Append(LIBS=[mono_lib]) + + if is_apple: + env.Append(LIBS=['iconv', 'pthread']) + elif is_android: + pass # Nothing + else: + env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) + + if not mono_static: + mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) + + copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext) + else: + assert not mono_static + + # TODO: Add option to force using pkg-config + print('Mono root directory not found. Using pkg-config instead') + + env.ParseConfig('pkg-config monosgen-2 --libs') + env_mono.ParseConfig('pkg-config monosgen-2 --cflags') + + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found: + mono_lib_path = hint_dir + mono_so_name = name_found + break + + if not mono_so_name: + raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) + + if not mono_static: + libs_output_dir = get_android_out_dir(env) if is_android else '#bin' + copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext) + + env.Append(LINKFLAGS='-rdynamic') + + if not tools_enabled and not is_android: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + make_template_dir(env, mono_root) + elif not tools_enabled and is_android: + # Compress Android Mono Config + from . import make_android_mono_config + config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config') + make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/') + + # Copy the required shared libraries + copy_mono_shared_libs(env, mono_root, None) + + if copy_mono_root: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + if tools_enabled: + copy_mono_root_files(env, mono_root) + else: + print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") + + +def make_template_dir(env, mono_root): + from shutil import rmtree + + platform = env['platform'] + target = env['target'] + + template_dir_name = '' + + if platform in ['windows', 'osx', 'x11', 'android']: + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) + else: + assert False + + output_dir = Dir('#bin').abspath + template_dir = os.path.join(output_dir, template_dir_name) + + template_mono_root_dir = os.path.join(template_dir, 'Mono') + + if os.path.isdir(template_mono_root_dir): + rmtree(template_mono_root_dir) # Clean first + + # Copy etc/mono/ + + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(env, mono_root, template_mono_root_dir) + + +def copy_mono_root_files(env, mono_root): + from glob import glob + from shutil import copy + from shutil import rmtree + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + output_dir = Dir('#bin').abspath + editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') + + if os.path.isdir(editor_mono_root_dir): + rmtree(editor_mono_root_dir) # Clean first + + # Copy etc/mono/ + + editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(env, mono_root, editor_mono_root_dir) + + # Copy framework assemblies + + mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') + + editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') + editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') + + if not os.path.isdir(editor_mono_framework_dir): + os.makedirs(editor_mono_framework_dir) + if not os.path.isdir(editor_mono_framework_facades_dir): + os.makedirs(editor_mono_framework_facades_dir) + + for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): + copy(assembly, editor_mono_framework_dir) + for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): + copy(assembly, editor_mono_framework_facades_dir) + + +def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): + from distutils.dir_util import copy_tree + from glob import glob + from shutil import copy + + if not os.path.isdir(target_mono_config_dir): + os.makedirs(target_mono_config_dir) + + mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') + if not os.path.isdir(mono_etc_dir): + mono_etc_dir = '' + etc_hint_dirs = [] + if platform != 'windows': + etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] + if 'MONO_CFG_DIR' in os.environ: + etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] + for etc_hint_dir in etc_hint_dirs: + if os.path.isdir(etc_hint_dir): + mono_etc_dir = etc_hint_dir + break + if not mono_etc_dir: + raise RuntimeError('Mono installation etc directory not found') + + copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) + copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) + copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) + if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')): + copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) + + for file in glob(os.path.join(mono_etc_dir, '*')): + if os.path.isfile(file): + copy(file, target_mono_config_dir) + + +def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): + from shutil import copy + + def copy_if_exists(src, dst): + if os.path.isfile(src): + copy(src, dst) + + platform = env['platform'] + + if platform == 'windows': + target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') + + if not os.path.isdir(target_mono_bin_dir): + os.makedirs(target_mono_bin_dir) + + copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir) + else: + target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib') + + if not os.path.isdir(target_mono_lib_dir): + os.makedirs(target_mono_lib_dir) + + if platform == 'osx': + # TODO: Make sure nothing is missing + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir) + elif platform == 'x11' or platform == 'android': + lib_file_names = [lib_name + '.so' for lib_name in [ + 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper', + 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW' + ]] + + for lib_file_name in lib_file_names: + copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir) + + +def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): + tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) + tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') + for hint_dir in tmpenv['LIBPATH']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): + return os.path.join(hint_dir, '..') + return '' diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index c8ebb54ded..b2c48f0a61 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py @@ -40,7 +40,7 @@ def _reg_open_key_bits(key, subkey, bits): def _find_mono_in_reg(subkey, bits): try: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot') + value = winreg.QueryValueEx(hKey, 'SdkInstallRoot')[0] return value except (WindowsError, OSError): return None @@ -49,7 +49,7 @@ def _find_mono_in_reg(subkey, bits): def _find_mono_in_reg_old(subkey, bits): try: with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR') + default_clr = winreg.QueryValueEx(hKey, 'DefaultCLR')[0] if default_clr: return _find_mono_in_reg(subkey + '\\' + default_clr, bits) return None @@ -91,7 +91,13 @@ def find_msbuild_tools_path_reg(): if not val: raise ValueError('Value of `installationPath` entry is empty') - return os.path.join(val, "MSBuild\\15.0\\Bin") + # Since VS2019, the directory is simply named "Current" + msbuild_dir = os.path.join(val, 'MSBuild\\Current\\Bin') + if os.path.isdir(msbuild_dir): + return msbuild_dir + + # Directory name "15.0" is used in VS 2017 + return os.path.join(val, 'MSBuild\\15.0\\Bin') raise ValueError('Cannot find `installationPath` entry') except ValueError as e: @@ -106,9 +112,7 @@ def find_msbuild_tools_path_reg(): try: subkey = r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0' with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: - value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath') + value = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')[0] return value except (WindowsError, OSError): return '' - - return '' diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff new file mode 100644 index 0000000000..05f8dcadcc --- /dev/null +++ b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff @@ -0,0 +1,70 @@ +diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h +index e2bdf13ac3e..f962200ba4e 100644 +--- a/libgc/include/private/gcconfig.h ++++ b/libgc/include/private/gcconfig.h +@@ -2255,6 +2255,14 @@ + # define GETPAGESIZE() getpagesize() + # endif + ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID ++#endif ++ + # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. Probably others also qualify. */ + # define SVR4 +diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c +index f93ce26b562..4a49a6d578c 100644 +--- a/libgc/pthread_stop_world.c ++++ b/libgc/pthread_stop_world.c +@@ -336,7 +336,7 @@ void GC_push_all_stacks() + pthread_t GC_stopping_thread; + int GC_stopping_pid; + +-#ifdef HOST_ANDROID ++#ifdef USE_TKILL_ON_ANDROID + static + int android_thread_kill(pid_t tid, int sig) + { +diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c +index ad9b8823f8f..3542b32b540 100644 +--- a/mono/metadata/threads.c ++++ b/mono/metadata/threads.c +@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); + #include <zircon/syscalls.h> + #endif + +-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) +-#define USE_TKILL_ON_ANDROID 1 ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID + #endif + + #ifdef HOST_ANDROID +diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c +index 3e4bf93de5f..79c9f731fe7 100644 +--- a/mono/utils/mono-threads-posix.c ++++ b/mono/utils/mono-threads-posix.c +@@ -31,8 +31,12 @@ + + #include <errno.h> + +-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) +-#define USE_TKILL_ON_ANDROID 1 ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID + #endif + + #ifdef USE_TKILL_ON_ANDROID diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py new file mode 100644 index 0000000000..d1529a64d2 --- /dev/null +++ b/modules/mono/build_scripts/solution_builder.py @@ -0,0 +1,214 @@ + +import os + + +verbose = False + + +def find_nuget_unix(): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(env): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + from . mono_reg_utils import find_mono_root_dir + + mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits']) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + +def find_msbuild_unix(filename): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, filename) + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_msbuild_windows(env): + from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg + + mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits']) + + if not mono_root: + raise RuntimeError('Cannot find mono root directory') + + mono_bin_dir = os.path.join(mono_root, 'bin') + msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') + + msbuild_tools_path = find_msbuild_tools_path_reg() + + if msbuild_tools_path: + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), {}) + + if os.path.isfile(msbuild_mono): + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. + mono_msbuild_env = { + 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), + 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), + 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') + } + return (msbuild_mono, mono_msbuild_env) + + return None + + +def run_command(command, args, env_override=None, name=None): + def cmd_args_to_str(cmd_args): + return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args]) + + args = [command] + args + + if name is None: + name = os.path.basename(command) + + if verbose: + print("Running '%s': %s" % (name, cmd_args_to_str(args))) + + import subprocess + try: + if env_override is None: + subprocess.check_call(args) + else: + subprocess.check_call(args, env=env_override) + except subprocess.CalledProcessError as e: + raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode)) + + +def nuget_restore(env, *args): + global verbose + verbose = env['verbose'] + + # Find NuGet + nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + run_command(nuget_path, ['restore'] + list(args), name='nuget restore') + + +def build_solution(env, solution_path, build_config, extra_msbuild_args=[]): + global verbose + verbose = env['verbose'] + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] + + # Find MSBuild + if os.name == 'nt': + msbuild_info = find_msbuild_windows(env) + if msbuild_info is None: + raise RuntimeError('Cannot find MSBuild executable') + msbuild_path = msbuild_info[0] + msbuild_env.update(msbuild_info[1]) + else: + msbuild_path = find_msbuild_unix('msbuild') + if msbuild_path is None: + xbuild_fallback = env['xbuild_fallback'] + + if xbuild_fallback and os.name == 'nt': + print('Option \'xbuild_fallback\' not supported on Windows') + xbuild_fallback = False + + if xbuild_fallback: + print('Cannot find MSBuild executable, trying with xbuild') + print('Warning: xbuild is deprecated') + + msbuild_path = find_msbuild_unix('xbuild') + + if msbuild_path is None: + raise RuntimeError('Cannot find xbuild executable') + else: + raise RuntimeError('Cannot find MSBuild executable') + + print('MSBuild path: ' + msbuild_path) + + # Build solution + + msbuild_args = [solution_path, '/p:Configuration=' + build_config] + msbuild_args += extra_msbuild_args + + run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild') diff --git a/modules/mono/tls_configure.py b/modules/mono/build_scripts/tls_configure.py index 622280b00b..622280b00b 100644 --- a/modules/mono/tls_configure.py +++ b/modules/mono/build_scripts/tls_configure.py diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp new file mode 100644 index 0000000000..71ccdb7aab --- /dev/null +++ b/modules/mono/class_db_api_json.cpp @@ -0,0 +1,242 @@ +/*************************************************************************/ +/* class_db_api_json.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "class_db_api_json.h" + +#include "core/io/json.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/version.h" + +void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { + Dictionary classes_dict; + + List<StringName> names; + + const StringName *k = NULL; + + while ((k = ClassDB::classes.next(k))) { + + names.push_back(*k); + } + //must be alphabetically sorted for hash to compute + names.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = names.front(); E; E = E->next()) { + + ClassDB::ClassInfo *t = ClassDB::classes.getptr(E->get()); + ERR_FAIL_COND(!t); + if (t->api != p_api || !t->exposed) + continue; + + Dictionary class_dict; + classes_dict[t->name] = class_dict; + + class_dict["inherits"] = t->inherits; + + { //methods + + List<StringName> snames; + + k = NULL; + + while ((k = t->method_map.next(k))) { + + snames.push_back(*k); + } + + snames.sort_custom<StringName::AlphCompare>(); + + Array methods; + + for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { + Dictionary method_dict; + methods.push_back(method_dict); + + MethodBind *mb = t->method_map[F->get()]; + method_dict["name"] = mb->get_name(); + method_dict["argument_count"] = mb->get_argument_count(); + method_dict["return_type"] = mb->get_argument_type(-1); + + Array arguments; + method_dict["arguments"] = arguments; + + for (int i = 0; i < mb->get_argument_count(); i++) { + Dictionary argument_dict; + arguments.push_back(argument_dict); + const PropertyInfo info = mb->get_argument_info(i); + argument_dict["type"] = info.type; + argument_dict["name"] = info.name; + argument_dict["hint"] = info.hint; + argument_dict["hint_string"] = info.hint_string; + } + + method_dict["default_argument_count"] = mb->get_default_argument_count(); + + Array default_arguments; + method_dict["default_arguments"] = default_arguments; + + for (int i = 0; i < mb->get_default_argument_count(); i++) { + Dictionary default_argument_dict; + default_arguments.push_back(default_argument_dict); + //hash should not change, i hope for tis + Variant da = mb->get_default_argument(i); + default_argument_dict["value"] = da; + } + + method_dict["hint_flags"] = mb->get_hint_flags(); + } + + if (!methods.empty()) { + class_dict["methods"] = methods; + } + } + + { //constants + + List<StringName> snames; + + k = NULL; + + while ((k = t->constant_map.next(k))) { + + snames.push_back(*k); + } + + snames.sort_custom<StringName::AlphCompare>(); + + Array constants; + + for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { + Dictionary constant_dict; + constants.push_back(constant_dict); + + constant_dict["name"] = F->get(); + constant_dict["value"] = t->constant_map[F->get()]; + } + + if (!constants.empty()) { + class_dict["constants"] = constants; + } + } + + { //signals + + List<StringName> snames; + + k = NULL; + + while ((k = t->signal_map.next(k))) { + + snames.push_back(*k); + } + + snames.sort_custom<StringName::AlphCompare>(); + + Array signals; + + for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { + Dictionary signal_dict; + signals.push_back(signal_dict); + + MethodInfo &mi = t->signal_map[F->get()]; + signal_dict["name"] = F->get(); + + Array arguments; + signal_dict["arguments"] = arguments; + for (int i = 0; i < mi.arguments.size(); i++) { + Dictionary argument_dict; + arguments.push_back(argument_dict); + argument_dict["type"] = mi.arguments[i].type; + } + } + + if (!signals.empty()) { + class_dict["signals"] = signals; + } + } + + { //properties + + List<StringName> snames; + + k = NULL; + + while ((k = t->property_setget.next(k))) { + + snames.push_back(*k); + } + + snames.sort_custom<StringName::AlphCompare>(); + + Array properties; + + for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { + Dictionary property_dict; + properties.push_back(property_dict); + + ClassDB::PropertySetGet *psg = t->property_setget.getptr(F->get()); + + property_dict["name"] = F->get(); + property_dict["setter"] = psg->setter; + property_dict["getter"] = psg->getter; + } + + if (!properties.empty()) { + class_dict["property_setget"] = properties; + } + } + + Array property_list; + + //property list + for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) { + Dictionary property_dict; + property_list.push_back(property_dict); + + property_dict["name"] = F->get().name; + property_dict["type"] = F->get().type; + property_dict["hint"] = F->get().hint; + property_dict["hint_string"] = F->get().hint_string; + property_dict["usage"] = F->get().usage; + } + + if (!property_list.empty()) { + class_dict["property_list"] = property_list; + } + } + + FileAccessRef f = FileAccess::open(p_output_file, FileAccess::WRITE); + ERR_FAIL_COND(!f); + f->store_string(JSON::print(classes_dict, /*indent: */ "\t")); + f->close(); + + print_line(String() + "ClassDB API JSON written to: " + ProjectSettings::get_singleton()->globalize_path(p_output_file)); +} diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/class_db_api_json.h index 64ba0f4037..0aa9c20930 100644 --- a/modules/mono/editor/mono_build_info.h +++ b/modules/mono/class_db_api_json.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* mono_build_info.h */ +/* class_db_api_json.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,28 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MONO_BUILD_INFO_H -#define MONO_BUILD_INFO_H +#ifndef CLASS_DB_API_JSON_H +#define CLASS_DB_API_JSON_H +#include "core/class_db.h" #include "core/ustring.h" -#include "core/vector.h" -struct MonoBuildInfo { +void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api); - struct Hasher { - static uint32_t hash(const MonoBuildInfo &p_key); - }; - - String solution; - String configuration; - Vector<String> custom_props; - - bool operator==(const MonoBuildInfo &p_b) const; - - String get_log_dirpath(); - - MonoBuildInfo(); - MonoBuildInfo(const String &p_solution, const String &p_config); -}; - -#endif // MONO_BUILD_INFO_H +#endif // CLASS_DB_API_JSON_H diff --git a/modules/mono/config.py b/modules/mono/config.py index 189699cca8..9adf4ee6e5 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,438 +1,36 @@ - -import imp -import os -import sys -import subprocess - -from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables - - -monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') - - def can_build(env, platform): if platform in ['javascript']: return False # Not yet supported return True -def is_enabled(): - # The module is disabled by default. Use module_mono_enabled=yes to enable it. - return False - - -def get_doc_classes(): - return [ - '@C#', - 'CSharpScript', - 'GodotSharp', - ] - - -def get_doc_path(): - return 'doc_classes' - - -def find_file_in_dir(directory, files, prefix='', extension=''): - if not extension.startswith('.'): - extension = '.' + extension - for curfile in files: - if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): - return curfile - return '' - - -def copy_file(src_dir, dst_dir, name): - from shutil import copyfile - - src_path = os.path.join(src_dir, name) - dst_path = os.path.join(dst_dir, name) - - if not os.path.isdir(dst_dir): - os.mkdir(dst_dir) - - copyfile(src_path, dst_path) - - def configure(env): env.use_ptrcall = True env.add_module_version_string('mono') + from SCons.Script import BoolVariable, PathVariable, Variables + envvars = Variables() + envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept)) envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) + envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) envvars.Update(env) - bits = env['bits'] - - tools_enabled = env['tools'] - mono_static = env['mono_static'] - copy_mono_root = env['copy_mono_root'] - - mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] - - if env['platform'] == 'windows': - mono_root = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - if mono_static: - lib_suffix = Environment()['LIBSUFFIX'] - - if env.msvc: - mono_static_lib_name = 'libmono-static-sgen' - else: - mono_static_lib_name = 'libmonosgen-2.0' - - if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): - raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) - - env.Append(LINKFLAGS='Mincore' + lib_suffix) - env.Append(LINKFLAGS='msvcrt' + lib_suffix) - env.Append(LINKFLAGS='LIBCMT' + lib_suffix) - env.Append(LINKFLAGS='Psapi' + lib_suffix) - else: - env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) - - env.Append(LIBS='psapi') - env.Append(LIBS='version') - else: - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') - - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) - else: - env.Append(LIBS=mono_lib_name) - - mono_bin_path = os.path.join(mono_root, 'bin') - - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - - copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') - else: - sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so' - - mono_root = '' - mono_lib_path = '' - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - - if not mono_root and sys.platform == 'darwin': - # Try with some known directories under OSX - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] - for hint_dir in hint_dirs: - if os.path.isdir(hint_dir): - mono_root = hint_dir - break - - # We can't use pkg-config to link mono statically, - # but we can still use it to find the mono root directory - if not mono_root and mono_static: - mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) - if not mono_root: - raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') - - if mono_root: - print('Found Mono root directory: ' + mono_root) - - mono_version = mono_root_try_find_mono_version(mono_root) - configure_for_mono_version(env, mono_version) - - mono_lib_path = os.path.join(mono_root, 'lib') - - env.Append(LIBPATH=mono_lib_path) - env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - - mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') - - if not mono_lib: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) - - env.Append(CPPFLAGS=['-D_REENTRANT']) - - if mono_static: - mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') - - if sys.platform == 'darwin': - env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - else: - env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) - else: - env.Append(LIBS=[mono_lib]) - - if sys.platform == 'darwin': - env.Append(LIBS=['iconv', 'pthread']) - else: - env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) - - if not mono_static: - mono_so_name = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension=sharedlib_ext) - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - else: - assert not mono_static - # TODO: Add option to force using pkg-config - print('Mono root directory not found. Using pkg-config instead') - - mono_version = pkgconfig_try_find_mono_version() - configure_for_mono_version(env, mono_version) - - env.ParseConfig('pkg-config monosgen-2 --cflags --libs') - - mono_lib_path = '' - mono_so_name = '' - - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found: - mono_lib_path = hint_dir - mono_so_name = name_found - break - - if not mono_so_name: - raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) - - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - - env.Append(LINKFLAGS='-rdynamic') - - if not tools_enabled: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - make_template_dir(env, mono_root) - - if copy_mono_root: - if not mono_root: - mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() - - if tools_enabled: - copy_mono_root_files(env, mono_root) - else: - print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") - - -def make_template_dir(env, mono_root): - from shutil import rmtree - - platform = env['platform'] - target = env['target'] - - template_dir_name = '' - - if platform in ['windows', 'osx', 'x11']: - template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) - else: - assert False - - output_dir = Dir('#bin').abspath - template_dir = os.path.join(output_dir, template_dir_name) - - template_mono_root_dir = os.path.join(template_dir, 'Mono') - - if os.path.isdir(template_mono_root_dir): - rmtree(template_mono_root_dir) # Clean first - - # Copy etc/mono/ - - template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) - - -def copy_mono_root_files(env, mono_root): - from glob import glob - from shutil import copy - from shutil import rmtree - - if not mono_root: - raise RuntimeError('Mono installation directory not found') - - output_dir = Dir('#bin').abspath - editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') - - if os.path.isdir(editor_mono_root_dir): - rmtree(editor_mono_root_dir) # Clean first - - # Copy etc/mono/ - - editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) - - # Copy the required shared libraries - - copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) - - # Copy framework assemblies - - mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') - - editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') - editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') - - if not os.path.isdir(editor_mono_framework_dir): - os.makedirs(editor_mono_framework_dir) - if not os.path.isdir(editor_mono_framework_facades_dir): - os.makedirs(editor_mono_framework_facades_dir) - - for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): - copy(assembly, editor_mono_framework_dir) - for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): - copy(assembly, editor_mono_framework_facades_dir) - - -def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): - from distutils.dir_util import copy_tree - from glob import glob - from shutil import copy - - if not os.path.isdir(target_mono_config_dir): - os.makedirs(target_mono_config_dir) - - mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') - if not os.path.isdir(mono_etc_dir): - mono_etc_dir = '' - etc_hint_dirs = [] - if platform != 'windows': - etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] - if 'MONO_CFG_DIR' in os.environ: - etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] - for etc_hint_dir in etc_hint_dirs: - if os.path.isdir(etc_hint_dir): - mono_etc_dir = etc_hint_dir - break - if not mono_etc_dir: - raise RuntimeError('Mono installation etc directory not found') - - copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) - copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) - copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) - copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) - - for file in glob(os.path.join(mono_etc_dir, '*')): - if os.path.isfile(file): - copy(file, target_mono_config_dir) - - -def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): - from shutil import copy - - if platform == 'windows': - target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') - - if not os.path.isdir(target_mono_bin_dir): - os.makedirs(target_mono_bin_dir) - - copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) - else: - target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') - - if not os.path.isdir(target_mono_lib_dir): - os.makedirs(target_mono_lib_dir) - - if platform == 'osx': - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) - elif platform == 'x11': - copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) - - -def configure_for_mono_version(env, mono_version): - if mono_version is None: - raise RuntimeError('Mono JIT compiler version not found') - print('Found Mono JIT compiler version: ' + str(mono_version)) - if mono_version >= LooseVersion('5.12.0'): - env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) - - -def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): - tmpenv = Environment() - tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) - tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') - for hint_dir in tmpenv['LIBPATH']: - name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) - if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): - return os.path.join(hint_dir, '..') - return '' - - -def pkgconfig_try_find_mono_version(): - from compat import decode_utf8 +def get_doc_classes(): + return [ + '@C#', + 'CSharpScript', + 'GodotSharp', + ] - lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() - greater_version = None - for line in lines: - try: - version = LooseVersion(decode_utf8(line)) - if greater_version is None or version > greater_version: - greater_version = version - except ValueError: - pass - return greater_version +def get_doc_path(): + return 'doc_classes' -def mono_root_try_find_mono_version(mono_root): - from compat import decode_utf8 - mono_bin = os.path.join(mono_root, 'bin') - if os.path.isfile(os.path.join(mono_bin, 'mono')): - mono_binary = os.path.join(mono_bin, 'mono') - elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): - mono_binary = os.path.join(mono_bin, 'mono.exe') - else: - return None - output = subprocess.check_output([mono_binary, '--version']) - first_line = decode_utf8(output.splitlines()[0]) - try: - return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) - except (ValueError, IndexError): - return None +def is_enabled(): + # The module is disabled by default. Use module_mono_enabled=yes to enable it. + return False diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 3c818898e6..078a490b22 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,9 +42,13 @@ #include "editor/bindings_generator.h" #include "editor/csharp_project.h" #include "editor/editor_node.h" -#include "editor/godotsharp_editor.h" #endif +#ifdef DEBUG_METHODS_ENABLED +#include "class_db_api_json.h" +#endif + +#include "editor/editor_internal_calls.h" #include "godotsharp_dirs.h" #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.h" @@ -65,8 +69,8 @@ static bool _create_project_solution_if_needed() { if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) { // A solution does not yet exist, create a new one - CRASH_COND(GodotSharpEditor::get_singleton() == NULL); - return GodotSharpEditor::get_singleton()->call("_create_project_solution"); + CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL); + return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution"); } return true; @@ -96,32 +100,36 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -#ifdef TOOLS_ENABLED -void gdsharp_editor_init_callback() { +void CSharpLanguage::init() { - EditorNode *editor = EditorNode::get_singleton(); - editor->add_child(memnew(GodotSharpEditor(editor))); -} +#ifdef DEBUG_METHODS_ENABLED + if (OS::get_singleton()->get_cmdline_args().find("--class_db_to_json")) { + class_db_api_to_json("user://class_db_api.json", ClassDB::API_CORE); +#ifdef TOOLS_ENABLED + class_db_api_to_json("user://class_db_api_editor.json", ClassDB::API_EDITOR); +#endif + } #endif - -void CSharpLanguage::init() { gdmono = memnew(GDMono); gdmono->initialize(); -#ifndef MONO_GLUE_ENABLED - WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting"); +#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) + // Generate bindings here, before loading assemblies. `initialize_load_assemblies` aborts + // the applications if the api assemblies or the main tools assembly is missing, but this + // is not a problem for BindingsGenerator as it only needs the tools project editor assembly. + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + BindingsGenerator::handle_cmdline_args(cmdline_args); #endif -#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) - if (gdmono->get_editor_tools_assembly() != NULL) { - List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); - BindingsGenerator::handle_cmdline_args(cmdline_args); - } +#ifndef MONO_GLUE_ENABLED + print_line("Run this binary with `--generate-mono-glue path/to/modules/mono/glue`"); #endif + gdmono->initialize_load_assemblies(); + #ifdef TOOLS_ENABLED - EditorNode::add_init_callback(&gdsharp_editor_init_callback); + EditorNode::add_init_callback(&_editor_init_callback); GLOBAL_DEF("mono/export/include_scripts_content", false); #endif @@ -131,22 +139,24 @@ void CSharpLanguage::finish() { finalizing = true; -#ifdef TOOLS_ENABLED - // Must be here, to avoid StringName leaks - if (BindingsGenerator::singleton) { - memdelete(BindingsGenerator::singleton); - BindingsGenerator::singleton = NULL; - } -#endif + // Make sure all script binding gchandles are released before finalizing GDMono + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); - // Release gchandle bindings before finalizing mono runtime - script_bindings.clear(); + if (script_binding.gchandle.is_valid()) { + script_binding.gchandle->release(); + script_binding.inited = false; + } + } if (gdmono) { memdelete(gdmono); gdmono = NULL; } + // Clear here, after finalizing all domains to make sure there is nothing else referencing the elements. + script_bindings.clear(); + finalizing = false; } @@ -439,7 +449,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { Variant::_RID }; - for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { + for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { if (p_var_type_name == Variant::get_type_name(var_types[i])) return p_var_type_name; } @@ -489,6 +499,47 @@ String CSharpLanguage::_get_indentation() const { return "\t"; } +String CSharpLanguage::debug_get_error() const { + + return _debug_error; +} + +int CSharpLanguage::debug_get_stack_level_count() const { + + if (_debug_parse_err_line >= 0) + return 1; + + // TODO: StackTrace + return 1; +} + +int CSharpLanguage::debug_get_stack_level_line(int p_level) const { + + if (_debug_parse_err_line >= 0) + return _debug_parse_err_line; + + // TODO: StackTrace + return 1; +} + +String CSharpLanguage::debug_get_stack_level_function(int p_level) const { + + if (_debug_parse_err_line >= 0) + return String(); + + // TODO: StackTrace + return String(); +} + +String CSharpLanguage::debug_get_stack_level_source(int p_level) const { + + if (_debug_parse_err_line >= 0) + return _debug_parse_err_file; + + // TODO: StackTrace + return String(); +} + Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { #ifdef DEBUG_ENABLED @@ -520,7 +571,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoException *exc = NULL; - MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (MonoObject **)&exc); + MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -544,7 +595,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); + invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -574,11 +625,11 @@ void CSharpLanguage::frame() { if (task_scheduler) { MonoException *exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, &exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } } } @@ -607,31 +658,10 @@ struct CSharpScriptDepSort { void CSharpLanguage::reload_all_scripts() { -#ifdef DEBUG_ENABLED - - List<Ref<CSharpScript> > scripts; - - { - SCOPED_MUTEX_LOCK(script_instances_mutex); - - SelfList<CSharpScript> *elem = script_list.first(); - while (elem) { - if (elem->self()->get_path().is_resource_file()) { - scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to gdscript to avoid being erased by accident - } - elem = elem->next(); - } - } - - //as scripts are going to be reloaded, must proceed without locking here - - scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order - - for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { - E->get()->load_source_code(E->get()->get_path()); - E->get()->reload(true); +#ifdef GD_MONO_HOT_RELOAD + if (is_assembly_reloading_needed()) { + reload_assemblies(false); } - #endif } @@ -639,15 +669,20 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft (void)p_script; // UNUSED + CRASH_COND(!Engine::get_singleton()->is_editor_hint()); + #ifdef TOOLS_ENABLED - MonoReloadNode::get_singleton()->restart_reload_timer(); + get_godotsharp_editor()->get_node(NodePath("HotReloadAssemblyWatcher"))->call("RestartTimer"); +#endif + +#ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { reload_assemblies(p_soft_reload); } #endif } -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD bool CSharpLanguage::is_assembly_reloading_needed() { if (!gdmono->is_runtime_initialized()) @@ -655,19 +690,20 @@ bool CSharpLanguage::is_assembly_reloading_needed() { GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } - name += ".dll"; + appname_safe += ".dll"; if (proj_assembly) { String proj_asm_path = proj_assembly->get_path(); if (!FileAccess::exists(proj_assembly->get_path())) { // Maybe it wasn't loaded from the default path, so check this as well - proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); + proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe); if (!FileAccess::exists(proj_asm_path)) return false; // No assembly to load } @@ -675,16 +711,10 @@ bool CSharpLanguage::is_assembly_reloading_needed() { if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) return false; // Already up to date } else { - if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe))) return false; // No assembly to load } - if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) - return false; // The core API assembly to load is invalidated - - if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) - return false; // The editor API assembly to load is invalidated - return true; } @@ -701,58 +731,93 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { SCOPED_MUTEX_LOCK(script_instances_mutex); for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) { - if (elem->self()->get_path().is_resource_file()) { - // Cast to CSharpScript to avoid being erased by accident - scripts.push_back(Ref<CSharpScript>(elem->self())); - } + // Cast to CSharpScript to avoid being erased by accident + scripts.push_back(Ref<CSharpScript>(elem->self())); } } List<Ref<CSharpScript> > to_reload; + // We need to keep reference instances alive during reloading + List<Ref<Reference> > ref_instances; + + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); + Reference *ref = Object::cast_to<Reference>(script_binding.owner); + if (ref) { + ref_instances.push_back(Ref<Reference>(ref)); + } + } + // As scripts are going to be reloaded, must proceed without locking here scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { - Ref<CSharpScript> &script = E->get(); to_reload.push_back(script); + if (script->get_path().empty()) { + script->tied_class_name_for_reload = script->script_class->get_name(); + script->tied_class_namespace_for_reload = script->script_class->get_namespace(); + } + // Script::instances are deleted during managed object disposal, which happens on domain finalize. // Only placeholders are kept. Therefore we need to keep a copy before that happens. - for (Set<Object *>::Element *E = script->instances.front(); E; E = E->next()) { - script->pending_reload_instances.insert(E->get()->get_instance_id()); + for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) { + Object *obj = F->get(); + script->pending_reload_instances.insert(obj->get_instance_id()); + + Reference *ref = Object::cast_to<Reference>(obj); + if (ref) { + ref_instances.push_back(Ref<Reference>(ref)); + } } #ifdef TOOLS_ENABLED - for (Set<PlaceHolderScriptInstance *>::Element *E = script->placeholders.front(); E; E = E->next()) { - script->pending_reload_instances.insert(E->get()->get_owner()->get_instance_id()); + for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) { + Object *obj = F->get()->get_owner(); + script->pending_reload_instances.insert(obj->get_instance_id()); + + Reference *ref = Object::cast_to<Reference>(obj); + if (ref) { + ref_instances.push_back(Ref<Reference>(ref)); + } } #endif - // FIXME: What about references? Need to keep them alive if only managed code references them. - // Save state and remove script from instances Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state; - while (script->instances.front()) { - Object *obj = script->instances.front()->get(); - // Save instance info - CSharpScript::StateBackup state; + for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) { + Object *obj = F->get(); ERR_CONTINUE(!obj->get_script_instance()); - // TODO: Proper state backup (Not only variants, serialize managed state of scripts) - obj->get_script_instance()->get_property_state(state.properties); + CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance()); - Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle; - if (gchandle.is_valid()) - gchandle->release(); + // Call OnBeforeSerialize + if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) + obj->get_script_instance()->call_multilevel(string_names.on_before_serialize); + + // Save instance info + CSharpScript::StateBackup state; + + // TODO: Proper state backup (Not only variants, serialize managed state of scripts) + csi->get_properties_state_for_reloading(state.properties); owners_map[obj->get_instance_id()] = state; + } + } + + // After the state of all instances is saved, clear scripts and script instances + for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { + Ref<CSharpScript> &script = E->get(); + + while (script->instances.front()) { + Object *obj = script->instances.front()->get(); obj->set_script(RefPtr()); // Remove script and existing script instances (placeholder are not removed before domain reload) } @@ -781,9 +846,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj); obj->set_script_instance(placeholder); +#ifdef TOOLS_ENABLED // Even though build didn't fail, this tells the placeholder to keep properties and // it allows using property_set_fallback for restoring the state without a valid script. - placeholder->set_build_failed(true); + scr->placeholder_fallback_enabled = true; +#endif // Restore Variant properties state, it will be kept by the placeholder until the next script reloading for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) { @@ -793,25 +860,80 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { scr->pending_reload_state.erase(obj_id); } } + return; } + List<Ref<CSharpScript> > to_reload_state; + for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) { + Ref<CSharpScript> script = E->get(); + + if (!script->get_path().empty()) { +#ifdef TOOLS_ENABLED + script->exports_invalidated = true; +#endif + script->signals_invalidated = true; + + script->reload(p_soft_reload); + script->update_exports(); + + if (!script->valid) { + script->pending_reload_instances.clear(); + continue; + } + } else { + const StringName &class_namespace = script->tied_class_namespace_for_reload; + const StringName &class_name = script->tied_class_name_for_reload; + GDMonoAssembly *project_assembly = gdmono->get_project_assembly(); + + // Search in project and tools assemblies first as those are the most likely to have the class + GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : NULL); + +#ifdef TOOLS_ENABLED + if (!script_class) { + GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly(); + script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : NULL); + } +#endif + + if (!script_class) { + script_class = gdmono->get_class(class_namespace, class_name); + } + + if (!script_class) { + // The class was removed, can't reload + script->pending_reload_instances.clear(); + continue; + } + + bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class); + if (!obj_type) { + // The class no longer inherits Godot.Object, can't reload + script->pending_reload_instances.clear(); + continue; + } + + GDMonoClass *native = GDMonoUtils::get_class_native_base(script_class); + + CSharpScript::initialize_for_managed_type(script, script_class, native); + } - Ref<CSharpScript> scr = E->get(); - scr->exports_invalidated = true; - scr->signals_invalidated = true; - scr->reload(p_soft_reload); - scr->update_exports(); + String native_name = NATIVE_GDMONOCLASS_NAME(script->native); { -#ifdef DEBUG_ENABLED - for (Set<ObjectID>::Element *F = scr->pending_reload_instances.front(); F; F = F->next()) { + for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) { ObjectID obj_id = F->get(); Object *obj = ObjectDB::get_instance(obj_id); if (!obj) { - scr->pending_reload_state.erase(obj_id); + script->pending_reload_state.erase(obj_id); + continue; + } + + if (!ClassDB::is_parent_class(obj->get_class_name(), native_name)) { + // No longer inherits the same compatible type, can't reload + script->pending_reload_state.erase(obj_id); continue; } @@ -823,28 +945,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // Non-placeholder script instances are removed in godot_icall_Object_Disposed. CRASH_COND(!si->is_placeholder()); - if (scr->is_tool() || ScriptServer::is_scripting_enabled()) { + if (script->is_tool() || ScriptServer::is_scripting_enabled()) { // Replace placeholder with a script instance - CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id]; + CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id]; // Backup placeholder script instance state before replacing it with a script instance - obj->get_script_instance()->get_property_state(state_backup.properties); + si->get_property_state(state_backup.properties); - ScriptInstance *script_instance = scr->instance_create(obj); + ScriptInstance *script_instance = script->instance_create(obj); if (script_instance) { - scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si)); + script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si)); obj->set_script_instance(script_instance); } - - // TODO: Restore serialized state - - for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) { - script_instance->set(G->get().first, G->get().second); - } - - scr->pending_reload_state.erase(obj_id); } continue; @@ -853,32 +967,55 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { CRASH_COND(si != NULL); #endif // Re-create script instance + obj->set_script(script.get_ref_ptr()); // will create the script instance as well + } + } - obj->set_script(scr.get_ref_ptr()); // will create the script instance as well + to_reload_state.push_back(script); + } - // TODO: Restore serialized state + for (List<Ref<CSharpScript> >::Element *E = to_reload_state.front(); E; E = E->next()) { + Ref<CSharpScript> script = E->get(); - for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) { - obj->get_script_instance()->set(G->get().first, G->get().second); - } + for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) { + ObjectID obj_id = F->get(); + Object *obj = ObjectDB::get_instance(obj_id); - scr->pending_reload_state.erase(obj_id); + if (!obj) { + script->pending_reload_state.erase(obj_id); + continue; + } + + ERR_CONTINUE(!obj->get_script_instance()); + + // TODO: Restore serialized state + + CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id]; + + for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) { + obj->get_script_instance()->set(G->get().first, G->get().second); } -#endif - scr->pending_reload_instances.clear(); + // Call OnAfterDeserialization + CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance()); + if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) + obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize); } + + script->pending_reload_instances.clear(); } +#ifdef TOOLS_ENABLED // FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative. if (Engine::get_singleton()->is_editor_hint()) { EditorNode::get_singleton()->get_inspector()->update_tree(); NodeDock::singleton->update_lists(); } +#endif } #endif -void CSharpLanguage::project_assembly_loaded() { +void CSharpLanguage::_load_scripts_metadata() { scripts_metadata.clear(); @@ -912,6 +1049,7 @@ void CSharpLanguage::project_assembly_loaded() { } scripts_metadata = old_dict_var.operator Dictionary(); + scripts_metadata_invalidated = false; print_verbose("Successfully loaded scripts metadata"); } else { @@ -929,12 +1067,12 @@ void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const #ifdef TOOLS_ENABLED Error CSharpLanguage::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { - return GodotSharpEditor::get_singleton()->open_in_external_editor(p_script, p_line, p_col); + return (Error)(int)get_godotsharp_editor()->call("OpenInExternalEditor", p_script, p_line, p_col); } bool CSharpLanguage::overrides_external_editor() { - return GodotSharpEditor::get_singleton()->overrides_external_editor(); + return get_godotsharp_editor()->call("OverridesExternalEditor"); } #endif @@ -958,12 +1096,11 @@ void CSharpLanguage::thread_exit() { bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { - // Break because of parse error + // Not a parser error in our case, but it's still used for other type of errors if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { - // TODO - //_debug_parse_err_line = p_line; - //_debug_parse_err_file = p_file; - //_debug_error = p_error; + _debug_parse_err_line = p_line; + _debug_parse_err_file = p_file; + _debug_error = p_error; ScriptDebugger::get_singleton()->debug(this, false); return true; } else { @@ -974,10 +1111,9 @@ bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const S bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { - // TODO - //_debug_parse_err_line = -1; - //_debug_parse_err_file = ""; - //_debug_error = p_error; + _debug_parse_err_line = -1; + _debug_parse_err_file = ""; + _debug_error = p_error; ScriptDebugger::get_singleton()->debug(this, p_allow_continue); return true; } else { @@ -985,6 +1121,43 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } +void CSharpLanguage::_on_scripts_domain_unloaded() { + for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { + CSharpScriptBinding &script_binding = E->value(); + script_binding.inited = false; + } + + scripts_metadata_invalidated = true; +} + +#ifdef TOOLS_ENABLED +void CSharpLanguage::_editor_init_callback() { + + register_editor_internal_calls(); + + // Initialize GodotSharpEditor + + GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor"); + CRASH_COND(editor_klass == NULL); + + MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr()); + CRASH_COND(mono_object == NULL); + + MonoException *exc = NULL; + GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc); + UNHANDLED_EXCEPTION(exc); + + EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(GDMonoMarshal::mono_object_to_variant(mono_object)); + CRASH_COND(godotsharp_editor == NULL); + + // Enable it as a plugin + EditorNode::add_editor_plugin(godotsharp_editor); + godotsharp_editor->enable_plugin(); + + get_singleton()->godotsharp_editor = godotsharp_editor; +} +#endif + void CSharpLanguage::set_language_index(int p_idx) { ERR_FAIL_COND(lang_idx != -1); @@ -1040,6 +1213,12 @@ CSharpLanguage::CSharpLanguage() { #endif lang_idx = -1; + + scripts_metadata_invalidated = true; + +#ifdef TOOLS_ENABLED + godotsharp_editor = NULL; +#endif } CSharpLanguage::~CSharpLanguage() { @@ -1064,12 +1243,14 @@ CSharpLanguage::~CSharpLanguage() { singleton = NULL; } -void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { +bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) { #ifdef DEBUG_ENABLED // I don't trust you - if (p_object->get_script_instance()) - CRASH_COND(NULL != CAST_CSHARP_INSTANCE(p_object->get_script_instance())); + if (p_object->get_script_instance()) { + CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); + CRASH_COND(csharp_instance != NULL && !csharp_instance->is_destructing_script_instance()); + } #endif StringName type_name = p_object->get_class_name(); @@ -1078,29 +1259,22 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name); while (classinfo && !classinfo->exposed) classinfo = classinfo->inherits_ptr; - ERR_FAIL_NULL_V(classinfo, NULL); + ERR_FAIL_NULL_V(classinfo, false); type_name = classinfo->name; GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name); - ERR_FAIL_NULL_V(type_class, NULL); + ERR_FAIL_NULL_V(type_class, false); MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object); - ERR_FAIL_NULL_V(mono_object, NULL); - - CSharpScriptBinding script_binding; - - script_binding.type_name = type_name; - script_binding.wrapper_class = type_class; // cache - script_binding.gchandle = MonoGCHandle::create_strong(mono_object); - - void *data; + ERR_FAIL_NULL_V(mono_object, false); - { - SCOPED_MUTEX_LOCK(language_bind_mutex); - data = (void *)script_bindings.insert(p_object, script_binding); - } + r_script_binding.inited = true; + r_script_binding.type_name = type_name; + r_script_binding.wrapper_class = type_class; // cache + r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object); + r_script_binding.owner = p_object; // Tie managed to unmanaged Reference *ref = Object::cast_to<Reference>(p_object); @@ -1114,7 +1288,28 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { ref->reference(); } - return data; + return true; +} + +void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { + + SCOPED_MUTEX_LOCK(language_bind_mutex); + + Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object); + if (match) + return (void *)match; + + CSharpScriptBinding script_binding; + + if (!setup_csharp_script_binding(script_binding, p_object)) + return NULL; + + return (void *)insert_script_binding(p_object, script_binding); +} + +Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) { + + return script_bindings.insert(p_object, p_script_binding); } void CSharpLanguage::free_instance_binding_data(void *p_data) { @@ -1135,10 +1330,15 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; - // Set the native instance field to IntPtr.Zero, if not yet garbage collected - MonoObject *mono_object = data->value().gchandle->get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + CSharpScriptBinding &script_binding = data->value(); + + if (script_binding.inited) { + // Set the native instance field to IntPtr.Zero, if not yet garbage collected. + // This is done to avoid trying to dispose the native instance from Dispose(bool). + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + } } script_bindings.erase(data); @@ -1154,9 +1354,13 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { #endif void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) + CRASH_COND(!data); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + + if (!script_binding.inited) return; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // The reference count was increased after the managed side was the only one referencing our owner. @@ -1182,14 +1386,18 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { CRASH_COND(!ref_owner); #endif + void *data = p_object->get_script_instance_binding(get_language_index()); + CRASH_COND(!data); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + int refcount = ref_owner->reference_get_count(); - void *data = p_object->get_script_instance_binding(get_language_index()); - if (!data) + if (!script_binding.inited) return refcount == 0; - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. @@ -1233,6 +1441,10 @@ MonoObject *CSharpInstance::get_mono_object() const { return gchandle->get_target(); } +Object *CSharpInstance::get_owner() { + return owner; +} + bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); @@ -1243,14 +1455,14 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GDMonoClass *top = script->script_class; while (top && top != script->native) { - GDMonoField *field = script->script_class->get_field(p_name); + GDMonoField *field = top->get_field(p_name); if (field) { field->set_value_from_variant(mono_object, p_value); return true; } - GDMonoProperty *property = script->script_class->get_property(p_name); + GDMonoProperty *property = top->get_property(p_name); if (property) { property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type())); @@ -1347,11 +1559,64 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { return false; } +void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state) { + + List<PropertyInfo> pinfo; + get_property_list(&pinfo); + + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + Pair<StringName, Variant> state_pair; + state_pair.first = E->get().name; + + ManagedType managedType; + + GDMonoField *field = script->script_class->get_field(state_pair.first); + if (!field) + continue; // Properties ignored. We get the property baking fields instead. + + managedType = field->get_type(); + + if (GDMonoMarshal::managed_to_variant_type(managedType) != Variant::NIL) { // If we can marshal it + if (get(state_pair.first, state_pair.second)) { + r_state.push_back(state_pair); + } + } + } +} + void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) { p_properties->push_back(E->value()); } + + // Call _get_property_list + + ERR_FAIL_COND(!script.is_valid()); + + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL(mono_object); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0); + + if (method) { + MonoObject *ret = method->invoke(mono_object); + + if (ret) { + Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); + for (int i = 0, size = array.size(); i < size; i++) + p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + return; + } + + break; + } + + top = top->get_parent_class(); + } } Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { @@ -1493,14 +1758,8 @@ bool CSharpInstance::_unreference_owner_unsafe() { // Unsafe refcount decrement. The managed instance also counts as a reference. // See: _reference_owner_unsafe() - bool die = static_cast<Reference *>(owner)->unreference(); - - if (die) { - memdelete(owner); - owner = NULL; - } - - return die; + // Destroying the owner here means self destructing, so we defer the owner destruction to the caller. + return static_cast<Reference *>(owner)->unreference(); } MonoObject *CSharpInstance::_internal_new_managed() { @@ -1508,32 +1767,47 @@ MonoObject *CSharpInstance::_internal_new_managed() { CRASH_COND(!gchandle.is_valid()); #endif + // Search the constructor first, to fail with an error if it's not found before allocating anything else. + GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + if (ctor == NULL) { + ERR_PRINTS("Cannot create script instance because the class does not define a parameterless constructor: " + script->get_path()); + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); ERR_FAIL_NULL_V(owner, NULL); ERR_FAIL_COND_V(script.is_null(), NULL); - if (base_ref) - _reference_owner_unsafe(); - - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here script = Ref<CSharpScript>(); - owner->set_script_instance(NULL); + + bool die = _unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + + owner = NULL; + ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); } + // Tie managed to unmanaged + gchandle = MonoGCHandle::create_strong(mono_object); + + if (base_ref) + _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); // Construct - GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); ctor->invoke_raw(mono_object, NULL); - // Tie managed to unmanaged - gchandle = MonoGCHandle::create_strong(mono_object); - return mono_object; } @@ -1546,25 +1820,36 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); } -void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted) { +void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); CRASH_COND(gchandle.is_null()); #endif + + r_remove_script_instance = false; + if (_unreference_owner_unsafe()) { - r_owner_deleted = true; + // Safe to self destruct here with memdelete(owner), but it's deferred to the caller to prevent future mistakes. + r_delete_owner = true; } else { - r_owner_deleted = false; + r_delete_owner = false; CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); - if (p_is_finalizer && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - // If the native instance is still alive, then it was - // referenced from another thread before the finalizer could - // unreference it and delete it, so we want to keep it. - // GC.ReRegisterForFinalize(this) is not safe because the objects - // referenced by this could have already been collected. - // Instead we will create a new managed instance here. - _internal_new_managed(); + + if (!p_is_finalizer) { + // If the native instance is still alive and Dispose() was called + // (instead of the finalizer), then we remove the script instance. + r_remove_script_instance = true; + } else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) { + // If the native instance is still alive and this is called from the finalizer, + // then it was referenced from another thread before the finalizer could + // unreference and delete it, so we want to keep it. + // GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this' + // could have already been collected. Instead we will create a new managed instance here. + MonoObject *new_managed = _internal_new_managed(); + if (!new_managed) { + r_remove_script_instance = true; + } } } } @@ -1618,7 +1903,7 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { +MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(IMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) return MultiplayerAPI::RPC_MODE_REMOTE; @@ -1739,6 +2024,34 @@ void CSharpInstance::_call_notification(int p_notification) { } } +String CSharpInstance::to_string(bool *r_valid) { + MonoObject *mono_object = get_mono_object(); + + if (mono_object == NULL) { + if (r_valid) + *r_valid = false; + return String(); + } + + MonoException *exc = NULL; + MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + if (r_valid) + *r_valid = false; + return String(); + } + + if (result == NULL) { + if (r_valid) + *r_valid = false; + return String(); + } + + return GDMonoMarshal::mono_string_to_godot(result); +} + Ref<Script> CSharpInstance::get_script() const { return script; @@ -1760,6 +2073,8 @@ CSharpInstance::CSharpInstance() : CSharpInstance::~CSharpInstance() { + destructing_script_instance = true; + if (gchandle.is_valid()) { if (!predelete_notified && !ref_dying) { // This destructor is not called from the owners destructor. @@ -1772,9 +2087,7 @@ CSharpInstance::~CSharpInstance() { if (mono_object) { MonoException *exc = NULL; - destructing_script_instance = true; GDMonoUtils::dispose(mono_object, &exc); - destructing_script_instance = false; if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -1782,11 +2095,32 @@ CSharpInstance::~CSharpInstance() { } } - gchandle->release(); // Make sure it's released + gchandle->release(); // Make sure the gchandle is released } - if (base_ref && !ref_dying && owner) { // it may be called from the owner's destructor - _unreference_owner_unsafe(); + // If not being called from the owner's destructor, and we still hold a reference to the owner + if (base_ref && !ref_dying && owner && unsafe_referenced) { + // The owner's script or script instance is being replaced (or removed) + + // Transfer ownership to an "instance binding" + + void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + + if (!script_binding.inited) { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex()); + + if (!script_binding.inited) { // Other thread may have set it up + // Already had a binding that needs to be setup + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner); + CRASH_COND(!script_binding.inited); + } + } + + bool die = _unreference_owner_unsafe(); + CRASH_COND(die == true); // The "instance binding" should be holding a reference } if (script.is_valid() && owner) { @@ -1825,17 +2159,64 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List propnames.push_back(E->get()); } } + +void CSharpScript::_update_member_info_no_exports() { + + if (exports_invalidated) { + exports_invalidated = false; + + member_info.clear(); + + GDMonoClass *top = script_class; + + while (top && top != native) { + PropertyInfo prop_info; + bool exported; + + const Vector<GDMonoField *> &fields = top->get_all_fields(); + + for (int i = fields.size() - 1; i >= 0; i--) { + GDMonoField *field = fields[i]; + + if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) { + StringName member_name = field->get_name(); + + member_info[member_name] = prop_info; + exported_members_cache.push_front(prop_info); + exported_members_defval_cache[member_name] = Variant(); + } + } + + const Vector<GDMonoProperty *> &properties = top->get_all_properties(); + + for (int i = properties.size() - 1; i >= 0; i--) { + GDMonoProperty *property = properties[i]; + + if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) { + StringName member_name = property->get_name(); + + member_info[member_name] = prop_info; + exported_members_cache.push_front(prop_info); + exported_members_defval_cache[member_name] = Variant(); + } + } + + top = top->get_parent_class(); + } + } +} #endif bool CSharpScript::_update_exports() { #ifdef TOOLS_ENABLED - if (!valid) { - for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(true); - } + if (!Engine::get_singleton()->is_editor_hint()) + return false; + + placeholder_fallback_enabled = true; // until proven otherwise + + if (!valid) return false; - } bool changed = false; @@ -1850,16 +2231,24 @@ bool CSharpScript::_update_exports() { // Here we create a temporary managed instance of the class to get the initial values - MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); + MonoObject *tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr()); if (!tmp_object) { - ERR_PRINT("Failed to create temporary MonoObject"); + ERR_PRINT("Failed to allocate temporary MonoObject"); return false; } uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed) GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + + if (ctor == NULL) { + ERR_PRINTS("Cannot construct temporary MonoObject because the class does not define a parameterless constructor: " + get_path()); + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + MonoException *ctor_exc = NULL; ctor->invoke(tmp_object, NULL, &ctor_exc); @@ -1883,18 +2272,18 @@ bool CSharpScript::_update_exports() { for (int i = fields.size() - 1; i >= 0; i--) { GDMonoField *field = fields[i]; - if (_get_member_export(top, field, prop_info, exported)) { - StringName name = field->get_name(); + if (_get_member_export(field, /* inspect export: */ true, prop_info, exported)) { + StringName member_name = field->get_name(); if (exported) { - member_info[name] = prop_info; + member_info[member_name] = prop_info; exported_members_cache.push_front(prop_info); if (tmp_object) { - exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); + exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); } } else { - member_info[name] = prop_info; + member_info[member_name] = prop_info; } } } @@ -1904,25 +2293,25 @@ bool CSharpScript::_update_exports() { for (int i = properties.size() - 1; i >= 0; i--) { GDMonoProperty *property = properties[i]; - if (_get_member_export(top, property, prop_info, exported)) { - StringName name = property->get_name(); + if (_get_member_export(property, /* inspect export: */ true, prop_info, exported)) { + StringName member_name = property->get_name(); if (exported) { - member_info[name] = prop_info; + member_info[member_name] = prop_info; exported_members_cache.push_front(prop_info); if (tmp_object) { MonoException *exc = NULL; MonoObject *ret = property->get_value(tmp_object, &exc); if (exc) { - exported_members_defval_cache[name] = Variant(); + exported_members_defval_cache[member_name] = Variant(); GDMonoUtils::debug_print_unhandled_exception(exc); } else { - exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); + exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(ret); } } } else { - member_info[name] = prop_info; + member_info[member_name] = prop_info; } } } @@ -1944,6 +2333,8 @@ bool CSharpScript::_update_exports() { tmp_object = NULL; } + placeholder_fallback_enabled = false; + if (placeholders.size()) { // Update placeholders if any Map<StringName, Variant> values; @@ -1951,7 +2342,6 @@ bool CSharpScript::_update_exports() { _update_exports_values(values, propnames); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->set_build_failed(false); E->get()->update(propnames, values); } } @@ -2030,43 +2420,53 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve * Returns false if there was an error, otherwise true. * If there was an error, r_prop_info and r_exported are not assigned any value. */ -bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { +bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported) { - StringName name = p_member->get_name(); + // Goddammit, C++. All I wanted was some nested functions. +#define MEMBER_FULL_QUALIFIED_NAME(m_member) \ + (m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name()) if (p_member->is_static()) { if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) - ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String()); + ERR_PRINTS("Cannot export member because it is static: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; } - if (member_info.has(name)) + if (member_info.has(p_member->get_name())) return false; ManagedType type; - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_FIELD) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_FIELD) { type = static_cast<GDMonoField *>(p_member)->get_type(); - } else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + } else if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { type = static_cast<GDMonoProperty *>(p_member)->get_type(); } else { CRASH_NOW(); } - Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); - - if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { - r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); - r_exported = false; - return true; - } + bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute)); - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); - if (!property->has_getter() || !property->has_setter()) { - ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); + if (!property->has_getter()) { + if (exported) + ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; } + if (!property->has_setter()) { + if (exported) + ERR_PRINTS("Write-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); + return false; + } + } + + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); + + if (!p_inspect_export || !exported) { + r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = false; + return true; } MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); @@ -2075,15 +2475,38 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p String hint_string; if (variant_type == Variant::NIL) { - ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); + ERR_PRINTS("Unknown exported member type: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; - } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { - variant_type = Variant::INT; - hint = PROPERTY_HINT_ENUM; + } - Vector<MonoClassField *> fields = type.type_class->get_enum_fields(); + int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string); - MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr()); + if (hint_res == -1) { + ERR_EXPLAIN("Error while trying to determine information about the exported member: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); + ERR_FAIL_V(false); + } + + if (hint_res == 0) { + hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); + hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + } + + r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = true; + + return true; + +#undef MEMBER_FULL_QUALIFIED_NAME +} + +int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) { + + if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) { + r_hint = PROPERTY_HINT_ENUM; + + Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields(); + + MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr()); String name_only_hint_string; @@ -2096,12 +2519,12 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p MonoClassField *field = fields[i]; if (i > 0) { - hint_string += ","; + r_hint_string += ","; name_only_hint_string += ","; } String enum_field_name = mono_field_get_name(field); - hint_string += enum_field_name; + r_hint_string += enum_field_name; name_only_hint_string += enum_field_name; // TODO: @@ -2111,45 +2534,73 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL); if (val_obj == NULL) { - ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " + - p_class->get_full_name() + "." + name.operator String()); - return false; + ERR_EXPLAIN("Failed to get '" + enum_field_name + "' constant enum value"); + ERR_FAIL_V(-1); } bool r_error; uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error); if (r_error) { - ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " + - p_class->get_full_name() + "." + name.operator String()); - return false; + ERR_EXPLAIN("Failed to unbox '" + enum_field_name + "' constant enum value"); + ERR_FAIL_V(-1); } - if (val != i) { + if (val != (unsigned int)i) { uses_default_values = false; } - hint_string += ":"; - hint_string += String::num_uint64(val); + r_hint_string += ":"; + r_hint_string += String::num_uint64(val); } if (uses_default_values) { // If we use the format NAME:VAL, that's what the editor displays. // That's annoying if the user is not using custom values for the enum constants. // This may not be needed in the future if the editor is changed to not display values. - hint_string = name_only_hint_string; + r_hint_string = name_only_hint_string; + } + } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) { + GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class); + CRASH_COND(field_native_class == NULL); + + r_hint = PROPERTY_HINT_RESOURCE_TYPE; + r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class); + } else if (p_allow_generics && p_variant_type == Variant::ARRAY) { + // Nested arrays are not supported in the inspector + + ManagedType elem_type; + + if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type)) + return 0; + + Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type); + + PropertyHint elem_hint = PROPERTY_HINT_NONE; + String elem_hint_string; + + if (elem_variant_type == Variant::NIL) { + ERR_EXPLAIN("Unknown array element type"); + ERR_FAIL_V(-1); + } + + int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string); + + if (hint_res == -1) { + ERR_EXPLAIN("Error while trying to determine information about the array element type"); + ERR_FAIL_V(-1); } - } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { - hint = PROPERTY_HINT_RESOURCE_TYPE; - hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class); + + // Format: type/hint:hint_string + r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string; + r_hint = PROPERTY_HINT_TYPE_STRING; + + } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) { + // TODO: Dictionaries are not supported in the inspector } else { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + return 0; } - r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); - r_exported = true; - - return true; + return 1; } #endif @@ -2235,34 +2686,60 @@ void CSharpScript::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo(Variant::OBJECT, "new")); } -Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) { +Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native) { - // This method should not fail + // This method should not fail, only assertions allowed - CRASH_COND(!p_class); + CRASH_COND(p_class == NULL); + // TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time Ref<CSharpScript> script = memnew(CSharpScript); - script->name = p_class->get_name(); - script->script_class = p_class; - script->native = GDMonoUtils::get_class_native_base(script->script_class); + initialize_for_managed_type(script, p_class, p_native); + + return script; +} + +void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native) { + + // This method should not fail, only assertions allowed + + CRASH_COND(p_class == NULL); + + p_script->name = p_class->get_name(); + p_script->script_class = p_class; + p_script->native = p_native; - CRASH_COND(script->native == NULL); + CRASH_COND(p_script->native == NULL); - GDMonoClass *base = script->script_class->get_parent_class(); + GDMonoClass *base = p_script->script_class->get_parent_class(); - if (base != script->native) - script->base = base; + if (base != p_script->native) + p_script->base = base; + + p_script->valid = true; + p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute)); + + if (!p_script->tool) { + GDMonoClass *nesting_class = p_script->script_class->get_nesting_class(); + p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute)); + } + +#if TOOLS_ENABLED + if (!p_script->tool) { + p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly(); + } +#endif #ifdef DEBUG_ENABLED // For debug builds, we must fetch from all native base methods as well. // Native base methods must be fetched before the current class. // Not needed if the script class itself is a native class. - if (script->script_class != script->native) { - GDMonoClass *native_top = script->native; + if (p_script->script_class != p_script->native) { + GDMonoClass *native_top = p_script->native; while (native_top) { - native_top->fetch_methods_with_godot_api_checks(script->native); + native_top->fetch_methods_with_godot_api_checks(p_script->native); if (native_top == CACHED_CLASS(GodotObject)) break; @@ -2272,18 +2749,19 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) { } #endif - script->script_class->fetch_methods_with_godot_api_checks(script->native); + p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native); // Need to fetch method from base classes as well - GDMonoClass *top = script->script_class; - while (top && top != script->native) { - top->fetch_methods_with_godot_api_checks(script->native); + GDMonoClass *top = p_script->script_class; + while (top && top != p_script->native) { + top->fetch_methods_with_godot_api_checks(p_script->native); top = top->get_parent_class(); } - script->load_script_signals(script->script_class, script->native); - - return script; + p_script->load_script_signals(p_script->script_class, p_script->native); +#ifdef TOOLS_ENABLED + p_script->_update_member_info_no_exports(); +#endif } bool CSharpScript::can_instance() const { @@ -2291,7 +2769,8 @@ bool CSharpScript::can_instance() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - if (get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted... + // Hack to lower the risk of attached scripts not being added to the C# project + if (!get_path().empty() && get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted... if (_create_project_solution_if_needed()) { CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(), "Compile", @@ -2339,22 +2818,66 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 1, CREATE */ + // Search the constructor first, to fail with an error if it's not found before allocating anything else. + GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); + if (ctor == NULL) { + if (p_argcount == 0) { + String path = get_path(); + ERR_PRINTS("Cannot create script instance. The class '" + script_class->get_full_name() + + "' does not define a parameterless constructor." + (path.empty() ? String() : ". Path: " + path)); + } + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + + Ref<Reference> ref; + if (p_isref) { + // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. + ref = Ref<Reference>(static_cast<Reference *>(p_owner)); + } + + // If the object had a script instance binding, dispose it before adding the CSharpInstance + if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) { + void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + CRASH_COND(data == NULL); + + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited && script_binding.gchandle.is_valid()) { + MonoObject *mono_object = script_binding.gchandle->get_target(); + if (mono_object) { + MonoException *exc = NULL; + GDMonoUtils::dispose(mono_object, &exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } + } + + script_binding.inited = false; + } + } + CSharpInstance *instance = memnew(CSharpInstance); instance->base_ref = p_isref; instance->script = Ref<CSharpScript>(this); instance->owner = p_owner; instance->owner->set_script_instance(instance); - if (instance->base_ref) - instance->_reference_owner_unsafe(); - /* STEP 2, INITIALIZE AND CONSTRUCT */ - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr()); if (!mono_object) { + // Important to clear this before destroying the script instance here instance->script = Ref<CSharpScript>(); - instance->owner->set_script_instance(NULL); + instance->owner = NULL; + + bool die = instance->_unreference_owner_unsafe(); + // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug. + CRASH_COND(die == true); + + p_owner->set_script_instance(NULL); r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; ERR_EXPLAIN("Failed to allocate memory for the object"); ERR_FAIL_V(NULL); @@ -2363,6 +2886,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Tie managed to unmanaged instance->gchandle = MonoGCHandle::create_strong(mono_object); + if (instance->base_ref) + instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback) + { SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); instances.insert(instance->owner); @@ -2371,7 +2897,6 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner); // Construct - GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); ctor->invoke(mono_object, p_args); /* STEP 3, PARTY */ @@ -2422,7 +2947,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { #endif if (native) { - String native_name = native->get_name(); + String native_name = NATIVE_GDMONOCLASS_NAME(native); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { if (ScriptDebugger::get_singleton()) { CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); @@ -2433,7 +2958,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { } Variant::CallError unchecked_error; - return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error); + return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this) != NULL, unchecked_error); } PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_this) { @@ -2474,6 +2999,18 @@ void CSharpScript::set_source_code(const String &p_code) { #endif } +void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { + + if (!script_class) + return; + + // TODO: Filter out things unsuitable for explicit calls, like constructors. + const Vector<GDMonoMethod *> &methods = script_class->get_all_methods(); + for (int i = 0; i < methods.size(); ++i) { + p_list->push_back(methods[i]->get_method_info()); + } +} + bool CSharpScript::has_method(const StringName &p_method) const { if (!script_class) @@ -2482,6 +3019,25 @@ bool CSharpScript::has_method(const StringName &p_method) const { return script_class->has_fetched_method_unknown_params(p_method); } +MethodInfo CSharpScript::get_method_info(const StringName &p_method) const { + + if (!script_class) + return MethodInfo(); + + GDMonoClass *top = script_class; + + while (top && top != native) { + GDMonoMethod *params = top->get_fetched_method_unknown_params(p_method); + if (params) { + return params->get_method_info(); + } + + top = top->get_parent_class(); + } + + return MethodInfo(); +} + Error CSharpScript::reload(bool p_keep_state) { bool has_instances; @@ -2517,11 +3073,22 @@ Error CSharpScript::reload(bool p_keep_state) { if (script_class) { #ifdef DEBUG_ENABLED - print_verbose("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path()); + print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path()); #endif tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); + if (!tool) { + GDMonoClass *nesting_class = script_class->get_nesting_class(); + tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute)); + } + +#if TOOLS_ENABLED + if (!tool) { + tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly(); + } +#endif + native = GDMonoUtils::get_class_native_base(script_class); CRASH_COND(native == NULL); @@ -2559,6 +3126,7 @@ Error CSharpScript::reload(bool p_keep_state) { } load_script_signals(script_class, native); + _update_exports(); } return OK; @@ -2598,10 +3166,7 @@ void CSharpScript::update_exports() { } bool CSharpScript::has_script_signal(const StringName &p_signal) const { - if (_signals.has(p_signal)) - return true; - - return false; + return _signals.has(p_signal); } void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { @@ -2666,6 +3231,7 @@ CSharpScript::CSharpScript() : #ifdef TOOLS_ENABLED source_changed_cache = false; + placeholder_fallback_enabled = false; exports_invalidated = true; #endif @@ -2720,7 +3286,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p #endif #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { + MonoDomain *domain = mono_domain_get(); + if (Engine::get_singleton()->is_editor_hint() && domain == NULL) { CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); @@ -2728,8 +3295,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p // because this may be called by one of the editor's worker threads. // Attach this thread temporarily to reload the script. - if (SCRIPTS_DOMAIN) { - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + if (domain) { + MonoThread *mono_thread = mono_thread_attach(domain); CRASH_COND(mono_thread == NULL); script->reload(); mono_thread_detach(mono_thread); @@ -2800,9 +3367,11 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r file->close(); memdelete(file); +#ifdef TOOLS_ENABLED if (ScriptServer::is_reload_scripts_on_save_enabled()) { CSharpLanguage::get_singleton()->reload_tool_script(p_resource, false); } +#endif return OK; } @@ -2824,7 +3393,10 @@ CSharpLanguage::StringNameCache::StringNameCache() { _signal_callback = StaticCString::create("_signal_callback"); _set = StaticCString::create("_set"); _get = StaticCString::create("_get"); + _get_property_list = StaticCString::create("_get_property_list"); _notification = StaticCString::create("_notification"); _script_source = StaticCString::create("script/source"); + on_before_serialize = StaticCString::create("OnBeforeSerialize"); + on_after_deserialize = StaticCString::create("OnAfterDeserialize"); dotctor = StaticCString::create(".ctor"); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 501e0d9d6d..eb168f344d 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,6 +41,10 @@ #include "mono_gd/gd_mono_header.h" #include "mono_gd/gd_mono_internals.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_plugin.h" +#endif + class CSharpScript; class CSharpInstance; class CSharpLanguage; @@ -63,7 +67,7 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { class CSharpScript : public Script { - GDCLASS(CSharpScript, Script) + GDCLASS(CSharpScript, Script); friend class CSharpInstance; friend class CSharpLanguage; @@ -82,10 +86,7 @@ class CSharpScript : public Script { Set<Object *> instances; -#ifdef DEBUG_ENABLED - Set<ObjectID> pending_reload_instances; -#endif - +#ifdef GD_MONO_HOT_RELOAD struct StateBackup { // TODO // Replace with buffer containing the serialized state of managed scripts. @@ -93,8 +94,10 @@ class CSharpScript : public Script { List<Pair<StringName, Variant> > properties; }; -#ifdef TOOLS_ENABLED - Map<ObjectID, CSharpScript::StateBackup> pending_reload_state; + Set<ObjectID> pending_reload_instances; + Map<ObjectID, StateBackup> pending_reload_state; + StringName tied_class_name_for_reload; + StringName tied_class_namespace_for_reload; #endif String source; @@ -115,8 +118,10 @@ class CSharpScript : public Script { Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache Set<PlaceHolderScriptInstance *> placeholders; bool source_changed_cache; + bool placeholder_fallback_enabled; bool exports_invalidated; void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); + void _update_member_info_no_exports(); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); #endif @@ -129,7 +134,8 @@ class CSharpScript : public Script { bool _update_exports(); #ifdef TOOLS_ENABLED - bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported); + static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string); #endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); @@ -137,7 +143,8 @@ class CSharpScript : public Script { // Do not use unless you know what you are doing friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); - static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class); + static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native); + static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native); protected: static void _bind_methods(); @@ -164,7 +171,7 @@ public: virtual bool has_script_signal(const StringName &p_signal) const; virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; - /* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const; + virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const; virtual void get_script_property_list(List<PropertyInfo> *p_list) const; virtual void update_exports(); @@ -174,12 +181,16 @@ public: virtual Ref<Script> get_base_script() const; virtual ScriptLanguage *get_language() const; - /* TODO */ virtual void get_script_method_list(List<MethodInfo> *p_list) const {} + virtual void get_script_method_list(List<MethodInfo> *p_list) const; bool has_method(const StringName &p_method) const; - /* TODO */ MethodInfo get_method_info(const StringName &p_method) const { return MethodInfo(); } + MethodInfo get_method_info(const StringName &p_method) const; virtual int get_member_line(const StringName &p_member) const; +#ifdef TOOLS_ENABLED + virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; } +#endif + Error load_source_code(const String &p_path); StringName get_script_name() const; @@ -204,8 +215,15 @@ class CSharpInstance : public ScriptInstance { Ref<MonoGCHandle> gchandle; bool _reference_owner_unsafe(); + + /* + * If true is returned, the caller must memdelete the script instance's owner. + */ bool _unreference_owner_unsafe(); + /* + * If NULL is returned, the caller must destroy the script instance by removing it from its owner. + */ MonoObject *_internal_new_managed(); // Do not use unless you know what you are doing @@ -214,13 +232,17 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); - MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; + + void get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state); public: MonoObject *get_mono_object() const; _FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; } + virtual Object *get_owner(); + virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; virtual void get_property_list(List<PropertyInfo> *p_properties) const; @@ -233,7 +255,12 @@ public: virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); void mono_object_disposed(MonoObject *p_obj); - void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted); + + /* + * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if + * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. + */ + void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); virtual void refcount_incremented(); virtual bool refcount_decremented(); @@ -244,6 +271,8 @@ public: virtual void notification(int p_notification); void _call_notification(int p_notification); + virtual String to_string(bool *r_valid); + virtual Ref<Script> get_script() const; virtual ScriptLanguage *get_language(); @@ -253,9 +282,11 @@ public: }; struct CSharpScriptBinding { + bool inited; StringName type_name; GDMonoClass *wrapper_class; Ref<MonoGCHandle> gchandle; + Object *owner; }; class CSharpLanguage : public ScriptLanguage { @@ -281,9 +312,12 @@ class CSharpLanguage : public ScriptLanguage { StringName _signal_callback; StringName _set; StringName _get; + StringName _get_property_list; StringName _notification; StringName _script_source; StringName dotctor; // .ctor + StringName on_before_serialize; // OnBeforeSerialize + StringName on_after_deserialize; // OnAfterDeserialize StringNameCache(); }; @@ -291,10 +325,29 @@ class CSharpLanguage : public ScriptLanguage { int lang_idx; Dictionary scripts_metadata; + bool scripts_metadata_invalidated; + + // For debug_break and debug_break_parse + int _debug_parse_err_line; + String _debug_parse_err_file; + String _debug_error; + + void _load_scripts_metadata(); + + friend class GDMono; + void _on_scripts_domain_unloaded(); + +#ifdef TOOLS_ENABLED + EditorPlugin *godotsharp_editor; + + static void _editor_init_callback(); +#endif public: StringNameCache string_names; + Mutex *get_language_bind_mutex() { return language_bind_mutex; } + _FORCE_INLINE_ int get_language_index() { return lang_idx; } void set_language_index(int p_idx); @@ -302,20 +355,30 @@ public: _FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; } +#ifdef TOOLS_ENABLED + _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; } +#endif + static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle); - static void release_script_gchandle(MonoObject *p_pinned_expected_obj, Ref<MonoGCHandle> &p_gchandle); + static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle); bool debug_break(const String &p_error, bool p_allow_continue = true); bool debug_break_parse(const String &p_file, int p_line, const String &p_error); -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD bool is_assembly_reloading_needed(); void reload_assemblies(bool p_soft_reload); #endif - void project_assembly_loaded(); + _FORCE_INLINE_ Dictionary get_scripts_metadata_or_nothing() { + return scripts_metadata_invalidated ? Dictionary() : scripts_metadata; + } - _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; } + _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { + if (scripts_metadata_invalidated) + _load_scripts_metadata(); + return scripts_metadata; + } virtual String get_name() const; @@ -340,17 +403,16 @@ public: virtual bool supports_builtin_mode() const; /* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; } virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - /* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; } virtual String _get_indentation() const; /* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {} /* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {} /* DEBUGGER FUNCTIONS */ - /* TODO */ virtual String debug_get_error() const { return ""; } - /* TODO */ virtual int debug_get_stack_level_count() const { return 1; } - /* TODO */ virtual int debug_get_stack_level_line(int p_level) const { return 1; } - /* TODO */ virtual String debug_get_stack_level_function(int p_level) const { return ""; } - /* TODO */ virtual String debug_get_stack_level_source(int p_level) const { return ""; } + virtual String debug_get_error() const; + virtual int debug_get_stack_level_count() const; + virtual int debug_get_stack_level_line(int p_level) const; + virtual String debug_get_stack_level_function(int p_level) const; + virtual String debug_get_stack_level_source(int p_level) const; /* TODO */ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} /* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} /* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {} @@ -389,6 +451,9 @@ public: virtual void refcount_incremented_instance_binding(Object *p_object); virtual bool refcount_decremented_instance_binding(Object *p_object); + Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding); + bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object); + #ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); #endif diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index 082bc30fd8..826c106d7e 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@C#" category="Core" version="3.1"> +<class name="@C#" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index a1f7399653..de2e246ea9 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" category="Core" version="3.1"> +<class name="CSharpScript" inherits="Script" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new" qualifiers="vararg"> <return type="Object"> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 921c1ca825..18556a84ba 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" category="Core" version="3.1"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="attach_thread"> <return type="void"> diff --git a/modules/mono/editor/GodotSharpTools/.gitignore b/modules/mono/editor/GodotSharpTools/.gitignore deleted file mode 100644 index 296ad48834..0000000000 --- a/modules/mono/editor/GodotSharpTools/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# nuget packages -packages
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs deleted file mode 100644 index 16beacb45c..0000000000 --- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; -using Microsoft.Build.Framework; - -namespace GodotSharpTools.Build -{ - public class BuildInstance : IDisposable - { - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static string godot_icall_BuildInstance_get_MSBuildPath(); - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static string godot_icall_BuildInstance_get_FrameworkPath(); - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir(); - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows(); - - private static string GetMSBuildPath() - { - string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath(); - - if (msbuildPath == null) - throw new FileNotFoundException("Cannot find the MSBuild executable."); - - return msbuildPath; - } - - private static string GetFrameworkPath() - { - return godot_icall_BuildInstance_get_FrameworkPath(); - } - - private static string MonoWindowsBinDir - { - get - { - string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir(); - - if (monoWinBinDir == null) - throw new FileNotFoundException("Cannot find the Windows Mono binaries directory."); - - return monoWinBinDir; - } - } - - private static bool UsingMonoMSBuildOnWindows - { - get - { - return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows(); - } - } - - private string solution; - private string config; - - private Process process; - - private int exitCode; - public int ExitCode { get { return exitCode; } } - - public bool IsRunning { get { return process != null && !process.HasExited; } } - - public BuildInstance(string solution, string config) - { - this.solution = solution; - this.config = config; - } - - public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) - { - bool debugMSBuild = IsDebugMSBuildRequested(); - - List<string> customPropertiesList = new List<string>(); - - if (customProperties != null) - customPropertiesList.AddRange(customProperties); - - string frameworkPath = GetFrameworkPath(); - - if (!string.IsNullOrEmpty(frameworkPath)) - customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath); - - string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); - - ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); - - bool redirectOutput = !debugMSBuild; - - startInfo.RedirectStandardOutput = redirectOutput; - startInfo.RedirectStandardError = redirectOutput; - startInfo.UseShellExecute = false; - - if (UsingMonoMSBuildOnWindows) - { - // These environment variables are required for Mono's MSBuild to find the compilers. - // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono. - string monoWinBinDir = MonoWindowsBinDir; - startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat")); - startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat")); - startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat")); - } - - // Needed when running from Developer Command Prompt for VS - RemovePlatformVariable(startInfo.EnvironmentVariables); - - using (Process process = new Process()) - { - process.StartInfo = startInfo; - - process.Start(); - - if (redirectOutput) - { - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } - - process.WaitForExit(); - - exitCode = process.ExitCode; - } - - return true; - } - - public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) - { - bool debugMSBuild = IsDebugMSBuildRequested(); - - if (process != null) - throw new InvalidOperationException("Already in use"); - - List<string> customPropertiesList = new List<string>(); - - if (customProperties != null) - customPropertiesList.AddRange(customProperties); - - string frameworkPath = GetFrameworkPath(); - - if (!string.IsNullOrEmpty(frameworkPath)) - customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath); - - string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); - - ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); - - bool redirectOutput = !debugMSBuild; - - startInfo.RedirectStandardOutput = redirectOutput; - startInfo.RedirectStandardError = redirectOutput; - startInfo.UseShellExecute = false; - - if (UsingMonoMSBuildOnWindows) - { - // These environment variables are required for Mono's MSBuild to find the compilers. - // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono. - string monoWinBinDir = MonoWindowsBinDir; - startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat")); - startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat")); - startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat")); - } - - // Needed when running from Developer Command Prompt for VS - RemovePlatformVariable(startInfo.EnvironmentVariables); - - process = new Process(); - process.StartInfo = startInfo; - process.EnableRaisingEvents = true; - process.Exited += new EventHandler(BuildProcess_Exited); - - process.Start(); - - if (redirectOutput) - { - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } - - return true; - } - - private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties) - { - string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""", - solution, - "Configuration=" + config, - typeof(GodotBuildLogger).FullName, - loggerAssemblyPath, - loggerOutputDir - ); - - foreach (string customProperty in customProperties) - { - arguments += " \"/p:" + customProperty + "\""; - } - - return arguments; - } - - private void RemovePlatformVariable(StringDictionary environmentVariables) - { - // EnvironmentVariables is case sensitive? Seriously? - - List<string> platformEnvironmentVariables = new List<string>(); - - foreach (string env in environmentVariables.Keys) - { - if (env.ToUpper() == "PLATFORM") - platformEnvironmentVariables.Add(env); - } - - foreach (string env in platformEnvironmentVariables) - environmentVariables.Remove(env); - } - - private void BuildProcess_Exited(object sender, System.EventArgs e) - { - exitCode = process.ExitCode; - - godot_icall_BuildInstance_ExitCallback(solution, config, exitCode); - - Dispose(); - } - - private static bool IsDebugMSBuildRequested() - { - return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1"; - } - - public void Dispose() - { - if (process != null) - { - process.Dispose(); - process = null; - } - } - } - - public class GodotBuildLogger : ILogger - { - public string Parameters { get; set; } - public LoggerVerbosity Verbosity { get; set; } - - public void Initialize(IEventSource eventSource) - { - if (null == Parameters) - throw new LoggerException("Log directory was not set."); - - string[] parameters = Parameters.Split(';'); - - string logDir = parameters[0]; - - if (String.IsNullOrEmpty(logDir)) - throw new LoggerException("Log directory was not set."); - - if (parameters.Length > 1) - throw new LoggerException("Too many parameters passed."); - - string logFile = Path.Combine(logDir, "msbuild_log.txt"); - string issuesFile = Path.Combine(logDir, "msbuild_issues.csv"); - - try - { - if (!Directory.Exists(logDir)) - Directory.CreateDirectory(logDir); - - this.logStreamWriter = new StreamWriter(logFile); - this.issuesStreamWriter = new StreamWriter(issuesFile); - } - catch (Exception ex) - { - if - ( - ex is UnauthorizedAccessException - || ex is ArgumentNullException - || ex is PathTooLongException - || ex is DirectoryNotFoundException - || ex is NotSupportedException - || ex is ArgumentException - || ex is SecurityException - || ex is IOException - ) - { - throw new LoggerException("Failed to create log file: " + ex.Message); - } - else - { - // Unexpected failure - throw; - } - } - - eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted); - eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted); - eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised); - eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised); - eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised); - eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished); - } - - void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e) - { - string line = String.Format("{0}({1},{2}): error {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message); - - if (e.ProjectFile.Length > 0) - line += string.Format(" [{0}]", e.ProjectFile); - - WriteLine(line); - - string errorLine = String.Format(@"error,{0},{1},{2},{3},{4},{5}", - e.File.CsvEscape(), e.LineNumber, e.ColumnNumber, - e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile.CsvEscape()); - issuesStreamWriter.WriteLine(errorLine); - } - - void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) - { - string line = String.Format("{0}({1},{2}): warning {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile); - - if (e.ProjectFile != null && e.ProjectFile.Length > 0) - line += string.Format(" [{0}]", e.ProjectFile); - - WriteLine(line); - - string warningLine = String.Format(@"warning,{0},{1},{2},{3},{4},{5}", - e.File.CsvEscape(), e.LineNumber, e.ColumnNumber, - e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty); - issuesStreamWriter.WriteLine(warningLine); - } - - void eventSource_MessageRaised(object sender, BuildMessageEventArgs e) - { - // BuildMessageEventArgs adds Importance to BuildEventArgs - // Let's take account of the verbosity setting we've been passed in deciding whether to log the message - if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)) - || (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)) - || (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed)) - ) - { - WriteLineWithSenderAndMessage(String.Empty, e); - } - } - - void eventSource_TaskStarted(object sender, TaskStartedEventArgs e) - { - // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName - // To keep this log clean, this logger will ignore these events. - } - - void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) - { - WriteLine(e.Message); - indent++; - } - - void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) - { - indent--; - WriteLine(e.Message); - } - - /// <summary> - /// Write a line to the log, adding the SenderName - /// </summary> - private void WriteLineWithSender(string line, BuildEventArgs e) - { - if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/)) - { - // Well, if the sender name is MSBuild, let's leave it out for prettiness - WriteLine(line); - } - else - { - WriteLine(e.SenderName + ": " + line); - } - } - - /// <summary> - /// Write a line to the log, adding the SenderName and Message - /// (these parameters are on all MSBuild event argument objects) - /// </summary> - private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e) - { - if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/)) - { - // Well, if the sender name is MSBuild, let's leave it out for prettiness - WriteLine(line + e.Message); - } - else - { - WriteLine(e.SenderName + ": " + line + e.Message); - } - } - - private void WriteLine(string line) - { - for (int i = indent; i > 0; i--) - { - logStreamWriter.Write("\t"); - } - logStreamWriter.WriteLine(line); - } - - public void Shutdown() - { - logStreamWriter.Close(); - issuesStreamWriter.Close(); - } - - public bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity) - { - return this.Verbosity >= checkVerbosity; - } - - private StreamWriter logStreamWriter; - private StreamWriter issuesStreamWriter; - private int indent; - } -} diff --git a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs deleted file mode 100644 index 5fd708d539..0000000000 --- a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; - -namespace GodotSharpTools.Editor -{ - public static class GodotSharpExport - { - public static void _ExportBegin(string[] features, bool debug, string path, int flags) - { - var featureSet = new HashSet<string>(features); - - if (PlatformHasTemplateDir(featureSet)) - { - string templateDirName = "data.mono"; - - if (featureSet.Contains("Windows")) - { - templateDirName += ".windows"; - templateDirName += featureSet.Contains("64") ? ".64" : ".32"; - } - else if (featureSet.Contains("X11")) - { - templateDirName += ".x11"; - templateDirName += featureSet.Contains("64") ? ".64" : ".32"; - } - else - { - throw new NotSupportedException("Target platform not supported"); - } - - templateDirName += debug ? ".debug" : ".release"; - - string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName); - - if (!Directory.Exists(templateDirPath)) - throw new FileNotFoundException("Data template directory not found"); - - string outputDir = new FileInfo(path).Directory.FullName; - - string outputDataDir = Path.Combine(outputDir, GetDataDirName()); - - if (Directory.Exists(outputDataDir)) - Directory.Delete(outputDataDir, recursive: true); // Clean first - - Directory.CreateDirectory(outputDataDir); - - foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1))); - } - - foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories)) - { - File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1))); - } - } - } - - public static bool PlatformHasTemplateDir(HashSet<string> featureSet) - { - // OSX export templates are contained in a zip, so we place - // our custom template inside it and let Godot do the rest. - return !featureSet.Contains("OSX"); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - extern static string GetTemplatesDir(); - - [MethodImpl(MethodImplOptions.InternalCall)] - extern static string GetDataDirName(); - } -} diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln deleted file mode 100644 index 5f7d0e8a39..0000000000 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln +++ /dev/null @@ -1,17 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs deleted file mode 100644 index a13f4fd6ef..0000000000 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using DotNet.Globbing; -using Microsoft.Build.Construction; - -namespace GodotSharpTools.Project -{ - public static class ProjectUtils - { - public static void AddItemToProjectChecked(string projectPath, string itemType, string include) - { - var dir = Directory.GetParent(projectPath).FullName; - var root = ProjectRootElement.Open(projectPath); - var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\"); - - if (root.AddItemChecked(itemType, normalizedInclude)) - root.Save(); - } - - private static string[] GetAllFilesRecursive(string rootDirectory, string mask) - { - string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); - - // We want relative paths - for (int i = 0; i < files.Length; i++) { - files[i] = files[i].RelativeToPath(rootDirectory); - } - - return files; - } - - public static string[] GetIncludeFiles(string projectPath, string itemType) - { - var result = new List<string>(); - var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs"); - - GlobOptions globOptions = new GlobOptions(); - globOptions.Evaluation.CaseInsensitive = false; - - var root = ProjectRootElement.Open(projectPath); - - foreach (var itemGroup in root.ItemGroups) - { - if (itemGroup.Condition.Length != 0) - continue; - - foreach (var item in itemGroup.Items) - { - if (item.ItemType != itemType) - continue; - - string normalizedInclude = item.Include.NormalizePath(); - - var glob = Glob.Parse(normalizedInclude, globOptions); - - // TODO Check somehow if path has no blog to avoid the following loop... - - foreach (var existingFile in existingFiles) - { - if (glob.IsMatch(existingFile)) - { - result.Add(existingFile); - } - } - } - } - - return result.ToArray(); - } - } -} diff --git a/modules/mono/editor/GodotSharpTools/Utils/OS.cs b/modules/mono/editor/GodotSharpTools/Utils/OS.cs deleted file mode 100644 index 148e954e77..0000000000 --- a/modules/mono/editor/GodotSharpTools/Utils/OS.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace GodotSharpTools.Utils -{ - public static class OS - { - [MethodImpl(MethodImplOptions.InternalCall)] - extern static string GetPlatformName(); - - const string HaikuName = "Haiku"; - const string OSXName = "OSX"; - const string ServerName = "Server"; - const string UWPName = "UWP"; - const string WindowsName = "Windows"; - const string X11Name = "X11"; - - public static bool IsHaiku() - { - return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsOSX() - { - return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsServer() - { - return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsUWP() - { - return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsWindows() - { - return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - public static bool IsX11() - { - return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); - } - - static bool? IsUnixCache = null; - static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name }; - - public static bool IsUnix() - { - if (IsUnixCache.HasValue) - return IsUnixCache.Value; - - string osName = GetPlatformName(); - IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); - return IsUnixCache.Value; - } - } -} diff --git a/modules/mono/editor/GodotTools/.gitignore b/modules/mono/editor/GodotTools/.gitignore new file mode 100644 index 0000000000..48e2f914d8 --- /dev/null +++ b/modules/mono/editor/GodotTools/.gitignore @@ -0,0 +1,356 @@ +# Rider +.idea/ + +# Visual Studio Code +.vscode/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs new file mode 100644 index 0000000000..a0f6f1ff32 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs @@ -0,0 +1,186 @@ +using System; +using System.IO; +using System.Security; +using Microsoft.Build.Framework; +using GodotTools.Core; + +namespace GodotTools.BuildLogger +{ + public class GodotBuildLogger : ILogger + { + public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location); + + public string Parameters { get; set; } + public LoggerVerbosity Verbosity { get; set; } + + public void Initialize(IEventSource eventSource) + { + if (null == Parameters) + throw new LoggerException("Log directory was not set."); + + var parameters = Parameters.Split(new[] {';'}); + + string logDir = parameters[0]; + + if (string.IsNullOrEmpty(logDir)) + throw new LoggerException("Log directory was not set."); + + if (parameters.Length > 1) + throw new LoggerException("Too many parameters passed."); + + string logFile = Path.Combine(logDir, "msbuild_log.txt"); + string issuesFile = Path.Combine(logDir, "msbuild_issues.csv"); + + try + { + if (!Directory.Exists(logDir)) + Directory.CreateDirectory(logDir); + + logStreamWriter = new StreamWriter(logFile); + issuesStreamWriter = new StreamWriter(issuesFile); + } + catch (Exception ex) + { + if (ex is UnauthorizedAccessException + || ex is ArgumentNullException + || ex is PathTooLongException + || ex is DirectoryNotFoundException + || ex is NotSupportedException + || ex is ArgumentException + || ex is SecurityException + || ex is IOException) + { + throw new LoggerException("Failed to create log file: " + ex.Message); + } + else + { + // Unexpected failure + throw; + } + } + + eventSource.ProjectStarted += eventSource_ProjectStarted; + eventSource.TaskStarted += eventSource_TaskStarted; + eventSource.MessageRaised += eventSource_MessageRaised; + eventSource.WarningRaised += eventSource_WarningRaised; + eventSource.ErrorRaised += eventSource_ErrorRaised; + eventSource.ProjectFinished += eventSource_ProjectFinished; + } + + void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e) + { + string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}"; + + if (e.ProjectFile.Length > 0) + line += $" [{e.ProjectFile}]"; + + WriteLine(line); + + string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," + + $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}"; + issuesStreamWriter.WriteLine(errorLine); + } + + void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) + { + string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}"; + + if (!string.IsNullOrEmpty(e.ProjectFile)) + line += $" [{e.ProjectFile}]"; + + WriteLine(line); + + string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," + + $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}"; + issuesStreamWriter.WriteLine(warningLine); + } + + private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e) + { + // BuildMessageEventArgs adds Importance to BuildEventArgs + // Let's take account of the verbosity setting we've been passed in deciding whether to log the message + if (e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal) + || e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal) + || e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed)) + { + WriteLineWithSenderAndMessage(string.Empty, e); + } + } + + private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e) + { + // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName + // To keep this log clean, this logger will ignore these events. + } + + private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) + { + WriteLine(e.Message); + indent++; + } + + private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) + { + indent--; + WriteLine(e.Message); + } + + /// <summary> + /// Write a line to the log, adding the SenderName + /// </summary> + private void WriteLineWithSender(string line, BuildEventArgs e) + { + if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase)) + { + // Well, if the sender name is MSBuild, let's leave it out for prettiness + WriteLine(line); + } + else + { + WriteLine(e.SenderName + ": " + line); + } + } + + /// <summary> + /// Write a line to the log, adding the SenderName and Message + /// (these parameters are on all MSBuild event argument objects) + /// </summary> + private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e) + { + if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase)) + { + // Well, if the sender name is MSBuild, let's leave it out for prettiness + WriteLine(line + e.Message); + } + else + { + WriteLine(e.SenderName + ": " + line + e.Message); + } + } + + private void WriteLine(string line) + { + for (int i = indent; i > 0; i--) + { + logStreamWriter.Write("\t"); + } + + logStreamWriter.WriteLine(line); + } + + public void Shutdown() + { + logStreamWriter.Close(); + issuesStreamWriter.Close(); + } + + private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity) + { + return Verbosity >= checkVerbosity; + } + + private StreamWriter logStreamWriter; + private StreamWriter issuesStreamWriter; + private int indent; + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj new file mode 100644 index 0000000000..f3ac353c0f --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>GodotTools.BuildLogger</RootNamespace> + <AssemblyName>GodotTools.BuildLogger</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>portable</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>portable</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="GodotBuildLogger.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj"> + <Project>{639e48bd-44e5-4091-8edd-22d36dc0768d}</Project> + <Name>GodotTools.Core</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8717c4901e --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GodotTools.BuildLogger")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Godot Engine contributors")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6CE9A984-37B1-4F8A-8FE9-609F05F071B3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj index f9e9f41977..f36b40f87c 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj @@ -1,17 +1,17 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid> + <ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid> <OutputType>Library</OutputType> - <RootNamespace>GodotSharpTools</RootNamespace> - <AssemblyName>GodotSharpTools</AssemblyName> + <RootNamespace>GodotTools.Core</RootNamespace> + <AssemblyName>GodotTools.Core</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> - <DebugType>portable</DebugType> + <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> @@ -20,7 +20,6 @@ <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>portable</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> @@ -29,25 +28,11 @@ </PropertyGroup> <ItemGroup> <Reference Include="System" /> - <Reference Include="Microsoft.Build" /> - <Reference Include="Microsoft.Build.Framework" /> - <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL"> - <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> - <Compile Include="StringExtensions.cs" /> - <Compile Include="Build\BuildSystem.cs" /> - <Compile Include="Editor\MonoDevelopInstance.cs" /> - <Compile Include="Project\ProjectExtensions.cs" /> - <Compile Include="Project\ProjectGenerator.cs" /> - <Compile Include="Project\ProjectUtils.cs" /> + <Compile Include="ProcessExtensions.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Utils\OS.cs" /> - <Compile Include="Editor\GodotSharpExport.cs" /> - </ItemGroup> - <ItemGroup> - <None Include="packages.config" /> + <Compile Include="StringExtensions.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs new file mode 100644 index 0000000000..43d40f2ad9 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace GodotTools.Core +{ + public static class ProcessExtensions + { + public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken)) + { + var tcs = new TaskCompletionSource<bool>(); + + void ProcessExited(object sender, EventArgs e) + { + tcs.TrySetResult(true); + } + + process.EnableRaisingEvents = true; + process.Exited += ProcessExited; + + try + { + if (process.HasExited) + return; + + using (cancellationToken.Register(() => tcs.TrySetCanceled())) + { + await tcs.Task; + } + } + finally + { + process.Exited -= ProcessExited; + } + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..699ae6e741 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("GodotTools.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Godot Engine contributors")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index b0436d2f18..8cd7e76303 100644 --- a/modules/mono/editor/GodotSharpTools/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -1,7 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; -namespace GodotSharpTools +namespace GodotTools.Core { public static class StringExtensions { @@ -25,7 +26,7 @@ namespace GodotSharpTools path = path.Replace('\\', '/'); - string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim(); @@ -37,18 +38,40 @@ namespace GodotSharpTools public static bool IsAbsolutePath(this string path) { return path.StartsWith("/", StringComparison.Ordinal) || - path.StartsWith("\\", StringComparison.Ordinal) || - path.StartsWith(driveRoot, StringComparison.Ordinal); + path.StartsWith("\\", StringComparison.Ordinal) || + path.StartsWith(driveRoot, StringComparison.Ordinal); } public static string CsvEscape(this string value, char delimiter = ',') { - bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1; + bool hasSpecialChar = value.IndexOfAny(new char[] {'\"', '\n', '\r', delimiter}) != -1; if (hasSpecialChar) return "\"" + value.Replace("\"", "\"\"") + "\""; return value; } + + public static string ToSafeDirName(this string dirName, bool allowDirSeparator) + { + var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"}; + + if (allowDirSeparator) + { + // Directory separators are allowed, but disallow ".." to avoid going up the filesystem + invalidChars.Add(".."); + } + else + { + invalidChars.Add("/"); + } + + string safeDirName = dirName.Replace("\\", "/").Trim(); + + foreach (string invalidChar in invalidChars) + safeDirName = safeDirName.Replace(invalidChar, "-"); + + return safeDirName; + } } } diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiAssembliesInfo.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiAssembliesInfo.cs new file mode 100644 index 0000000000..345a472185 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiAssembliesInfo.cs @@ -0,0 +1,15 @@ +namespace GodotTools +{ + public static class ApiAssemblyNames + { + public const string SolutionName = "GodotSharp"; + public const string Core = "GodotSharp"; + public const string Editor = "GodotSharpEditor"; + } + + public enum ApiAssemblyType + { + Core, + Editor + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiSolutionGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiSolutionGenerator.cs new file mode 100644 index 0000000000..bfae2afc13 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiSolutionGenerator.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; + +namespace GodotTools.ProjectEditor +{ + public static class ApiSolutionGenerator + { + public static void GenerateApiSolution(string solutionDir, + string coreProjDir, IEnumerable<string> coreCompileItems, + string editorProjDir, IEnumerable<string> editorCompileItems) + { + var solution = new DotNetSolution(ApiAssemblyNames.SolutionName); + + solution.DirectoryPath = solutionDir; + + // GodotSharp project + + const string coreApiAssemblyName = ApiAssemblyNames.Core; + + string coreGuid = ProjectGenerator.GenCoreApiProject(coreProjDir, coreCompileItems); + + var coreProjInfo = new DotNetSolution.ProjectInfo + { + Guid = coreGuid, + PathRelativeToSolution = Path.Combine(coreApiAssemblyName, $"{coreApiAssemblyName}.csproj") + }; + coreProjInfo.Configs.Add("Debug"); + coreProjInfo.Configs.Add("Release"); + + solution.AddNewProject(coreApiAssemblyName, coreProjInfo); + + // GodotSharpEditor project + + const string editorApiAssemblyName = ApiAssemblyNames.Editor; + + string editorGuid = ProjectGenerator.GenEditorApiProject(editorProjDir, + $"../{coreApiAssemblyName}/{coreApiAssemblyName}.csproj", editorCompileItems); + + var editorProjInfo = new DotNetSolution.ProjectInfo(); + editorProjInfo.Guid = editorGuid; + editorProjInfo.PathRelativeToSolution = Path.Combine(editorApiAssemblyName, $"{editorApiAssemblyName}.csproj"); + editorProjInfo.Configs.Add("Debug"); + editorProjInfo.Configs.Add("Release"); + + solution.AddNewProject(editorApiAssemblyName, editorProjInfo); + + // Save solution + + solution.Save(); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs new file mode 100644 index 0000000000..76cb249acf --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -0,0 +1,122 @@ +using GodotTools.Core; +using System.Collections.Generic; +using System.IO; + +namespace GodotTools.ProjectEditor +{ + public class DotNetSolution + { + private string directoryPath; + private readonly Dictionary<string, ProjectInfo> projects = new Dictionary<string, ProjectInfo>(); + + public string Name { get; } + + public string DirectoryPath + { + get => directoryPath; + set => directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value); + } + + public class ProjectInfo + { + public string Guid; + public string PathRelativeToSolution; + public List<string> Configs = new List<string>(); + } + + public void AddNewProject(string name, ProjectInfo projectInfo) + { + projects[name] = projectInfo; + } + + public bool HasProject(string name) + { + return projects.ContainsKey(name); + } + + public ProjectInfo GetProjectInfo(string name) + { + return projects[name]; + } + + public bool RemoveProject(string name) + { + return projects.Remove(name); + } + + public void Save() + { + if (!Directory.Exists(DirectoryPath)) + throw new FileNotFoundException("The solution directory does not exist."); + + string projectsDecl = string.Empty; + string slnPlatformsCfg = string.Empty; + string projPlatformsCfg = string.Empty; + + bool isFirstProject = true; + + foreach (var pair in projects) + { + string name = pair.Key; + ProjectInfo projectInfo = pair.Value; + + if (!isFirstProject) + projectsDecl += "\n"; + + projectsDecl += string.Format(ProjectDeclaration, + name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid); + + for (int i = 0; i < projectInfo.Configs.Count; i++) + { + string config = projectInfo.Configs[i]; + + if (i != 0 || !isFirstProject) + { + slnPlatformsCfg += "\n"; + projPlatformsCfg += "\n"; + } + + slnPlatformsCfg += string.Format(SolutionPlatformsConfig, config); + projPlatformsCfg += string.Format(ProjectPlatformsConfig, projectInfo.Guid, config); + } + + isFirstProject = false; + } + + string solutionPath = Path.Combine(DirectoryPath, Name + ".sln"); + string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg); + + File.WriteAllText(solutionPath, content); + } + + public DotNetSolution(string name) + { + Name = name; + } + + const string SolutionTemplate = +@"Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +{0} +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution +{1} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution +{2} + EndGlobalSection +EndGlobal +"; + + const string ProjectDeclaration = +@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}"" +EndProject"; + + const string SolutionPlatformsConfig = +@" {0}|Any CPU = {0}|Any CPU"; + + const string ProjectPlatformsConfig = +@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU + {{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU"; + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj new file mode 100644 index 0000000000..08b8ba3946 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>GodotTools.ProjectEditor</RootNamespace> + <AssemblyName>GodotTools.ProjectEditor</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>portable</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="Microsoft.Build" /> + <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL"> + <!-- + When building Godot with 'mono_glue=no' SCons will build this project alone instead of the + entire solution. $(SolutionDir) is not defined in that case, so we need to workaround that. + We make SCons restore the NuGet packages in the project directory instead in this case. + --> + <HintPath Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> + <HintPath>$(ProjectDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> + <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> <!-- Are you happy CI? --> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="ApiAssembliesInfo.cs" /> + <Compile Include="ApiSolutionGenerator.cs" /> + <Compile Include="DotNetSolution.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="IdentifierUtils.cs" /> + <Compile Include="ProjectExtensions.cs" /> + <Compile Include="ProjectGenerator.cs" /> + <Compile Include="ProjectUtils.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj"> + <Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project> + <Name>GodotTools.Core</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs new file mode 100644 index 0000000000..f93eb9a1fa --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace GodotTools.ProjectEditor +{ + public static class IdentifierUtils + { + public static string SanitizeQualifiedIdentifier(string qualifiedIdentifier, bool allowEmptyIdentifiers) + { + if (string.IsNullOrEmpty(qualifiedIdentifier)) + throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier)); + + string[] identifiers = qualifiedIdentifier.Split('.'); + + for (int i = 0; i < identifiers.Length; i++) + { + identifiers[i] = SanitizeIdentifier(identifiers[i], allowEmpty: allowEmptyIdentifiers); + } + + return string.Join(".", identifiers); + } + + public static string SanitizeIdentifier(string identifier, bool allowEmpty) + { + if (string.IsNullOrEmpty(identifier)) + { + if (allowEmpty) + return "Empty"; // Default value for empty identifiers + + throw new ArgumentException($"{nameof(identifier)} cannot be empty if {nameof(allowEmpty)} is false", nameof(identifier)); + } + + if (identifier.Length > 511) + identifier = identifier.Substring(0, 511); + + var identifierBuilder = new StringBuilder(); + int startIndex = 0; + + if (identifier[0] == '@') + { + identifierBuilder.Append('@'); + startIndex += 1; + } + + for (int i = startIndex; i < identifier.Length; i++) + { + char @char = identifier[i]; + + switch (Char.GetUnicodeCategory(@char)) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.LetterNumber: + case UnicodeCategory.OtherLetter: + identifierBuilder.Append(@char); + break; + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.SpacingCombiningMark: + case UnicodeCategory.ConnectorPunctuation: + case UnicodeCategory.DecimalDigitNumber: + // Identifiers may start with underscore + if (identifierBuilder.Length > startIndex || @char == '_') + identifierBuilder.Append(@char); + break; + } + } + + if (identifierBuilder.Length == startIndex) + { + // All characters were invalid so now it's empty. Fill it with something. + identifierBuilder.Append("Empty"); + } + + identifier = identifierBuilder.ToString(); + + if (identifier[0] != '@' && IsKeyword(identifier, anyDoubleUnderscore: true)) + identifier = '@' + identifier; + + return identifier; + } + + static bool IsKeyword(string value, bool anyDoubleUnderscore) + { + // Identifiers that start with double underscore are meant to be used for reserved keywords. + // Only existing keywords are enforced, but it may be useful to forbid any identifier + // that begins with double underscore to prevent issues with future C# versions. + if (anyDoubleUnderscore) + { + if (value.Length > 2 && value[0] == '_' && value[1] == '_' && value[2] != '_') + return true; + } + else + { + if (DoubleUnderscoreKeywords.Contains(value)) + return true; + } + + return Keywords.Contains(value); + } + + private static readonly HashSet<string> DoubleUnderscoreKeywords = new HashSet<string> + { + "__arglist", + "__makeref", + "__reftype", + "__refvalue", + }; + + private static readonly HashSet<string> Keywords = new HashSet<string> + { + "as", + "do", + "if", + "in", + "is", + "for", + "int", + "new", + "out", + "ref", + "try", + "base", + "bool", + "byte", + "case", + "char", + "else", + "enum", + "goto", + "lock", + "long", + "null", + "this", + "true", + "uint", + "void", + "break", + "catch", + "class", + "const", + "event", + "false", + "fixed", + "float", + "sbyte", + "short", + "throw", + "ulong", + "using", + "where", + "while", + "yield", + "double", + "extern", + "object", + "params", + "public", + "return", + "sealed", + "sizeof", + "static", + "string", + "struct", + "switch", + "typeof", + "unsafe", + "ushort", + "checked", + "decimal", + "default", + "finally", + "foreach", + "partial", + "private", + "virtual", + "abstract", + "continue", + "delegate", + "explicit", + "implicit", + "internal", + "operator", + "override", + "readonly", + "volatile", + "interface", + "namespace", + "protected", + "unchecked", + "stackalloc", + }; + } +} diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs index 647d9ac81d..36961eb45e 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs @@ -1,8 +1,9 @@ +using GodotTools.Core; using System; using DotNet.Globbing; using Microsoft.Build.Construction; -namespace GodotSharpTools.Project +namespace GodotTools.ProjectEditor { public static class ProjectExtensions { diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs index 2ce7837a27..4f21871f1a 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs @@ -1,17 +1,19 @@ +using GodotTools.Core; using System; +using System.Collections.Generic; using System.IO; using Microsoft.Build.Construction; -namespace GodotSharpTools.Project +namespace GodotTools.ProjectEditor { public static class ProjectGenerator { - public const string CoreApiProjectName = "GodotSharp"; - public const string EditorApiProjectName = "GodotSharpEditor"; - const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; - const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; + private const string CoreApiProjectName = "GodotSharp"; + private const string EditorApiProjectName = "GodotSharpEditor"; + private const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; + private const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; - public static string GenCoreApiProject(string dir, string[] compileItems) + public static string GenCoreApiProject(string dir, IEnumerable<string> compileItems) { string path = Path.Combine(dir, CoreApiProjectName + ".csproj"); @@ -21,10 +23,11 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); + mainGroup.SetProperty("BaseIntermediateOutputPath", "obj"); GenAssemblyInfoFile(root, dir, CoreApiProjectName, - new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" }, - new string[] { "System.Runtime.CompilerServices" }); + new[] {"[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]"}, + new[] {"System.Runtime.CompilerServices"}); foreach (var item in compileItems) { @@ -36,7 +39,7 @@ namespace GodotSharpTools.Project return CoreApiProjectGuid; } - public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems) + public static string GenEditorApiProject(string dir, string coreApiProjPath, IEnumerable<string> compileItems) { string path = Path.Combine(dir, EditorApiProjectName + ".csproj"); @@ -46,6 +49,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); + mainGroup.SetProperty("BaseIntermediateOutputPath", "obj"); GenAssemblyInfoFile(root, dir, EditorApiProjectName); @@ -62,7 +66,7 @@ namespace GodotSharpTools.Project return EditorApiProjectGuid; } - public static string GenGameProject(string dir, string name, string[] compileItems) + public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems) { string path = Path.Combine(dir, name + ".csproj"); @@ -72,24 +76,26 @@ namespace GodotSharpTools.Project mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)")); mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj")); mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)")); + mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'Release' "; + mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'Release' "; var toolsGroup = root.AddPropertyGroup(); toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' "; toolsGroup.AddProperty("DebugSymbols", "true"); toolsGroup.AddProperty("DebugType", "portable"); toolsGroup.AddProperty("Optimize", "false"); - toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;"); + toolsGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;"); toolsGroup.AddProperty("ErrorReport", "prompt"); toolsGroup.AddProperty("WarningLevel", "4"); toolsGroup.AddProperty("ConsolePause", "false"); var coreApiRef = root.AddItem("Reference", CoreApiProjectName); - coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll")); + coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll")); coreApiRef.AddMetadata("Private", "False"); var editorApiRef = root.AddItem("Reference", EditorApiProjectName); editorApiRef.Condition = " '$(Configuration)' == 'Tools' "; - editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll")); + editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll")); editorApiRef.AddMetadata("Private", "False"); GenAssemblyInfoFile(root, dir, name); @@ -106,7 +112,6 @@ namespace GodotSharpTools.Project public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null) { - string propertiesDir = Path.Combine(dir, "Properties"); if (!Directory.Exists(propertiesDir)) Directory.CreateDirectory(propertiesDir); @@ -122,12 +127,9 @@ namespace GodotSharpTools.Project string assemblyLinesText = string.Empty; if (assemblyLines != null) - { - foreach (var assemblyLine in assemblyLines) - assemblyLinesText += string.Join("\n", assemblyLines) + "\n"; - } + assemblyLinesText += string.Join("\n", assemblyLines) + "\n"; - string content = string.Format(assemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText); + string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText); string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs"); @@ -138,6 +140,9 @@ namespace GodotSharpTools.Project public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup) { + if (string.IsNullOrEmpty(name)) + throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name)); + var root = ProjectRootElement.Create(); root.DefaultTargets = "Build"; @@ -147,7 +152,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}"); mainGroup.AddProperty("OutputType", "Library"); mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)")); - mainGroup.AddProperty("RootNamespace", name); + mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true)); mainGroup.AddProperty("AssemblyName", name); mainGroup.AddProperty("TargetFrameworkVersion", "v4.5"); @@ -156,7 +161,7 @@ namespace GodotSharpTools.Project debugGroup.AddProperty("DebugSymbols", "true"); debugGroup.AddProperty("DebugType", "portable"); debugGroup.AddProperty("Optimize", "false"); - debugGroup.AddProperty("DefineConstants", "DEBUG;"); + debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;"); debugGroup.AddProperty("ErrorReport", "prompt"); debugGroup.AddProperty("WarningLevel", "4"); debugGroup.AddProperty("ConsolePause", "false"); @@ -165,6 +170,7 @@ namespace GodotSharpTools.Project releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "; releaseGroup.AddProperty("DebugType", "portable"); releaseGroup.AddProperty("Optimize", "true"); + releaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;"); releaseGroup.AddProperty("ErrorReport", "prompt"); releaseGroup.AddProperty("WarningLevel", "4"); releaseGroup.AddProperty("ConsolePause", "false"); @@ -188,8 +194,8 @@ namespace GodotSharpTools.Project } } - private const string assemblyInfoTemplate = -@"using System.Reflection;{0} + private const string AssemblyInfoTemplate = + @"using System.Reflection;{0} // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs new file mode 100644 index 0000000000..1edc426e00 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -0,0 +1,175 @@ +using GodotTools.Core; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using DotNet.Globbing; +using Microsoft.Build.Construction; + +namespace GodotTools.ProjectEditor +{ + public static class ProjectUtils + { + public static void AddItemToProjectChecked(string projectPath, string itemType, string include) + { + var dir = Directory.GetParent(projectPath).FullName; + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\"); + + if (root.AddItemChecked(itemType, normalizedInclude)) + root.Save(); + } + + private static string[] GetAllFilesRecursive(string rootDirectory, string mask) + { + string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); + + // We want relative paths + for (int i = 0; i < files.Length; i++) + { + files[i] = files[i].RelativeToPath(rootDirectory); + } + + return files; + } + + public static string[] GetIncludeFiles(string projectPath, string itemType) + { + var result = new List<string>(); + var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs"); + + var globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + var root = ProjectRootElement.Open(projectPath); + + foreach (var itemGroup in root.ItemGroups) + { + if (itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + string normalizedInclude = item.Include.NormalizePath(); + + var glob = Glob.Parse(normalizedInclude, globOptions); + + // TODO Check somehow if path has no blob to avoid the following loop... + + foreach (var existingFile in existingFiles) + { + if (glob.IsMatch(existingFile)) + { + result.Add(existingFile); + } + } + } + } + + return result.ToArray(); + } + + /// Simple function to make sure the Api assembly references are configured correctly + public static void FixApiHintPath(string projectPath) + { + var root = ProjectRootElement.Open(projectPath); + Debug.Assert(root != null); + + bool dirty = false; + + void AddPropertyIfNotPresent(string name, string condition, string value) + { + if (root.PropertyGroups + .Any(g => g.Condition == string.Empty || g.Condition == condition && + g.Properties + .Any(p => p.Name == name && + p.Value == value && + (p.Condition == condition || g.Condition == condition)))) + { + return; + } + + root.AddProperty(name, value).Condition = condition; + dirty = true; + } + + AddPropertyIfNotPresent(name: "ApiConfiguration", + condition: " '$(Configuration)' != 'Release' ", + value: "Debug"); + AddPropertyIfNotPresent(name: "ApiConfiguration", + condition: " '$(Configuration)' == 'Release' ", + value: "Release"); + + void SetReferenceHintPath(string referenceName, string condition, string hintPath) + { + foreach (var itemGroup in root.ItemGroups.Where(g => + g.Condition == string.Empty || g.Condition == condition)) + { + var references = itemGroup.Items.Where(item => + item.ItemType == "Reference" && + item.Include == referenceName && + (item.Condition == condition || itemGroup.Condition == condition)); + + var referencesWithHintPath = references.Where(reference => + reference.Metadata.Any(m => m.Name == "HintPath")); + + if (referencesWithHintPath.Any(reference => reference.Metadata + .Any(m => m.Name == "HintPath" && m.Value == hintPath))) + { + // Found a Reference item with the right HintPath + return; + } + + var referenceWithHintPath = referencesWithHintPath.FirstOrDefault(); + if (referenceWithHintPath != null) + { + // Found a Reference item with a wrong HintPath + foreach (var metadata in referenceWithHintPath.Metadata.ToList() + .Where(m => m.Name == "HintPath")) + { + // Safe to remove as we duplicate with ToList() to loop + referenceWithHintPath.RemoveChild(metadata); + } + + referenceWithHintPath.AddMetadata("HintPath", hintPath); + dirty = true; + return; + } + + var referenceWithoutHintPath = references.FirstOrDefault(); + if (referenceWithoutHintPath != null) + { + // Found a Reference item without a HintPath + referenceWithoutHintPath.AddMetadata("HintPath", hintPath); + dirty = true; + return; + } + } + + // Found no Reference item at all. Add it. + root.AddItem("Reference", referenceName).Condition = condition; + dirty = true; + } + + const string coreProjectName = "GodotSharp"; + const string editorProjectName = "GodotSharpEditor"; + + const string coreCondition = ""; + const string editorCondition = " '$(Configuration)' == 'Tools' "; + + var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll"; + var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll"; + + SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath); + SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath); + + if (dirty) + root.Save(); + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs index 7115d8fc71..09333850fc 100644 --- a/modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. -[assembly: AssemblyTitle("GodotSharpTools")] +[assembly: AssemblyTitle("GodotTools.ProjectEditor")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("ignacio")] +[assembly: AssemblyCopyright("Godot Engine contributors")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/modules/mono/editor/GodotSharpTools/packages.config b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/packages.config index 2c7cb0bd4b..13915000e4 100644 --- a/modules/mono/editor/GodotSharpTools/packages.config +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <packages> <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln new file mode 100644 index 0000000000..6f7d44bec2 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools", "GodotTools\GodotTools.csproj", "{27B00618-A6F2-4828-B922-05CAEB08C286}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Core", "GodotTools.Core\GodotTools.Core.csproj", "{639E48BD-44E5-4091-8EDD-22D36DC0768D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "GodotTools.BuildLogger\GodotTools.BuildLogger.csproj", "{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU + {27B00618-A6F2-4828-B922-05CAEB08C286}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27B00618-A6F2-4828-B922-05CAEB08C286}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27B00618-A6F2-4828-B922-05CAEB08C286}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27B00618-A6F2-4828-B922-05CAEB08C286}.Release|Any CPU.Build.0 = Release|Any CPU + {639E48BD-44E5-4091-8EDD-22D36DC0768D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {639E48BD-44E5-4091-8EDD-22D36DC0768D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {639E48BD-44E5-4091-8EDD-22D36DC0768D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {639E48BD-44E5-4091-8EDD-22D36DC0768D}.Release|Any CPU.Build.0 = Release|Any CPU + {6CE9A984-37B1-4F8A-8FE9-609F05F071B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CE9A984-37B1-4F8A-8FE9-609F05F071B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6CE9A984-37B1-4F8A-8FE9-609F05F071B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6CE9A984-37B1-4F8A-8FE9-609F05F071B3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs new file mode 100644 index 0000000000..f849356919 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -0,0 +1,172 @@ +using GodotTools.Core; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using GodotTools.BuildLogger; +using GodotTools.Internals; +using GodotTools.Utils; +using Directory = System.IO.Directory; + +namespace GodotTools.Build +{ + public static class BuildSystem + { + private static string GetMsBuildPath() + { + string msbuildPath = MsBuildFinder.FindMsBuild(); + + if (msbuildPath == null) + throw new FileNotFoundException("Cannot find the MSBuild executable."); + + return msbuildPath; + } + + private static string MonoWindowsBinDir + { + get + { + string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin"); + + if (!Directory.Exists(monoWinBinDir)) + throw new FileNotFoundException("Cannot find the Windows Mono install bin directory."); + + return monoWinBinDir; + } + } + + private static Godot.EditorSettings EditorSettings => + GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + + private static bool UsingMonoMsBuildOnWindows + { + get + { + if (OS.IsWindows()) + { + return (GodotSharpBuilds.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool") + == GodotSharpBuilds.BuildTool.MsBuildMono; + } + + return false; + } + } + + private static bool PrintBuildOutput => + (bool) EditorSettings.GetSetting("mono/builds/print_build_output"); + + private static Process LaunchBuild(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null) + { + var customPropertiesList = new List<string>(); + + if (customProperties != null) + customPropertiesList.AddRange(customProperties); + + string compilerArgs = BuildArguments(solution, config, loggerOutputDir, customPropertiesList); + + var startInfo = new ProcessStartInfo(GetMsBuildPath(), compilerArgs); + + bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput; + + if (!redirectOutput || Godot.OS.IsStdoutVerbose()) + Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}"); + + startInfo.RedirectStandardOutput = redirectOutput; + startInfo.RedirectStandardError = redirectOutput; + startInfo.UseShellExecute = false; + + if (UsingMonoMsBuildOnWindows) + { + // These environment variables are required for Mono's MSBuild to find the compilers. + // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono. + string monoWinBinDir = MonoWindowsBinDir; + startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat")); + startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat")); + startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat")); + } + + // Needed when running from Developer Command Prompt for VS + RemovePlatformVariable(startInfo.EnvironmentVariables); + + var process = new Process {StartInfo = startInfo}; + + process.Start(); + + if (redirectOutput) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } + + return process; + } + + public static int Build(MonoBuildInfo monoBuildInfo) + { + return Build(monoBuildInfo.Solution, monoBuildInfo.Configuration, + monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties); + } + + public static async Task<int> BuildAsync(MonoBuildInfo monoBuildInfo) + { + return await BuildAsync(monoBuildInfo.Solution, monoBuildInfo.Configuration, + monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties); + } + + public static int Build(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null) + { + using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties)) + { + process.WaitForExit(); + + return process.ExitCode; + } + } + + public static async Task<int> BuildAsync(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null) + { + using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties)) + { + await process.WaitForExitAsync(); + + return process.ExitCode; + } + } + + private static string BuildArguments(string solution, string config, string loggerOutputDir, List<string> customProperties) + { + string arguments = $@"""{solution}"" /v:normal /t:Rebuild ""/p:{"Configuration=" + config}"" " + + $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}"""; + + foreach (string customProperty in customProperties) + { + arguments += " /p:" + customProperty; + } + + return arguments; + } + + private static void RemovePlatformVariable(StringDictionary environmentVariables) + { + // EnvironmentVariables is case sensitive? Seriously? + + var platformEnvironmentVariables = new List<string>(); + + foreach (string env in environmentVariables.Keys) + { + if (env.ToUpper() == "PLATFORM") + platformEnvironmentVariables.Add(env); + } + + foreach (string env in platformEnvironmentVariables) + environmentVariables.Remove(env); + } + + private static bool IsDebugMsBuildRequested() + { + return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1"; + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs new file mode 100644 index 0000000000..f0068385f4 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Godot; +using GodotTools.Internals; +using Directory = System.IO.Directory; +using Environment = System.Environment; +using File = System.IO.File; +using Path = System.IO.Path; +using OS = GodotTools.Utils.OS; + +namespace GodotTools.Build +{ + public static class MsBuildFinder + { + private static string _msbuildToolsPath = string.Empty; + private static string _msbuildUnixPath = string.Empty; + private static string _xbuildUnixPath = string.Empty; + + public static string FindMsBuild() + { + var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var buildTool = (GodotSharpBuilds.BuildTool) editorSettings.GetSetting("mono/builds/build_tool"); + + if (OS.IsWindows()) + { + switch (buildTool) + { + case GodotSharpBuilds.BuildTool.MsBuildVs: + { + if (_msbuildToolsPath.Empty() || !File.Exists(_msbuildToolsPath)) + { + // Try to search it again if it wasn't found last time or if it was removed from its location + _msbuildToolsPath = FindMsBuildToolsPathOnWindows(); + + if (_msbuildToolsPath.Empty()) + { + throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildVs}'. Tried with path: {_msbuildToolsPath}"); + } + } + + if (!_msbuildToolsPath.EndsWith("\\")) + _msbuildToolsPath += "\\"; + + return Path.Combine(_msbuildToolsPath, "MSBuild.exe"); + } + + case GodotSharpBuilds.BuildTool.MsBuildMono: + { + string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat"); + + if (!File.Exists(msbuildPath)) + { + throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildMono}'. Tried with path: {msbuildPath}"); + } + + return msbuildPath; + } + + case GodotSharpBuilds.BuildTool.XBuild: + { + string xbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "xbuild.bat"); + + if (!File.Exists(xbuildPath)) + { + throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameXbuild}'. Tried with path: {xbuildPath}"); + } + + return xbuildPath; + } + + default: + throw new IndexOutOfRangeException("Invalid build tool in editor settings"); + } + } + + if (OS.IsUnix()) + { + if (buildTool == GodotSharpBuilds.BuildTool.XBuild) + { + if (_xbuildUnixPath.Empty() || !File.Exists(_xbuildUnixPath)) + { + // Try to search it again if it wasn't found last time or if it was removed from its location + _xbuildUnixPath = FindBuildEngineOnUnix("msbuild"); + } + + if (_xbuildUnixPath.Empty()) + { + throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameXbuild}'"); + } + } + else + { + if (_msbuildUnixPath.Empty() || !File.Exists(_msbuildUnixPath)) + { + // Try to search it again if it wasn't found last time or if it was removed from its location + _msbuildUnixPath = FindBuildEngineOnUnix("msbuild"); + } + + if (_msbuildUnixPath.Empty()) + { + throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameMsbuildMono}'"); + } + } + + return buildTool != GodotSharpBuilds.BuildTool.XBuild ? _msbuildUnixPath : _xbuildUnixPath; + } + + throw new PlatformNotSupportedException(); + } + + private static IEnumerable<string> MsBuildHintDirs + { + get + { + var result = new List<string>(); + + if (OS.IsOSX()) + { + result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/"); + result.Add("/usr/local/var/homebrew/linked/mono/bin/"); + } + + result.Add("/opt/novell/mono/bin/"); + + return result; + } + } + + private static string FindBuildEngineOnUnix(string name) + { + string ret = OS.PathWhich(name); + + if (!ret.Empty()) + return ret; + + string retFallback = OS.PathWhich($"{name}.exe"); + + if (!retFallback.Empty()) + return retFallback; + + foreach (string hintDir in MsBuildHintDirs) + { + string hintPath = Path.Combine(hintDir, name); + + if (File.Exists(hintPath)) + return hintPath; + } + + return string.Empty; + } + + private static string FindMsBuildToolsPathOnWindows() + { + if (!OS.IsWindows()) + throw new PlatformNotSupportedException(); + + // Try to find 15.0 with vswhere + + string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)"); + vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + + var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"}; + + var outputArray = new Godot.Collections.Array<string>(); + int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs, + blocking: true, output: (Godot.Collections.Array) outputArray); + + if (exitCode == 0) + return string.Empty; + + if (outputArray.Count == 0) + return string.Empty; + + var lines = outputArray[0].Split('\n'); + + foreach (string line in lines) + { + int sepIdx = line.IndexOf(':'); + + if (sepIdx <= 0) + continue; + + string key = line.Substring(0, sepIdx); // No need to trim + + if (key != "installationPath") + continue; + + string value = line.Substring(sepIdx + 1).StripEdges(); + + if (value.Empty()) + throw new FormatException("installationPath value is empty"); + + if (!value.EndsWith("\\")) + value += "\\"; + + // Since VS2019, the directory is simply named "Current" + string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin"); + + if (Directory.Exists(msbuildDir)) + return msbuildDir; + + // Directory name "15.0" is used in VS 2017 + return Path.Combine(value, "MSBuild\\15.0\\Bin"); + } + + return string.Empty; + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs b/modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs new file mode 100644 index 0000000000..4535ed7247 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs @@ -0,0 +1,127 @@ +using Godot; +using System; +using Godot.Collections; +using GodotTools.Internals; +using GodotTools.ProjectEditor; +using static GodotTools.Internals.Globals; +using File = GodotTools.Utils.File; +using Directory = GodotTools.Utils.Directory; + +namespace GodotTools +{ + public static class CSharpProject + { + public static string GenerateGameProject(string dir, string name) + { + try + { + return ProjectGenerator.GenGameProject(dir, name, compileItems: new string[] { }); + } + catch (Exception e) + { + GD.PushError(e.ToString()); + return string.Empty; + } + } + + public static void AddItem(string projectPath, string itemType, string include) + { + if (!(bool) GlobalDef("mono/project/auto_update_project", true)) + return; + + ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include); + } + + public static void FixApiHintPath(string projectPath) + { + try + { + ProjectUtils.FixApiHintPath(projectPath); + } + catch (Exception e) + { + GD.PushError(e.ToString()); + } + } + + private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private static ulong ConvertToTimestamp(this DateTime value) + { + TimeSpan elapsedTime = value - Epoch; + return (ulong) elapsedTime.TotalSeconds; + } + + public static void GenerateScriptsMetadata(string projectPath, string outputPath) + { + if (File.Exists(outputPath)) + File.Delete(outputPath); + + var oldDict = Internal.GetScriptsMetadataOrNothing(); + var newDict = new Godot.Collections.Dictionary<string, object>(); + + foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile")) + { + string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath(); + + ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp(); + + if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar)) + { + var oldFileDict = (Dictionary) oldFileVar; + + if (ulong.TryParse(oldFileDict["modified_time"] as string, out ulong storedModifiedTime)) + { + if (storedModifiedTime == modifiedTime) + { + // No changes so no need to parse again + newDict[projectIncludeFile] = oldFileDict; + continue; + } + } + } + + ScriptClassParser.ParseFileOrThrow(projectIncludeFile, out var classes); + + string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile); + + var classDict = new Dictionary(); + + foreach (var classDecl in classes) + { + if (classDecl.BaseCount == 0) + continue; // Does not inherit nor implement anything, so it can't be a script class + + string classCmp = classDecl.Nested ? + classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) : + classDecl.Name; + + if (classCmp != searchName) + continue; + + classDict["namespace"] = classDecl.Namespace; + classDict["class_name"] = classDecl.Name; + classDict["nested"] = classDecl.Nested; + break; + } + + if (classDict.Count == 0) + continue; // Not found + + newDict[projectIncludeFile] = new Dictionary {["modified_time"] = $"{modifiedTime}", ["class"] = classDict}; + } + + if (newDict.Count > 0) + { + string json = JSON.Print(newDict); + + string baseDir = outputPath.GetBaseDir(); + + if (!Directory.Exists(baseDir)) + Directory.CreateDirectory(baseDir); + + File.WriteAllText(outputPath, json); + } + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs new file mode 100644 index 0000000000..a884b0ead0 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using GodotTools.Build; +using GodotTools.Internals; +using GodotTools.Utils; +using static GodotTools.Internals.Globals; +using Error = Godot.Error; +using File = GodotTools.Utils.File; +using Directory = GodotTools.Utils.Directory; + +namespace GodotTools +{ + public static class GodotSharpBuilds + { + private static readonly List<MonoBuildInfo> BuildsInProgress = new List<MonoBuildInfo>(); + + public const string PropNameMsbuildMono = "MSBuild (Mono)"; + public const string PropNameMsbuildVs = "MSBuild (VS Build Tools)"; + public const string PropNameXbuild = "xbuild (Deprecated)"; + + public const string MsBuildIssuesFileName = "msbuild_issues.csv"; + public const string MsBuildLogFileName = "msbuild_log.txt"; + + public enum BuildTool + { + MsBuildMono, + MsBuildVs, + XBuild // Deprecated + } + + private static void RemoveOldIssuesFile(MonoBuildInfo buildInfo) + { + var issuesFile = GetIssuesFilePath(buildInfo); + + if (!File.Exists(issuesFile)) + return; + + File.Delete(issuesFile); + } + + private static string _ApiFolderName(ApiAssemblyType apiType) + { + ulong apiHash = apiType == ApiAssemblyType.Core ? + Internal.GetCoreApiHash() : + Internal.GetEditorApiHash(); + return $"{apiHash}_{BindingsGenerator.Version}_{BindingsGenerator.CsGlueVersion}"; + } + + private static void ShowBuildErrorDialog(string message) + { + GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error"); + GodotSharpEditor.Instance.MonoBottomPanel.ShowBuildTab(); + } + + public static void RestartBuild(MonoBuildTab buildTab) => throw new NotImplementedException(); + public static void StopBuild(MonoBuildTab buildTab) => throw new NotImplementedException(); + + private static string GetLogFilePath(MonoBuildInfo buildInfo) + { + return Path.Combine(buildInfo.LogsDirPath, MsBuildLogFileName); + } + + private static string GetIssuesFilePath(MonoBuildInfo buildInfo) + { + return Path.Combine(Godot.ProjectSettings.LocalizePath(buildInfo.LogsDirPath), MsBuildIssuesFileName); + } + + private static void PrintVerbose(string text) + { + if (Godot.OS.IsStdoutVerbose()) + Godot.GD.Print(text); + } + + public static bool Build(MonoBuildInfo buildInfo) + { + if (BuildsInProgress.Contains(buildInfo)) + throw new InvalidOperationException("A build is already in progress"); + + BuildsInProgress.Add(buildInfo); + + try + { + MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo); + buildTab.OnBuildStart(); + + // Required in order to update the build tasks list + Internal.GodotMainIteration(); + + try + { + RemoveOldIssuesFile(buildInfo); + } + catch (IOException e) + { + buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); + Console.Error.WriteLine(e); + } + + try + { + int exitCode = BuildSystem.Build(buildInfo); + + if (exitCode != 0) + PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}"); + + buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error); + + return exitCode == 0; + } + catch (Exception e) + { + buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); + Console.Error.WriteLine(e); + return false; + } + } + finally + { + BuildsInProgress.Remove(buildInfo); + } + } + + public static async Task<bool> BuildAsync(MonoBuildInfo buildInfo) + { + if (BuildsInProgress.Contains(buildInfo)) + throw new InvalidOperationException("A build is already in progress"); + + BuildsInProgress.Add(buildInfo); + + try + { + MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo); + + try + { + RemoveOldIssuesFile(buildInfo); + } + catch (IOException e) + { + buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); + Console.Error.WriteLine(e); + } + + try + { + int exitCode = await BuildSystem.BuildAsync(buildInfo); + + if (exitCode != 0) + PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}"); + + buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error); + + return exitCode == 0; + } + catch (Exception e) + { + buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); + Console.Error.WriteLine(e); + return false; + } + } + finally + { + BuildsInProgress.Remove(buildInfo); + } + } + + public static bool BuildApiSolution(string apiSlnDir, string config) + { + string apiSlnFile = Path.Combine(apiSlnDir, $"{ApiAssemblyNames.SolutionName}.sln"); + + string coreApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Core, "bin", config); + string coreApiAssemblyFile = Path.Combine(coreApiAssemblyDir, $"{ApiAssemblyNames.Core}.dll"); + + string editorApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Editor, "bin", config); + string editorApiAssemblyFile = Path.Combine(editorApiAssemblyDir, $"{ApiAssemblyNames.Editor}.dll"); + + if (File.Exists(coreApiAssemblyFile) && File.Exists(editorApiAssemblyFile)) + return true; // The assemblies are in the output folder; assume the solution is already built + + var apiBuildInfo = new MonoBuildInfo(apiSlnFile, config); + + // TODO Replace this global NoWarn with '#pragma warning' directives on generated files, + // once we start to actively document manually maintained C# classes + apiBuildInfo.CustomProperties.Add("NoWarn=1591"); // Ignore missing documentation warnings + + if (Build(apiBuildInfo)) + return true; + + ShowBuildErrorDialog($"Failed to build {ApiAssemblyNames.SolutionName} solution."); + return false; + } + + public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines) + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return true; // No solution to build + + // Make sure to update the API assemblies if they happen to be missing. Just in + // case the user decided to delete them at some point after they were loaded. + Internal.UpdateApiAssembliesFromPrebuilt(); + + using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1)) + { + pr.Step("Building project solution", 0); + + var buildInfo = new MonoBuildInfo(GodotSharpDirs.ProjectSlnPath, config); + + // Add Godot defines + string constants = OS.IsWindows() ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\""; + + foreach (var godotDefine in godotDefines) + constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};"; + + if (Internal.GodotIsRealTDouble()) + constants += "GODOT_REAL_T_IS_DOUBLE;"; + + constants += OS.IsWindows() ? "\"" : "\\\""; + + buildInfo.CustomProperties.Add(constants); + + if (!Build(buildInfo)) + { + ShowBuildErrorDialog("Failed to build project solution"); + return false; + } + } + + return true; + } + + public static bool EditorBuildCallback() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return true; // No solution to build + + string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor"); + string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player"); + + CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath); + + if (File.Exists(editorScriptsMetadataPath)) + File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); + + var godotDefines = new[] + { + Godot.OS.GetName(), + Internal.GodotIs32Bits() ? "32" : "64" + }; + + return BuildProjectBlocking("Tools", godotDefines); + } + + public static void Initialize() + { + // Build tool settings + + EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono); + + var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + + editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary + { + ["type"] = Godot.Variant.Type.Int, + ["name"] = "mono/builds/build_tool", + ["hint"] = Godot.PropertyHint.Enum, + ["hint_string"] = OS.IsWindows() ? + $"{PropNameMsbuildMono},{PropNameMsbuildVs},{PropNameXbuild}" : + $"{PropNameMsbuildMono},{PropNameXbuild}" + }); + + EditorDef("mono/builds/print_build_output", false); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs new file mode 100644 index 0000000000..90dec43412 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -0,0 +1,491 @@ +using Godot; +using GodotTools.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using GodotTools.Internals; +using GodotTools.ProjectEditor; +using static GodotTools.Internals.Globals; +using File = GodotTools.Utils.File; +using Path = System.IO.Path; +using OS = GodotTools.Utils.OS; + +namespace GodotTools +{ + public class GodotSharpEditor : EditorPlugin, ISerializationListener + { + private EditorSettings editorSettings; + + private PopupMenu menuPopup; + + private AcceptDialog errorDialog; + private AcceptDialog aboutDialog; + private CheckBox aboutDialogCheckBox; + + private ToolButton bottomPanelBtn; + + private MonoDevelopInstance monoDevelopInstance; + private MonoDevelopInstance visualStudioForMacInstance; + + private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization + + public MonoBottomPanel MonoBottomPanel { get; private set; } + + private bool CreateProjectSolution() + { + using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2)) + { + pr.Step("Generating C# project...".TTR()); + + string resourceDir = ProjectSettings.GlobalizePath("res://"); + + string path = resourceDir; + string name = (string) ProjectSettings.GetSetting("application/config/name"); + if (name.Empty()) + name = "UnnamedProject"; + + string guid = CSharpProject.GenerateGameProject(path, name); + + if (guid.Length > 0) + { + var solution = new DotNetSolution(name) + { + DirectoryPath = path + }; + + var projectInfo = new DotNetSolution.ProjectInfo + { + Guid = guid, + PathRelativeToSolution = name + ".csproj", + Configs = new List<string> {"Debug", "Release", "Tools"} + }; + + solution.AddNewProject(name, projectInfo); + + try + { + solution.Save(); + } + catch (IOException e) + { + ShowErrorDialog("Failed to save solution. Exception message: ".TTR() + e.Message); + return false; + } + + // Make sure to update the API assemblies if they happen to be missing. Just in + // case the user decided to delete them at some point after they were loaded. + Internal.UpdateApiAssembliesFromPrebuilt(); + + pr.Step("Done".TTR()); + + // Here, after all calls to progress_task_step + CallDeferred(nameof(_RemoveCreateSlnMenuOption)); + } + else + { + ShowErrorDialog("Failed to create C# project.".TTR()); + } + + return true; + } + } + + private void _RemoveCreateSlnMenuOption() + { + menuPopup.RemoveItem(menuPopup.GetItemIndex((int) MenuOptions.CreateSln)); + bottomPanelBtn.Show(); + } + + private void _ShowAboutDialog() + { + bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start"); + aboutDialogCheckBox.Pressed = showOnStart; + aboutDialog.PopupCenteredMinsize(); + } + + private void _ToggleAboutDialogOnStart(bool enabled) + { + bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start"); + if (showOnStart != enabled) + editorSettings.SetSetting("mono/editor/show_info_on_start", enabled); + } + + private void _MenuOptionPressed(MenuOptions id) + { + switch (id) + { + case MenuOptions.CreateSln: + CreateProjectSolution(); + break; + case MenuOptions.AboutCSharp: + _ShowAboutDialog(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option"); + } + } + + private void _BuildSolutionPressed() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + { + if (!CreateProjectSolution()) + return; // Failed to create solution + } + + Instance.MonoBottomPanel.BuildProjectPressed(); + } + + public override void _Notification(int what) + { + base._Notification(what); + + if (what == NotificationReady) + { + bool showInfoDialog = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start"); + if (showInfoDialog) + { + aboutDialog.PopupExclusive = true; + _ShowAboutDialog(); + // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on. + aboutDialog.PopupExclusive = false; + } + } + } + + public enum MenuOptions + { + CreateSln, + AboutCSharp, + } + + public enum ExternalEditor + { + None, + VisualStudio, // TODO (Windows-only) + VisualStudioForMac, // Mac-only + MonoDevelop, + VsCode + } + + public void ShowErrorDialog(string message, string title = "Error") + { + errorDialog.WindowTitle = title; + errorDialog.DialogText = message; + errorDialog.PopupCenteredMinsize(); + } + + private static string _vsCodePath = string.Empty; + + private static readonly string[] VsCodeNames = + { + "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss" + }; + + public Error OpenInExternalEditor(Script script, int line, int col) + { + var editor = (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor"); + + switch (editor) + { + case ExternalEditor.VsCode: + { + if (_vsCodePath.Empty() || !File.Exists(_vsCodePath)) + { + // Try to search it again if it wasn't found last time or if it was removed from its location + _vsCodePath = VsCodeNames.SelectFirstNotNull(OS.PathWhich, orElse: string.Empty); + } + + var args = new List<string>(); + + bool osxAppBundleInstalled = false; + + if (OS.IsOSX()) + { + // The package path is '/Applications/Visual Studio Code.app' + const string vscodeBundleId = "com.microsoft.VSCode"; + + osxAppBundleInstalled = Internal.IsOsxAppBundleInstalled(vscodeBundleId); + + if (osxAppBundleInstalled) + { + args.Add("-b"); + args.Add(vscodeBundleId); + + // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is + // editing our folder. It's better to ask for a new window and let VSCode do the window management. + args.Add("-n"); + + // The open process must wait until the application finishes (which is instant in VSCode's case) + args.Add("--wait-apps"); + + args.Add("--args"); + } + } + + var resourcePath = ProjectSettings.GlobalizePath("res://"); + args.Add(resourcePath); + + string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); + + if (line >= 0) + { + args.Add("-g"); + args.Add($"{scriptPath}:{line + 1}:{col}"); + } + else + { + args.Add(scriptPath); + } + + string command; + + if (OS.IsOSX()) + { + if (!osxAppBundleInstalled && _vsCodePath.Empty()) + { + GD.PushError("Cannot find code editor: VSCode"); + return Error.FileNotFound; + } + + command = osxAppBundleInstalled ? "/usr/bin/open" : _vsCodePath; + } + else + { + if (_vsCodePath.Empty()) + { + GD.PushError("Cannot find code editor: VSCode"); + return Error.FileNotFound; + } + + command = _vsCodePath; + } + + try + { + OS.RunProcess(command, args); + } + catch (Exception e) + { + GD.PushError($"Error when trying to run code editor: VSCode. Exception message: '{e.Message}'"); + } + + break; + } + + case ExternalEditor.VisualStudioForMac: + goto case ExternalEditor.MonoDevelop; + case ExternalEditor.MonoDevelop: + { + MonoDevelopInstance GetMonoDevelopInstance(string solutionPath) + { + if (OS.IsOSX() && editor == ExternalEditor.VisualStudioForMac) + { + if (visualStudioForMacInstance == null) + visualStudioForMacInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.VisualStudioForMac); + + return visualStudioForMacInstance; + } + + if (monoDevelopInstance == null) + monoDevelopInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.MonoDevelop); + + return monoDevelopInstance; + } + + string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); + + if (line >= 0) + scriptPath += $";{line + 1};{col}"; + + GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath); + + break; + } + + case ExternalEditor.None: + return Error.Unavailable; + default: + throw new ArgumentOutOfRangeException(); + } + + return Error.Ok; + } + + public bool OverridesExternalEditor() + { + return (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditor.None; + } + + public override bool Build() + { + return GodotSharpBuilds.EditorBuildCallback(); + } + + public override void EnablePlugin() + { + base.EnablePlugin(); + + if (Instance != null) + throw new InvalidOperationException(); + Instance = this; + + var editorInterface = GetEditorInterface(); + var editorBaseControl = editorInterface.GetBaseControl(); + + editorSettings = editorInterface.GetEditorSettings(); + + errorDialog = new AcceptDialog(); + editorBaseControl.AddChild(errorDialog); + + MonoBottomPanel = new MonoBottomPanel(); + + bottomPanelBtn = AddControlToBottomPanel(MonoBottomPanel, "Mono".TTR()); + + AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"}); + + menuPopup = new PopupMenu(); + menuPopup.Hide(); + menuPopup.SetAsToplevel(true); + + AddToolSubmenuItem("Mono", menuPopup); + + // TODO: Remove or edit this info dialog once Mono support is no longer in alpha + { + menuPopup.AddItem("About C# support".TTR(), (int) MenuOptions.AboutCSharp); + aboutDialog = new AcceptDialog(); + editorBaseControl.AddChild(aboutDialog); + aboutDialog.WindowTitle = "Important: C# support is not feature-complete"; + + // We don't use DialogText as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox + // we'll add. Instead we add containers and a new autowrapped Label inside. + + // Main VBoxContainer (icon + label on top, checkbox at bottom) + var aboutVBox = new VBoxContainer(); + aboutDialog.AddChild(aboutVBox); + + // HBoxContainer for icon + label + var aboutHBox = new HBoxContainer(); + aboutVBox.AddChild(aboutHBox); + + var aboutIcon = new TextureRect(); + aboutIcon.Texture = aboutIcon.GetIcon("NodeWarning", "EditorIcons"); + aboutHBox.AddChild(aboutIcon); + + var aboutLabel = new Label(); + aboutHBox.AddChild(aboutLabel); + aboutLabel.RectMinSize = new Vector2(600, 150) * EditorScale; + aboutLabel.SizeFlagsVertical = (int) Control.SizeFlags.ExpandFill; + aboutLabel.Autowrap = true; + aboutLabel.Text = + "C# support in Godot Engine is in late alpha stage and, while already usable, " + + "it is not meant for use in production.\n\n" + + "Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " + + "Bugs and usability issues will be addressed gradually over future releases, " + + "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" + + " https://github.com/godotengine/godot/issues\n\n" + + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; + + EditorDef("mono/editor/show_info_on_start", true); + + // CheckBox in main container + aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"}; + aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart)); + aboutVBox.AddChild(aboutDialogCheckBox); + } + + if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath)) + { + // Make sure the existing project has Api assembly references configured correctly + CSharpProject.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath); + } + else + { + bottomPanelBtn.Hide(); + menuPopup.AddItem("Create C# solution".TTR(), (int) MenuOptions.CreateSln); + } + + menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed)); + + var buildButton = new ToolButton + { + Text = "Build", + HintTooltip = "Build solution", + FocusMode = Control.FocusModeEnum.None + }; + buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed)); + AddControlToContainer(CustomControlContainer.Toolbar, buildButton); + + // External editor settings + EditorDef("mono/editor/external_editor", ExternalEditor.None); + + string settingsHintStr = "Disabled"; + + if (OS.IsWindows()) + { + settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" + + $",Visual Studio Code:{(int) ExternalEditor.VsCode}"; + } + else if (OS.IsOSX()) + { + settingsHintStr += $",Visual Studio:{(int) ExternalEditor.VisualStudioForMac}" + + $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" + + $",Visual Studio Code:{(int) ExternalEditor.VsCode}"; + } + else if (OS.IsUnix()) + { + settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" + + $",Visual Studio Code:{(int) ExternalEditor.VsCode}"; + } + + editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary + { + ["type"] = Variant.Type.Int, + ["name"] = "mono/editor/external_editor", + ["hint"] = PropertyHint.Enum, + ["hint_string"] = settingsHintStr + }); + + // Export plugin + var exportPlugin = new GodotSharpExport(); + AddExportPlugin(exportPlugin); + exportPluginWeak = WeakRef(exportPlugin); + + GodotSharpBuilds.Initialize(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (exportPluginWeak != null) + { + // We need to dispose our export plugin before the editor destroys EditorSettings. + // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid + // will be freed after EditorSettings already was, and its device polling thread + // will try to access the EditorSettings singleton, resulting in null dereferencing. + (exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose(); + + exportPluginWeak.Dispose(); + } + } + + public void OnBeforeSerialize() + { + } + + public void OnAfterDeserialize() + { + Instance = this; + } + + // Singleton + + public static GodotSharpEditor Instance { get; private set; } + + private GodotSharpEditor() + { + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs new file mode 100644 index 0000000000..b80fe1fab7 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs @@ -0,0 +1,197 @@ +using Godot; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using GodotTools.Core; +using GodotTools.Internals; +using Directory = GodotTools.Utils.Directory; +using File = GodotTools.Utils.File; +using Path = System.IO.Path; + +namespace GodotTools +{ + public class GodotSharpExport : EditorExportPlugin + { + private void AddFile(string srcPath, string dstPath, bool remap = false) + { + AddFile(dstPath, File.ReadAllBytes(srcPath), remap); + } + + public override void _ExportFile(string path, string type, string[] features) + { + base._ExportFile(path, type, features); + + if (type != Internal.CSharpLanguageType) + return; + + if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}") + throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path)); + + // TODO What if the source file is not part of the game's C# project + + bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content"); + + if (!includeScriptsContent) + { + // We don't want to include the source code on exported games + AddFile(path, new byte[] { }, remap: false); + Skip(); + } + } + + public override void _ExportBegin(string[] features, bool isDebug, string path, int flags) + { + base._ExportBegin(features, isDebug, path, flags); + + try + { + _ExportBeginImpl(features, isDebug, path, flags); + } + catch (Exception e) + { + GD.PushError($"Failed to export project. Exception message: {e.Message}"); + Console.Error.WriteLine(e); + } + } + + public void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags) + { + // TODO Right now there is no way to stop the export process with an error + + if (File.Exists(GodotSharpDirs.ProjectSlnPath)) + { + string buildConfig = isDebug ? "Debug" : "Release"; + + string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}"); + CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath); + + AddFile(scriptsMetadataPath, scriptsMetadataPath); + + // Turn export features into defines + var godotDefines = features; + + if (!GodotSharpBuilds.BuildProjectBlocking(buildConfig, godotDefines)) + { + GD.PushError("Failed to build project"); + return; + } + + // Add dependency assemblies + + var dependencies = new Godot.Collections.Dictionary<string, string>(); + + var projectDllName = (string) ProjectSettings.GetSetting("application/config/name"); + if (projectDllName.Empty()) + { + projectDllName = "UnnamedProject"; + } + + string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig); + string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll"); + + dependencies[projectDllName] = projectDllSrcPath; + + { + string templatesDir = Internal.FullTemplatesDir; + string androidBclDir = Path.Combine(templatesDir, "android-bcl"); + + string customLibDir = features.Contains("Android") && Directory.Exists(androidBclDir) ? androidBclDir : string.Empty; + + GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies); + } + + string apiConfig = isDebug ? "Debug" : "Release"; + string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig); + + foreach (var dependency in dependencies) + { + string dependSrcPath = dependency.Value; + string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile()); + AddFile(dependSrcPath, dependDstPath); + } + } + + // Mono specific export template extras (data dir) + ExportDataDirectory(features, isDebug, path); + } + + private static void ExportDataDirectory(IEnumerable<string> features, bool debug, string path) + { + var featureSet = new HashSet<string>(features); + + if (!PlatformHasTemplateDir(featureSet)) + return; + + string templateDirName = "data.mono"; + + if (featureSet.Contains("Windows")) + { + templateDirName += ".windows"; + templateDirName += featureSet.Contains("64") ? ".64" : ".32"; + } + else if (featureSet.Contains("X11")) + { + templateDirName += ".x11"; + templateDirName += featureSet.Contains("64") ? ".64" : ".32"; + } + else + { + throw new NotSupportedException("Target platform not supported"); + } + + templateDirName += debug ? ".release_debug" : ".release"; + + string templateDirPath = Path.Combine(Internal.FullTemplatesDir, templateDirName); + + if (!Directory.Exists(templateDirPath)) + throw new FileNotFoundException("Data template directory not found"); + + string outputDir = new FileInfo(path).Directory?.FullName ?? + throw new FileNotFoundException("Base directory not found"); + + string outputDataDir = Path.Combine(outputDir, DataDirName); + + if (Directory.Exists(outputDataDir)) + Directory.Delete(outputDataDir, recursive: true); // Clean first + + Directory.CreateDirectory(outputDataDir); + + foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1))); + } + + foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories)) + { + File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1))); + } + } + + private static bool PlatformHasTemplateDir(IEnumerable<string> featureSet) + { + // OSX export templates are contained in a zip, so we place + // our custom template inside it and let Godot do the rest. + return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f)); + } + + private static string DataDirName + { + get + { + var appName = (string) ProjectSettings.GetSetting("application/config/name"); + string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false); + return $"data_{appNameSafe}"; + } + } + + private static void GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath, + string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies) => + internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath, + string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj new file mode 100644 index 0000000000..01e8c87d14 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>GodotTools</RootNamespace> + <AssemblyName>GodotTools</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath> + <GodotApiConfiguration>Debug</GodotApiConfiguration> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>portable</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="GodotSharp"> + <HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharp.dll</HintPath> + </Reference> + <Reference Include="GodotSharpEditor"> + <HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharpEditor.dll</HintPath> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="Build\MsBuildFinder.cs" /> + <Compile Include="Internals\BindingsGenerator.cs" /> + <Compile Include="Internals\EditorProgress.cs" /> + <Compile Include="Internals\GodotSharpDirs.cs" /> + <Compile Include="Internals\Internal.cs" /> + <Compile Include="Internals\ScriptClassParser.cs" /> + <Compile Include="Internals\Globals.cs" /> + <Compile Include="MonoDevelopInstance.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Build\BuildSystem.cs" /> + <Compile Include="Utils\Directory.cs" /> + <Compile Include="Utils\File.cs" /> + <Compile Include="Utils\OS.cs" /> + <Compile Include="GodotSharpEditor.cs" /> + <Compile Include="GodotSharpBuilds.cs" /> + <Compile Include="HotReloadAssemblyWatcher.cs" /> + <Compile Include="MonoBuildInfo.cs" /> + <Compile Include="MonoBuildTab.cs" /> + <Compile Include="MonoBottomPanel.cs" /> + <Compile Include="GodotSharpExport.cs" /> + <Compile Include="CSharpProject.cs" /> + <Compile Include="Utils\CollectionExtensions.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj"> + <Project>{6ce9a984-37b1-4f8a-8fe9-609f05f071b3}</Project> + <Name>GodotTools.BuildLogger</Name> + </ProjectReference> + <ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj"> + <Project>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</Project> + <Name>GodotTools.ProjectEditor</Name> + </ProjectReference> + <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj"> + <Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project> + <Name>GodotTools.Core</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Folder Include="Editor" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs new file mode 100644 index 0000000000..0f6f5ffadc --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -0,0 +1,48 @@ +using Godot; +using GodotTools.Internals; +using static GodotTools.Internals.Globals; + +namespace GodotTools +{ + public class HotReloadAssemblyWatcher : Node + { + private Timer watchTimer; + + public override void _Notification(int what) + { + if (what == MainLoop.NotificationWmFocusIn) + { + RestartTimer(); + + if (Internal.IsAssembliesReloadingNeeded()) + Internal.ReloadAssemblies(softReload: false); + } + } + + private void TimerTimeout() + { + if (Internal.IsAssembliesReloadingNeeded()) + Internal.ReloadAssemblies(softReload: false); + } + + public void RestartTimer() + { + watchTimer.Stop(); + watchTimer.Start(); + } + + public override void _Ready() + { + base._Ready(); + + watchTimer = new Timer + { + OneShot = false, + WaitTime = (float) EditorDef("mono/assembly_watch_interval_sec", 0.5) + }; + watchTimer.Connect("timeout", this, nameof(TimerTimeout)); + AddChild(watchTimer); + watchTimer.Start(); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/BindingsGenerator.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/BindingsGenerator.cs new file mode 100644 index 0000000000..1daa5e138e --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/BindingsGenerator.cs @@ -0,0 +1,87 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace GodotTools.Internals +{ + public class BindingsGenerator : IDisposable + { + class BindingsGeneratorSafeHandle : SafeHandle + { + public BindingsGeneratorSafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + internal_Dtor(handle); + return true; + } + } + + private BindingsGeneratorSafeHandle safeHandle; + private bool disposed = false; + + public bool LogPrintEnabled + { + get => internal_LogPrintEnabled(GetPtr()); + set => internal_SetLogPrintEnabled(GetPtr(), value); + } + + public static uint Version => internal_Version(); + public static uint CsGlueVersion => internal_CsGlueVersion(); + + public Godot.Error GenerateCsApi(string outputDir) => internal_GenerateCsApi(GetPtr(), outputDir); + + internal IntPtr GetPtr() + { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + if (disposed) + return; + + if (safeHandle != null && !safeHandle.IsInvalid) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public BindingsGenerator() + { + safeHandle = new BindingsGeneratorSafeHandle(internal_Ctor()); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr internal_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_Dtor(IntPtr handle); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_LogPrintEnabled(IntPtr handle); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_SetLogPrintEnabled(IntPtr handle, bool enabled); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Godot.Error internal_GenerateCsApi(IntPtr handle, string outputDir); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern uint internal_Version(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern uint internal_CsGlueVersion(); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs new file mode 100644 index 0000000000..70ba7c733a --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.CompilerServices; +using Godot; + +namespace GodotTools.Internals +{ + public class EditorProgress : IDisposable + { + public string Task { get; } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_Create(string task, string label, int amount, bool canCancel); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_Dispose(string task); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_Step(string task, string state, int step, bool forceRefresh); + + public EditorProgress(string task, string label, int amount, bool canCancel = false) + { + Task = task; + internal_Create(task, label, amount, canCancel); + } + + ~EditorProgress() + { + // Should never rely on the GC to dispose EditorProgress. + // It should be disposed immediately when the task finishes. + GD.PushError("EditorProgress disposed by the Garbage Collector"); + Dispose(); + } + + public void Dispose() + { + internal_Dispose(Task); + GC.SuppressFinalize(this); + } + + public void Step(string state, int step = -1, bool forceRefresh = true) + { + internal_Step(Task, state, step, forceRefresh); + } + + public bool TryStep(string state, int step = -1, bool forceRefresh = true) + { + return internal_Step(Task, state, step, forceRefresh); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs new file mode 100644 index 0000000000..793f84fd77 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -0,0 +1,33 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace GodotTools.Internals +{ + public static class Globals + { + public static float EditorScale => internal_EditorScale(); + + public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) => + internal_GlobalDef(setting, defaultValue, restartIfChanged); + + public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) => + internal_EditorDef(setting, defaultValue, restartIfChanged); + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public static string TTR(this string text) => internal_TTR(text); + + // Internal Calls + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern float internal_EditorScale(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_TTR(string text); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs new file mode 100644 index 0000000000..ddf3b829b5 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -0,0 +1,91 @@ +using System.Runtime.CompilerServices; + +namespace GodotTools.Internals +{ + public static class GodotSharpDirs + { + public static string ResDataDir => internal_ResDataDir(); + public static string ResMetadataDir => internal_ResMetadataDir(); + public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir(); + public static string ResAssembliesDir => internal_ResAssembliesDir(); + public static string ResConfigDir => internal_ResConfigDir(); + public static string ResTempDir => internal_ResTempDir(); + public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir(); + public static string ResTempAssembliesDir => internal_ResTempAssembliesDir(); + + public static string MonoUserDir => internal_MonoUserDir(); + public static string MonoLogsDir => internal_MonoLogsDir(); + + #region Tools-only + public static string MonoSolutionsDir => internal_MonoSolutionsDir(); + public static string BuildLogsDirs => internal_BuildLogsDirs(); + + public static string ProjectSlnPath => internal_ProjectSlnPath(); + public static string ProjectCsProjPath => internal_ProjectCsProjPath(); + + public static string DataEditorToolsDir => internal_DataEditorToolsDir(); + public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir(); + #endregion + + public static string DataMonoEtcDir => internal_DataMonoEtcDir(); + public static string DataMonoLibDir => internal_DataMonoLibDir(); + + #region Windows-only + public static string DataMonoBinDir => internal_DataMonoBinDir(); + #endregion + + + #region Internal + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResDataDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResMetadataDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResAssembliesBaseDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResAssembliesDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResConfigDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResTempDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResTempAssembliesBaseDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ResTempAssembliesDir(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_MonoUserDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_MonoLogsDir(); + + #region Tools-only + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_MonoSolutionsDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_BuildLogsDirs(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ProjectSlnPath(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_ProjectCsProjPath(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_DataEditorToolsDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_DataEditorPrebuiltApiDir(); + #endregion + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_DataMonoEtcDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_DataMonoLibDir(); + + #region Windows-only + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_DataMonoBinDir(); + #endregion + + #endregion + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs new file mode 100644 index 0000000000..9526dd3c6f --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -0,0 +1,99 @@ +using System; +using System.Runtime.CompilerServices; +using Godot; +using Godot.Collections; + +namespace GodotTools.Internals +{ + public static class Internal + { + public const string CSharpLanguageType = "CSharpScript"; + public const string CSharpLanguageExtension = "cs"; + + public static string UpdateApiAssembliesFromPrebuilt() => + internal_UpdateApiAssembliesFromPrebuilt(); + + public static string FullTemplatesDir => + internal_FullTemplatesDir(); + + public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path); + + public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId); + + public static bool GodotIs32Bits() => internal_GodotIs32Bits(); + + public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble(); + + public static void GodotMainIteration() => internal_GodotMainIteration(); + + public static ulong GetCoreApiHash() => internal_GetCoreApiHash(); + + public static ulong GetEditorApiHash() => internal_GetEditorApiHash(); + + public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded(); + + public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload); + + public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts(); + + public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) => + internal_ScriptEditorEdit(resource, line, col, grabFocus); + + public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen(); + + public static Dictionary<string, object> GetScriptsMetadataOrNothing() => + internal_GetScriptsMetadataOrNothing(typeof(Dictionary<string, object>)); + + public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot(); + + // Internal Calls + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_UpdateApiAssembliesFromPrebuilt(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_FullTemplatesDir(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_SimplifyGodotPath(this string path); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_IsOsxAppBundleInstalled(string bundleId); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_GodotIs32Bits(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_GodotIsRealTDouble(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_GodotMainIteration(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern ulong internal_GetCoreApiHash(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern ulong internal_GetEditorApiHash(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_IsAssembliesReloadingNeeded(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_ReloadAssemblies(bool softReload); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_ScriptEditorDebuggerReloadScripts(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void internal_EditorNodeShowScriptScreen(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Dictionary<string, object> internal_GetScriptsMetadataOrNothing(Type dictType); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string internal_MonoWindowsInstallRoot(); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs new file mode 100644 index 0000000000..2497d276a9 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Godot; +using Godot.Collections; + +namespace GodotTools.Internals +{ + public static class ScriptClassParser + { + public class ClassDecl + { + public string Name { get; } + public string Namespace { get; } + public bool Nested { get; } + public int BaseCount { get; } + + public ClassDecl(string name, string @namespace, bool nested, int baseCount) + { + Name = name; + Namespace = @namespace; + Nested = nested; + BaseCount = baseCount; + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Error internal_ParseFile(string filePath, Array<Dictionary> classes); + + public static void ParseFileOrThrow(string filePath, out IEnumerable<ClassDecl> classes) + { + var classesArray = new Array<Dictionary>(); + var error = internal_ParseFile(filePath, classesArray); + if (error != Error.Ok) + throw new Exception($"Failed to determine namespace and class for script: {filePath}. Parse error: {error}"); + + var classesList = new List<ClassDecl>(); + + foreach (var classDeclDict in classesArray) + { + classesList.Add(new ClassDecl( + (string) classDeclDict["name"], + (string) classDeclDict["namespace"], + (bool) classDeclDict["nested"], + (int) classDeclDict["base_count"] + )); + } + + classes = classesList; + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs new file mode 100644 index 0000000000..53ff0891d5 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs @@ -0,0 +1,343 @@ +using Godot; +using System; +using System.IO; +using Godot.Collections; +using GodotTools.Internals; +using static GodotTools.Internals.Globals; +using File = GodotTools.Utils.File; +using Path = System.IO.Path; + +namespace GodotTools +{ + public class MonoBottomPanel : VBoxContainer + { + private EditorInterface editorInterface; + + private TabContainer panelTabs; + + private VBoxContainer panelBuildsTab; + + private ItemList buildTabsList; + private TabContainer buildTabs; + + private ToolButton warningsBtn; + private ToolButton errorsBtn; + private Button viewLogBtn; + + private void _UpdateBuildTabsList() + { + buildTabsList.Clear(); + + int currentTab = buildTabs.CurrentTab; + + bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount(); + + for (int i = 0; i < buildTabs.GetChildCount(); i++) + { + var tab = (MonoBuildTab) buildTabs.GetChild(i); + + if (tab == null) + continue; + + string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution); + itemName += " [" + tab.BuildInfo.Configuration + "]"; + + buildTabsList.AddItem(itemName, tab.IconTexture); + + string itemTooltip = "Solution: " + tab.BuildInfo.Solution; + itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration; + itemTooltip += "\nStatus: "; + + if (tab.BuildExited) + itemTooltip += tab.BuildResult == MonoBuildTab.BuildResults.Success ? "Succeeded" : "Errored"; + else + itemTooltip += "Running"; + + if (!tab.BuildExited || tab.BuildResult == MonoBuildTab.BuildResults.Error) + itemTooltip += $"\nErrors: {tab.ErrorCount}"; + + itemTooltip += $"\nWarnings: {tab.WarningCount}"; + + buildTabsList.SetItemTooltip(i, itemTooltip); + + if (noCurrentTab || currentTab == i) + { + buildTabsList.Select(i); + _BuildTabsItemSelected(i); + } + } + } + + public MonoBuildTab GetBuildTabFor(MonoBuildInfo buildInfo) + { + foreach (var buildTab in new Array<MonoBuildTab>(buildTabs.GetChildren())) + { + if (buildTab.BuildInfo.Equals(buildInfo)) + return buildTab; + } + + var newBuildTab = new MonoBuildTab(buildInfo); + AddBuildTab(newBuildTab); + + return newBuildTab; + } + + private void _BuildTabsItemSelected(int idx) + { + if (idx < 0 || idx >= buildTabs.GetTabCount()) + throw new IndexOutOfRangeException(); + + buildTabs.CurrentTab = idx; + if (!buildTabs.Visible) + buildTabs.Visible = true; + + warningsBtn.Visible = true; + errorsBtn.Visible = true; + viewLogBtn.Visible = true; + } + + private void _BuildTabsNothingSelected() + { + if (buildTabs.GetTabCount() != 0) + { + // just in case + buildTabs.Visible = false; + + // This callback is called when clicking on the empty space of the list. + // ItemList won't deselect the items automatically, so we must do it ourselves. + buildTabsList.UnselectAll(); + } + + warningsBtn.Visible = false; + errorsBtn.Visible = false; + viewLogBtn.Visible = false; + } + + private void _WarningsToggled(bool pressed) + { + int currentTab = buildTabs.CurrentTab; + + if (currentTab < 0 || currentTab >= buildTabs.GetTabCount()) + throw new InvalidOperationException("No tab selected"); + + var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab); + buildTab.WarningsVisible = pressed; + buildTab.UpdateIssuesList(); + } + + private void _ErrorsToggled(bool pressed) + { + int currentTab = buildTabs.CurrentTab; + + if (currentTab < 0 || currentTab >= buildTabs.GetTabCount()) + throw new InvalidOperationException("No tab selected"); + + var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab); + buildTab.ErrorsVisible = pressed; + buildTab.UpdateIssuesList(); + } + + public void BuildProjectPressed() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return; // No solution to build + + string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor"); + string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player"); + + CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath); + + if (File.Exists(editorScriptsMetadataPath)) + { + try + { + File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); + } + catch (IOException e) + { + GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}"); + return; + } + } + + var godotDefines = new[] + { + OS.GetName(), + Internal.GodotIs32Bits() ? "32" : "64" + }; + + bool buildSuccess = GodotSharpBuilds.BuildProjectBlocking("Tools", godotDefines); + + if (!buildSuccess) + return; + + // Notify running game for hot-reload + Internal.ScriptEditorDebuggerReloadScripts(); + + // Hot-reload in the editor + GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); + + if (Internal.IsAssembliesReloadingNeeded()) + Internal.ReloadAssemblies(softReload: false); + } + + private void _ViewLogPressed() + { + if (!buildTabsList.IsAnythingSelected()) + return; + + var selectedItems = buildTabsList.GetSelectedItems(); + + if (selectedItems.Length != 1) + throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}"); + + int selectedItem = selectedItems[0]; + + var buildTab = (MonoBuildTab) buildTabs.GetTabControl(selectedItem); + + OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildLogFileName)); + } + + public override void _Notification(int what) + { + base._Notification(what); + + if (what == EditorSettings.NotificationEditorSettingsChanged) + { + var editorBaseControl = editorInterface.GetBaseControl(); + panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles")); + panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles")); + panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles")); + } + } + + public void AddBuildTab(MonoBuildTab buildTab) + { + buildTabs.AddChild(buildTab); + RaiseBuildTab(buildTab); + } + + public void RaiseBuildTab(MonoBuildTab buildTab) + { + if (buildTab.GetParent() != buildTabs) + throw new InvalidOperationException("Build tab is not in the tabs list"); + + buildTabs.MoveChild(buildTab, 0); + _UpdateBuildTabsList(); + } + + public void ShowBuildTab() + { + for (int i = 0; i < panelTabs.GetTabCount(); i++) + { + if (panelTabs.GetTabControl(i) == panelBuildsTab) + { + panelTabs.CurrentTab = i; + GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this); + return; + } + } + + GD.PushError("Builds tab not found"); + } + + public override void _Ready() + { + base._Ready(); + + editorInterface = GodotSharpEditor.Instance.GetEditorInterface(); + + var editorBaseControl = editorInterface.GetBaseControl(); + + SizeFlagsVertical = (int) SizeFlags.ExpandFill; + SetAnchorsAndMarginsPreset(LayoutPreset.Wide); + + panelTabs = new TabContainer + { + TabAlign = TabContainer.TabAlignEnum.Left, + RectMinSize = new Vector2(0, 228) * EditorScale, + SizeFlagsVertical = (int) SizeFlags.ExpandFill + }; + panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles")); + panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles")); + panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles")); + AddChild(panelTabs); + + { + // Builds tab + panelBuildsTab = new VBoxContainer + { + Name = "Builds".TTR(), + SizeFlagsHorizontal = (int) SizeFlags.ExpandFill + }; + panelTabs.AddChild(panelBuildsTab); + + var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill}; + panelBuildsTab.AddChild(toolBarHBox); + + var buildProjectBtn = new Button + { + Text = "Build Project".TTR(), + FocusMode = FocusModeEnum.None + }; + buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed)); + toolBarHBox.AddChild(buildProjectBtn); + + toolBarHBox.AddSpacer(begin: false); + + warningsBtn = new ToolButton + { + Text = "Warnings".TTR(), + ToggleMode = true, + Pressed = true, + Visible = false, + FocusMode = FocusModeEnum.None + }; + warningsBtn.Connect("toggled", this, nameof(_WarningsToggled)); + toolBarHBox.AddChild(warningsBtn); + + errorsBtn = new ToolButton + { + Text = "Errors".TTR(), + ToggleMode = true, + Pressed = true, + Visible = false, + FocusMode = FocusModeEnum.None + }; + errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled)); + toolBarHBox.AddChild(errorsBtn); + + toolBarHBox.AddSpacer(begin: false); + + viewLogBtn = new Button + { + Text = "View log".TTR(), + FocusMode = FocusModeEnum.None, + Visible = false + }; + viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed)); + toolBarHBox.AddChild(viewLogBtn); + + var hsc = new HSplitContainer + { + SizeFlagsHorizontal = (int) SizeFlags.ExpandFill, + SizeFlagsVertical = (int) SizeFlags.ExpandFill + }; + panelBuildsTab.AddChild(hsc); + + buildTabsList = new ItemList {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill}; + buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected)); + buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected)); + hsc.AddChild(buildTabsList); + + buildTabs = new TabContainer + { + TabAlign = TabContainer.TabAlignEnum.Left, + SizeFlagsHorizontal = (int) SizeFlags.ExpandFill, + TabsVisible = false + }; + hsc.AddChild(buildTabs); + } + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs new file mode 100644 index 0000000000..858e852392 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs @@ -0,0 +1,47 @@ +using System; +using Godot; +using Godot.Collections; +using GodotTools.Internals; +using Path = System.IO.Path; + +namespace GodotTools +{ + [Serializable] + public sealed class MonoBuildInfo : Reference // TODO Remove Reference once we have proper serialization + { + public string Solution { get; } + public string Configuration { get; } + public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization + + public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}"); + + public override bool Equals(object obj) + { + if (obj is MonoBuildInfo other) + return other.Solution == Solution && other.Configuration == Configuration; + + return false; + } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = hash * 29 + Solution.GetHashCode(); + hash = hash * 29 + Configuration.GetHashCode(); + return hash; + } + } + + private MonoBuildInfo() + { + } + + public MonoBuildInfo(string solution, string configuration) + { + Solution = solution; + Configuration = configuration; + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs new file mode 100644 index 0000000000..75fdacc0da --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs @@ -0,0 +1,260 @@ +using Godot; +using System; +using Godot.Collections; +using GodotTools.Internals; +using File = GodotTools.Utils.File; +using Path = System.IO.Path; + +namespace GodotTools +{ + public class MonoBuildTab : VBoxContainer + { + public enum BuildResults + { + Error, + Success + } + + [Serializable] + private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization + { + public bool Warning { get; set; } + public string File { get; set; } + public int Line { get; set; } + public int Column { get; set; } + public string Code { get; set; } + public string Message { get; set; } + public string ProjectFile { get; set; } + } + + private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization + private ItemList issuesList; + + public bool BuildExited { get; private set; } = false; + + public BuildResults? BuildResult { get; private set; } = null; + + public int ErrorCount { get; private set; } = 0; + + public int WarningCount { get; private set; } = 0; + + public bool ErrorsVisible { get; set; } = true; + public bool WarningsVisible { get; set; } = true; + + public Texture IconTexture + { + get + { + if (!BuildExited) + return GetIcon("Stop", "EditorIcons"); + + if (BuildResult == BuildResults.Error) + return GetIcon("StatusError", "EditorIcons"); + + return GetIcon("StatusSuccess", "EditorIcons"); + } + } + + public MonoBuildInfo BuildInfo { get; private set; } + + private void _LoadIssuesFromFile(string csvFile) + { + using (var file = new Godot.File()) + { + Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read); + + if (openError != Error.Ok) + return; + + while (!file.EofReached()) + { + string[] csvColumns = file.GetCsvLine(); + + if (csvColumns.Length == 1 && csvColumns[0].Empty()) + return; + + if (csvColumns.Length != 7) + { + GD.PushError($"Expected 7 columns, got {csvColumns.Length}"); + continue; + } + + var issue = new BuildIssue + { + Warning = csvColumns[0] == "warning", + File = csvColumns[1], + Line = int.Parse(csvColumns[2]), + Column = int.Parse(csvColumns[3]), + Code = csvColumns[4], + Message = csvColumns[5], + ProjectFile = csvColumns[6] + }; + + if (issue.Warning) + WarningCount += 1; + else + ErrorCount += 1; + + issues.Add(issue); + } + } + } + + private void _IssueActivated(int idx) + { + if (idx < 0 || idx >= issuesList.GetItemCount()) + throw new IndexOutOfRangeException("Item list index out of range"); + + // Get correct issue idx from issue list + int issueIndex = (int) issuesList.GetItemMetadata(idx); + + if (idx < 0 || idx >= issues.Count) + throw new IndexOutOfRangeException("Issue index out of range"); + + BuildIssue issue = issues[issueIndex]; + + if (issue.ProjectFile.Empty() && issue.File.Empty()) + return; + + string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir(); + + string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath()); + + if (!File.Exists(file)) + return; + + file = ProjectSettings.LocalizePath(file); + + if (file.StartsWith("res://")) + { + var script = (Script) ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType); + + if (script != null && Internal.ScriptEditorEdit(script, issue.Line, issue.Column)) + Internal.EditorNodeShowScriptScreen(); + } + } + + public void UpdateIssuesList() + { + issuesList.Clear(); + + using (var warningIcon = GetIcon("Warning", "EditorIcons")) + using (var errorIcon = GetIcon("Error", "EditorIcons")) + { + for (int i = 0; i < issues.Count; i++) + { + BuildIssue issue = issues[i]; + + if (!(issue.Warning ? WarningsVisible : ErrorsVisible)) + continue; + + string tooltip = string.Empty; + tooltip += $"Message: {issue.Message}"; + + if (!issue.Code.Empty()) + tooltip += $"\nCode: {issue.Code}"; + + tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}"; + + string text = string.Empty; + + if (!issue.File.Empty()) + { + text += $"{issue.File}({issue.Line},{issue.Column}): "; + + tooltip += $"\nFile: {issue.File}"; + tooltip += $"\nLine: {issue.Line}"; + tooltip += $"\nColumn: {issue.Column}"; + } + + if (!issue.ProjectFile.Empty()) + tooltip += $"\nProject: {issue.ProjectFile}"; + + text += issue.Message; + + int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal); + string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx); + issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon); + + int index = issuesList.GetItemCount() - 1; + issuesList.SetItemTooltip(index, tooltip); + issuesList.SetItemMetadata(index, i); + } + } + } + + public void OnBuildStart() + { + BuildExited = false; + + issues.Clear(); + WarningCount = 0; + ErrorCount = 0; + UpdateIssuesList(); + + GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this); + } + + public void OnBuildExit(BuildResults result) + { + BuildExited = true; + BuildResult = result; + + _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildIssuesFileName)); + UpdateIssuesList(); + + GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this); + } + + public void OnBuildExecFailed(string cause) + { + BuildExited = true; + BuildResult = BuildResults.Error; + + issuesList.Clear(); + + var issue = new BuildIssue {Message = cause, Warning = false}; + + ErrorCount += 1; + issues.Add(issue); + + UpdateIssuesList(); + + GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this); + } + + public void RestartBuild() + { + if (!BuildExited) + throw new InvalidOperationException("Build already started"); + + GodotSharpBuilds.RestartBuild(this); + } + + public void StopBuild() + { + if (!BuildExited) + throw new InvalidOperationException("Build is not in progress"); + + GodotSharpBuilds.StopBuild(this); + } + + public override void _Ready() + { + base._Ready(); + + issuesList = new ItemList {SizeFlagsVertical = (int) SizeFlags.ExpandFill}; + issuesList.Connect("item_activated", this, nameof(_IssueActivated)); + AddChild(issuesList); + } + + private MonoBuildTab() + { + } + + public MonoBuildTab(MonoBuildInfo buildInfo) + { + BuildInfo = buildInfo; + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs b/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs index fba4a8f65c..0c8d86e799 100644 --- a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs +++ b/modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs @@ -1,11 +1,11 @@ +using GodotTools.Core; using System; using System.IO; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; +using GodotTools.Internals; -namespace GodotSharpTools.Editor +namespace GodotTools { public class MonoDevelopInstance { @@ -15,24 +15,24 @@ namespace GodotSharpTools.Editor VisualStudioForMac = 1 } - readonly string solutionFile; - readonly EditorId editorId; + private readonly string solutionFile; + private readonly EditorId editorId; - Process process; + private Process process; - public void Execute(string[] files) + public void Execute(params string[] files) { bool newWindow = process == null || process.HasExited; - List<string> args = new List<string>(); + var args = new List<string>(); string command; if (Utils.OS.IsOSX()) { - string bundleId = codeEditorBundleIds[editorId]; + string bundleId = CodeEditorBundleIds[editorId]; - if (IsApplicationBundleInstalled(bundleId)) + if (Internal.IsOsxAppBundleInstalled(bundleId)) { command = "open"; @@ -47,12 +47,12 @@ namespace GodotSharpTools.Editor } else { - command = codeEditorPaths[editorId]; + command = CodeEditorPaths[editorId]; } } else { - command = codeEditorPaths[editorId]; + command = CodeEditorPaths[editorId]; } args.Add("--ipc-tcp"); @@ -72,7 +72,7 @@ namespace GodotSharpTools.Editor if (newWindow) { - process = Process.Start(new ProcessStartInfo() + process = Process.Start(new ProcessStartInfo { FileName = command, Arguments = string.Join(" ", args), @@ -81,12 +81,12 @@ namespace GodotSharpTools.Editor } else { - Process.Start(new ProcessStartInfo() + Process.Start(new ProcessStartInfo { FileName = command, Arguments = string.Join(" ", args), UseShellExecute = false - }); + })?.Dispose(); } } @@ -99,45 +99,42 @@ namespace GodotSharpTools.Editor this.editorId = editorId; } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern static bool IsApplicationBundleInstalled(string bundleId); - - static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths; - static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds; + private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorPaths; + private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorBundleIds; static MonoDevelopInstance() { if (Utils.OS.IsOSX()) { - codeEditorPaths = new Dictionary<EditorId, string> + CodeEditorPaths = new Dictionary<EditorId, string> { // Rely on PATH - { EditorId.MonoDevelop, "monodevelop" }, - { EditorId.VisualStudioForMac, "VisualStudio" } + {EditorId.MonoDevelop, "monodevelop"}, + {EditorId.VisualStudioForMac, "VisualStudio"} }; - codeEditorBundleIds = new Dictionary<EditorId, string> + CodeEditorBundleIds = new Dictionary<EditorId, string> { // TODO EditorId.MonoDevelop - { EditorId.VisualStudioForMac, "com.microsoft.visual-studio" } + {EditorId.VisualStudioForMac, "com.microsoft.visual-studio"} }; } else if (Utils.OS.IsWindows()) { - codeEditorPaths = new Dictionary<EditorId, string> + CodeEditorPaths = new Dictionary<EditorId, string> { // XamarinStudio is no longer a thing, and the latest version is quite old // MonoDevelop is available from source only on Windows. The recommendation // is to use Visual Studio instead. Since there are no official builds, we // will rely on custom MonoDevelop builds being added to PATH. - { EditorId.MonoDevelop, "MonoDevelop.exe" } + {EditorId.MonoDevelop, "MonoDevelop.exe"} }; } else if (Utils.OS.IsUnix()) { - codeEditorPaths = new Dictionary<EditorId, string> + CodeEditorPaths = new Dictionary<EditorId, string> { // Rely on PATH - { EditorId.MonoDevelop, "monodevelop" } + {EditorId.MonoDevelop, "monodevelop"} }; } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Properties/AssemblyInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f5fe85c722 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("GodotTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Godot Engine contributors")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs new file mode 100644 index 0000000000..3ae6c10bbf --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace GodotTools.Utils +{ + public static class CollectionExtensions + { + public static T SelectFirstNotNull<T>(this IEnumerable<T> enumerable, Func<T, T> predicate, T orElse = null) + where T : class + { + foreach (T elem in enumerable) + { + if (predicate(elem) != null) + return elem; + } + + return orElse; + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs new file mode 100644 index 0000000000..c67d48b92a --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs @@ -0,0 +1,40 @@ +using System.IO; +using Godot; + +namespace GodotTools.Utils +{ + public static class Directory + { + private static string GlobalizePath(this string path) + { + return ProjectSettings.GlobalizePath(path); + } + + public static bool Exists(string path) + { + return System.IO.Directory.Exists(path.GlobalizePath()); + } + + /// Create directory recursively + public static DirectoryInfo CreateDirectory(string path) + { + return System.IO.Directory.CreateDirectory(path.GlobalizePath()); + } + + public static void Delete(string path, bool recursive) + { + System.IO.Directory.Delete(path.GlobalizePath(), recursive); + } + + + public static string[] GetDirectories(string path, string searchPattern, SearchOption searchOption) + { + return System.IO.Directory.GetDirectories(path.GlobalizePath(), searchPattern, searchOption); + } + + public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption) + { + return System.IO.Directory.GetFiles(path.GlobalizePath(), searchPattern, searchOption); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/File.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/File.cs new file mode 100644 index 0000000000..e1e2188edb --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/File.cs @@ -0,0 +1,43 @@ +using System; +using Godot; + +namespace GodotTools.Utils +{ + public static class File + { + private static string GlobalizePath(this string path) + { + return ProjectSettings.GlobalizePath(path); + } + + public static void WriteAllText(string path, string contents) + { + System.IO.File.WriteAllText(path.GlobalizePath(), contents); + } + + public static bool Exists(string path) + { + return System.IO.File.Exists(path.GlobalizePath()); + } + + public static DateTime GetLastWriteTime(string path) + { + return System.IO.File.GetLastWriteTime(path.GlobalizePath()); + } + + public static void Delete(string path) + { + System.IO.File.Delete(path.GlobalizePath()); + } + + public static void Copy(string sourceFileName, string destFileName) + { + System.IO.File.Copy(sourceFileName.GlobalizePath(), destFileName.GlobalizePath(), overwrite: true); + } + + public static byte[] ReadAllBytes(string path) + { + return System.IO.File.ReadAllBytes(path.GlobalizePath()); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs new file mode 100644 index 0000000000..e48b1115db --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace GodotTools.Utils +{ + public static class OS + { + [MethodImpl(MethodImplOptions.InternalCall)] + extern static string GetPlatformName(); + + const string HaikuName = "Haiku"; + const string OSXName = "OSX"; + const string ServerName = "Server"; + const string UWPName = "UWP"; + const string WindowsName = "Windows"; + const string X11Name = "X11"; + + public static bool IsHaiku() + { + return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsOSX() + { + return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsServer() + { + return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsUWP() + { + return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsWindows() + { + return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsX11() + { + return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + private static bool? _isUnixCache; + private static readonly string[] UnixPlatforms = {HaikuName, OSXName, ServerName, X11Name}; + + public static bool IsUnix() + { + if (_isUnixCache.HasValue) + return _isUnixCache.Value; + + string osName = GetPlatformName(); + _isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); + return _isUnixCache.Value; + } + + public static char PathSep => IsWindows() ? ';' : ':'; + + public static string PathWhich(string name) + { + string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null; + string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep); + + var searchDirs = new List<string>(); + + if (pathDirs != null) + searchDirs.AddRange(pathDirs); + + searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list + + foreach (var dir in searchDirs) + { + string path = Path.Combine(dir, name); + + if (IsWindows() && windowsExts != null) + { + foreach (var extension in windowsExts) + { + string pathWithExtension = path + extension; + + if (File.Exists(pathWithExtension)) + return pathWithExtension; + } + } + else + { + if (File.Exists(path)) + return path; + } + } + + return null; + } + + public static void RunProcess(string command, IEnumerable<string> arguments) + { + string CmdLineArgsToString(IEnumerable<string> args) + { + return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg)); + } + + ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments)) + { + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + + using (Process process = Process.Start(startInfo)) + { + if (process == null) + throw new Exception("No process was started"); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } + } + } +} diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 166b3e1324..45037bf637 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,6 +80,7 @@ #define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method" #define C_LOCAL_RET "ret" +#define C_LOCAL_VARARG_RET "vararg_ret" #define C_LOCAL_PTRCALL_ARGS "call_args" #define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT" @@ -96,13 +97,19 @@ #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define BINDINGS_GENERATOR_VERSION UINT32_C(5) +#define BINDINGS_GENERATOR_VERSION UINT32_C(9) -const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; +const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); -bool BindingsGenerator::verbose_output = false; +static String fix_doc_description(const String &p_bbcode) { -BindingsGenerator *BindingsGenerator::singleton = NULL; + // This seems to be the correct way to do this. It's the same EditorHelp does. + + return p_bbcode.dedent() + .replace("\t", "") + .replace("\r", "") + .strip_edges(); +} static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false) { @@ -172,6 +179,457 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up return ret; } +String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { + + // Based on the version in EditorHelp + + if (p_bbcode.empty()) + return String(); + + DocData *doc = EditorHelp::get_doc_data(); + + String bbcode = p_bbcode; + + StringBuilder xml_output; + + xml_output.append("<para>"); + + List<String> tag_stack; + bool code_tag = false; + + int pos = 0; + while (pos < bbcode.length()) { + int brk_pos = bbcode.find("[", pos); + + if (brk_pos < 0) + brk_pos = bbcode.length(); + + if (brk_pos > pos) { + String text = bbcode.substr(pos, brk_pos - pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) + xml_output.append("<para>"); + + xml_output.append(lines[i].xml_escape()); + + if (i != lines.size() - 1) + xml_output.append("</para>\n"); + } + } + } + + if (brk_pos == bbcode.length()) + break; // nothing else to add + + int brk_end = bbcode.find("]", brk_pos + 1); + + if (brk_end == -1) { + String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) + xml_output.append("<para>"); + + xml_output.append(lines[i].xml_escape()); + + if (i != lines.size() - 1) + xml_output.append("</para>\n"); + } + } + + break; + } + + String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1); + + if (tag.begins_with("/")) { + bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); + + if (!tag_ok) { + xml_output.append("["); + pos = brk_pos + 1; + continue; + } + + tag_stack.pop_front(); + pos = brk_end + 1; + code_tag = false; + + if (tag == "/url") { + xml_output.append("</a>"); + } else if (tag == "/code") { + xml_output.append("</c>"); + } else if (tag == "/codeblock") { + xml_output.append("</code>"); + } + } else if (code_tag) { + xml_output.append("["); + pos = brk_pos + 1; + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ")) { + String link_target = tag.substr(tag.find(" ") + 1, tag.length()); + String link_tag = tag.substr(0, tag.find(" ")); + + Vector<String> link_target_parts = link_target.split("."); + + if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) { + ERR_PRINTS("Invalid reference format: " + tag); + + xml_output.append("<c>"); + xml_output.append(tag); + xml_output.append("</c>"); + + pos = brk_end + 1; + continue; + } + + const TypeInterface *target_itype; + StringName target_cname; + + if (link_target_parts.size() == 2) { + target_itype = _get_type_or_null(TypeReference(link_target_parts[0])); + if (!target_itype) { + target_itype = _get_type_or_null(TypeReference("_" + link_target_parts[0])); + } + target_cname = link_target_parts[1]; + } else { + target_itype = p_itype; + target_cname = link_target_parts[0]; + } + + if (link_tag == "method") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } else { + const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname); + + if (target_imethod) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_imethod->proxy_name); + xml_output.append("\"/>"); + } + } + } else if (link_tag == "member") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } else { + const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname); + + if (target_iprop) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_iprop->proxy_name); + xml_output.append("\"/>"); + } + } + } else if (link_tag == "signal") { + // We do not declare signals in any way in C#, so there is nothing to reference + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } else if (link_tag == "enum") { + StringName search_cname = !target_itype ? target_cname : + StringName(target_itype->name + "." + (String)target_cname); + + const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname); + + if (!enum_match && search_cname != target_cname) { + enum_match = enum_types.find(target_cname); + } + + if (enum_match) { + const TypeInterface &target_enum_itype = enum_match->value(); + + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve enum reference in documentation: " + link_target); + + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } + } else if (link_tag == "const") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } else if (!target_itype && target_cname == name_cache.type_at_GlobalScope) { + String target_name = (String)target_cname; + + // Try to find as a global constant + const ConstantInterface *target_iconst = find_constant_by_name(target_name, global_constants); + + if (target_iconst) { + // Found global constant + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "." BINDINGS_GLOBAL_SCOPE_CLASS "."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + // Try to find as global enum constant + const EnumInterface *target_ienum = NULL; + + for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(target_name, target_ienum->constants); + if (target_iconst) + break; + } + + if (target_iconst) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_ienum->cname); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve global constant reference in documentation: " + link_target); + + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } + } + } else { + String target_name = (String)target_cname; + + // Try to find the constant in the current class + const ConstantInterface *target_iconst = find_constant_by_name(target_name, target_itype->constants); + + if (target_iconst) { + // Found constant in current class + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + // Try to find as enum constant in the current class + const EnumInterface *target_ienum = NULL; + + for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(target_name, target_ienum->constants); + if (target_iconst) + break; + } + + if (target_iconst) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("."); + xml_output.append(target_ienum->cname); + xml_output.append("."); + xml_output.append(target_iconst->proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve constant reference in documentation: " + link_target); + + xml_output.append("<c>"); + xml_output.append(link_target); + xml_output.append("</c>"); + } + } + } + } + + pos = brk_end + 1; + } else if (doc->class_list.has(tag)) { + if (tag == "Array" || tag == "Dictionary") { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE_COLLECTIONS "."); + xml_output.append(tag); + xml_output.append("\"/>"); + } else if (tag == "bool" || tag == "int") { + xml_output.append("<see cref=\""); + xml_output.append(tag); + xml_output.append("\"/>"); + } else if (tag == "float") { + xml_output.append("<see cref=\"" +#ifdef REAL_T_IS_DOUBLE + "double" +#else + "float" +#endif + "\"/>"); + } else if (tag == "Variant") { + // We use System.Object for Variant, so there is no Variant type in C# + xml_output.append("<c>Variant</c>"); + } else if (tag == "String") { + xml_output.append("<see cref=\"string\"/>"); + } else if (tag == "Nil") { + xml_output.append("<see langword=\"null\"/>"); + } else if (tag.begins_with("@")) { + // @GlobalScope, @GDScript, etc + xml_output.append("<c>"); + xml_output.append(tag); + xml_output.append("</c>"); + } else if (tag == "PoolByteArray") { + xml_output.append("<see cref=\"byte\"/>"); + } else if (tag == "PoolIntArray") { + xml_output.append("<see cref=\"int\"/>"); + } else if (tag == "PoolRealArray") { +#ifdef REAL_T_IS_DOUBLE + xml_output.append("<see cref=\"double\"/>"); +#else + xml_output.append("<see cref=\"float\"/>"); +#endif + } else if (tag == "PoolStringArray") { + xml_output.append("<see cref=\"string\"/>"); + } else if (tag == "PoolVector2Array") { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>"); + } else if (tag == "PoolVector3Array") { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>"); + } else if (tag == "PoolColorArray") { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>"); + } else { + const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag)); + + if (!target_itype) { + target_itype = _get_type_or_null(TypeReference("_" + tag)); + } + + if (target_itype) { + xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); + xml_output.append(target_itype->proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve type reference in documentation: " + tag); + + xml_output.append("<c>"); + xml_output.append(tag); + xml_output.append("</c>"); + } + } + + pos = brk_end + 1; + } else if (tag == "b") { + // bold is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "i") { + // italics is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "code") { + xml_output.append("<c>"); + + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "codeblock") { + xml_output.append("<code>"); + + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "center") { + // center is alignment not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "br") { + xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now. + pos = brk_end + 1; + } else if (tag == "u") { + // underline is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "s") { + // strikethrough is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "url") { + int end = bbcode.find("[", brk_end); + if (end == -1) + end = bbcode.length(); + String url = bbcode.substr(brk_end + 1, end - brk_end - 1); + xml_output.append("<a href=\""); + xml_output.append(url); + xml_output.append("\">"); + xml_output.append(url); + + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag.begins_with("url=")) { + String url = tag.substr(4, tag.length()); + xml_output.append("<a href=\""); + xml_output.append(url); + xml_output.append("\">"); + + pos = brk_end + 1; + tag_stack.push_front("url"); + } else if (tag == "img") { + int end = bbcode.find("[", brk_end); + if (end == -1) + end = bbcode.length(); + String image = bbcode.substr(brk_end + 1, end - brk_end - 1); + + // Not supported. Just append the bbcode. + xml_output.append("[img]"); + xml_output.append(image); + xml_output.append("[/img]"); + + pos = end; + tag_stack.push_front(tag); + } else if (tag.begins_with("color=")) { + // Not supported. + pos = brk_end + 1; + tag_stack.push_front("color"); + } else if (tag.begins_with("font=")) { + // Not supported. + pos = brk_end + 1; + tag_stack.push_front("font"); + } else { + xml_output.append("["); // ignore + pos = brk_pos + 1; + } + } + + xml_output.append("</para>"); + + return xml_output.as_string(); +} + int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { CRASH_COND(p_ienum.constants.empty()); @@ -294,44 +752,47 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { } } -void BindingsGenerator::_generate_global_constants(List<String> &p_output) { +void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { // Constants (in partial GD class) - p_output.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - p_output.push_back(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{"); + p_output.append("\n#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n"); + + p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{"); for (const List<ConstantInterface>::Element *E = global_constants.front(); E; E = E->next()) { const ConstantInterface &iconstant = E->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); - p_output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - } - p_output.push_back(INDENT2 "/// </summary>"); + p_output.append(INDENT2 "/// </summary>"); + } } - p_output.push_back(MEMBER_BEGIN "public const int "); - p_output.push_back(iconstant.proxy_name); - p_output.push_back(" = "); - p_output.push_back(itos(iconstant.value)); - p_output.push_back(";"); + p_output.append(MEMBER_BEGIN "public const int "); + p_output.append(iconstant.proxy_name); + p_output.append(" = "); + p_output.append(itos(iconstant.value)); + p_output.append(";"); } if (!global_constants.empty()) - p_output.push_back("\n"); + p_output.append("\n"); - p_output.push_back(INDENT1 CLOSE_BLOCK); // end of GD class + p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class // Enums @@ -351,88 +812,82 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { CRASH_COND(enum_class_name != "Variant"); // Hard-coded... - if (verbose_output) { - WARN_PRINTS("Declaring global enum `" + enum_proxy_name + "` inside static class `" + enum_class_name + "`"); - } + _log("Declaring global enum `%s` inside static class `%s`\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data()); - p_output.push_back("\n" INDENT1 "public static partial class "); - p_output.push_back(enum_class_name); - p_output.push_back("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" INDENT1 "public static partial class "); + p_output.append(enum_class_name); + p_output.append("\n" INDENT1 OPEN_BLOCK); } - p_output.push_back("\n" INDENT1 "public enum "); - p_output.push_back(enum_proxy_name); - p_output.push_back("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" INDENT1 "public enum "); + p_output.append(enum_proxy_name); + p_output.append("\n" INDENT1 OPEN_BLOCK); - for (const List<ConstantInterface>::Element *E = ienum.constants.front(); E; E = E->next()) { - const ConstantInterface &iconstant = E->get(); + for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) { + const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - p_output.push_back(INDENT2 "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.append(INDENT2 "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); - p_output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - } - p_output.push_back(INDENT2 "/// </summary>\n"); + p_output.append(INDENT2 "/// </summary>\n"); + } } - p_output.push_back(INDENT2); - p_output.push_back(iconstant.proxy_name); - p_output.push_back(" = "); - p_output.push_back(itos(iconstant.value)); - p_output.push_back(E != ienum.constants.back() ? ",\n" : "\n"); + p_output.append(INDENT2); + p_output.append(iconstant.proxy_name); + p_output.append(" = "); + p_output.append(itos(iconstant.value)); + p_output.append(F != ienum.constants.back() ? ",\n" : "\n"); } - p_output.push_back(INDENT1 CLOSE_BLOCK); + p_output.append(INDENT1 CLOSE_BLOCK); if (enum_in_static_class) - p_output.push_back(INDENT1 CLOSE_BLOCK); + p_output.append(INDENT1 CLOSE_BLOCK); } - p_output.push_back(CLOSE_BLOCK); // end of namespace -} - -Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { + p_output.append(CLOSE_BLOCK); // end of namespace - verbose_output = p_verbose_output; + p_output.append("\n#pragma warning restore CS1591\n"); +} - String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME); +Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_items) { DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); - if (!DirAccess::exists(proj_dir)) { - Error err = da->make_dir_recursive(proj_dir); + if (!DirAccess::exists(p_proj_dir)) { + Error err = da->make_dir_recursive(p_proj_dir); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } - da->change_dir(proj_dir); + da->change_dir(p_proj_dir); da->make_dir("Core"); da->make_dir("ObjectType"); - String core_dir = path_join(proj_dir, "Core"); - String obj_type_dir = path_join(proj_dir, "ObjectType"); - - Vector<String> compile_items; + String core_dir = path::join(p_proj_dir, "Core"); + String obj_type_dir = path::join(p_proj_dir, "ObjectType"); // Generate source file for global scope constants and enums { - List<String> constants_source; + StringBuilder constants_source; _generate_global_constants(constants_source); - String output_file = path_join(core_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs"); + String output_file = path::join(core_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs"); Error save_err = _save_file(output_file, constants_source); if (save_err != OK) return save_err; - compile_items.push_back(output_file); + r_compile_items.push_back(output_file); } for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) { @@ -441,7 +896,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, if (itype.api_type == ClassDB::API_EDITOR) continue; - String output_file = path_join(obj_type_dir, itype.proxy_name + ".cs"); + String output_file = path::join(obj_type_dir, itype.proxy_name + ".cs"); Error err = _generate_cs_type(itype, output_file); if (err == ERR_SKIP) @@ -450,19 +905,19 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, if (err != OK) return err; - compile_items.push_back(output_file); + r_compile_items.push_back(output_file); } // Generate sources from compressed files - Map<String, CompressedFile> compressed_files; + Map<String, GodotCsCompressedFile> compressed_files; get_compressed_files(compressed_files); - for (Map<String, CompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) { + for (Map<String, GodotCsCompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) { const String &file_name = E->key(); - const CompressedFile &file_data = E->value(); + const GodotCsCompressedFile &file_data = E->value(); - String output_file = path_join(core_dir, file_name); + String output_file = path::join(core_dir, file_name); Vector<uint8_t> data; data.resize(file_data.uncompressed_size); @@ -471,8 +926,6 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, String output_dir = output_file.get_base_dir(); if (!DirAccess::exists(output_dir)) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(output_dir)); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } @@ -482,31 +935,31 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, file->store_buffer(data.ptr(), data.size()); file->close(); - compile_items.push_back(output_file); + r_compile_items.push_back(output_file); } - List<String> cs_icalls_content; - - cs_icalls_content.push_back("using System;\n" - "using System.Runtime.CompilerServices;\n" - "\n"); - cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); - - cs_icalls_content.push_back(MEMBER_BEGIN "internal static ulong godot_api_hash = "); - cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); - cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint bindings_version = "); - cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint cs_glue_version = "); - cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); - -#define ADD_INTERNAL_CALL(m_icall) \ - if (!m_icall.editor_only) { \ - cs_icalls_content.push_back(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.push_back(INDENT2 "internal extern static "); \ - cs_icalls_content.push_back(m_icall.im_type_out + " "); \ - cs_icalls_content.push_back(m_icall.name + "("); \ - cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + StringBuilder cs_icalls_content; + + cs_icalls_content.append("using System;\n" + "using System.Runtime.CompilerServices;\n" + "\n"); + cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); + cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); + cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = "); + cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = "); + cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (!m_icall.editor_only) { \ + cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(m_icall.im_type_out + " "); \ + cs_icalls_content.append(m_icall.name + "("); \ + cs_icalls_content.append(m_icall.im_sig + ");\n"); \ } for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) @@ -516,54 +969,35 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, #undef ADD_INTERNAL_CALL - cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); + String internal_methods_file = path::join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) return err; - compile_items.push_back(internal_methods_file); - - String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items); - - DotNetSolution::ProjectInfo proj_info; - proj_info.guid = guid; - proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj"); - proj_info.configs.push_back("Debug"); - proj_info.configs.push_back("Release"); - - r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info); - - if (verbose_output) - OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n"); + r_compile_items.push_back(internal_methods_file); return OK; } -Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { - - verbose_output = p_verbose_output; - - String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME); +Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items) { DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); - if (!DirAccess::exists(proj_dir)) { - Error err = da->make_dir_recursive(proj_dir); + if (!DirAccess::exists(p_proj_dir)) { + Error err = da->make_dir_recursive(p_proj_dir); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } - da->change_dir(proj_dir); + da->change_dir(p_proj_dir); da->make_dir("Core"); da->make_dir("ObjectType"); - String core_dir = path_join(proj_dir, "Core"); - String obj_type_dir = path_join(proj_dir, "ObjectType"); - - Vector<String> compile_items; + String core_dir = path::join(p_proj_dir, "Core"); + String obj_type_dir = path::join(p_proj_dir, "ObjectType"); for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) { const TypeInterface &itype = E.get(); @@ -571,7 +1005,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir if (itype.api_type != ClassDB::API_EDITOR) continue; - String output_file = path_join(obj_type_dir, itype.proxy_name + ".cs"); + String output_file = path::join(obj_type_dir, itype.proxy_name + ".cs"); Error err = _generate_cs_type(itype, output_file); if (err == ERR_SKIP) @@ -580,32 +1014,32 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir if (err != OK) return err; - compile_items.push_back(output_file); + r_compile_items.push_back(output_file); } - List<String> cs_icalls_content; - - cs_icalls_content.push_back("using System;\n" - "using System.Runtime.CompilerServices;\n" - "\n"); - cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); - - cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); - cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); - cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); - cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); - cs_icalls_content.push_back("\n"); - -#define ADD_INTERNAL_CALL(m_icall) \ - if (m_icall.editor_only) { \ - cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.push_back(INDENT2 "internal extern static "); \ - cs_icalls_content.push_back(m_icall.im_type_out + " "); \ - cs_icalls_content.push_back(m_icall.name + "("); \ - cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + StringBuilder cs_icalls_content; + + cs_icalls_content.append("using System;\n" + "using System.Runtime.CompilerServices;\n" + "\n"); + cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); + cs_icalls_content.append(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.append("\n"); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (m_icall.editor_only) { \ + cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(m_icall.im_type_out + " "); \ + cs_icalls_content.append(m_icall.name + "("); \ + cs_icalls_content.append(m_icall.im_sig + ");\n"); \ } for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) @@ -615,67 +1049,64 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir #undef ADD_INTERNAL_CALL - cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs"); + String internal_methods_file = path::join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) return err; - compile_items.push_back(internal_methods_file); - - String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items); - - DotNetSolution::ProjectInfo proj_info; - proj_info.guid = guid; - proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj"); - proj_info.configs.push_back("Debug"); - proj_info.configs.push_back("Release"); - - r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info); - - if (verbose_output) - OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n"); + r_compile_items.push_back(internal_methods_file); return OK; } -Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) { +Error BindingsGenerator::generate_cs_api(const String &p_output_dir) { + + String output_dir = path::abspath(path::realpath(p_output_dir)); DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); - if (!DirAccess::exists(p_output_dir)) { - Error err = da->make_dir_recursive(p_output_dir); + if (!DirAccess::exists(output_dir)) { + Error err = da->make_dir_recursive(output_dir); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } - DotNetSolution solution(API_SOLUTION_NAME); + Error proj_err; - if (!solution.set_path(p_output_dir)) - return ERR_FILE_NOT_FOUND; + // Generate GodotSharp source files - Error proj_err; + String core_proj_dir = output_dir.plus_file(CORE_API_ASSEMBLY_NAME); + Vector<String> core_compile_items; - proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output); + proj_err = generate_cs_core_project(core_proj_dir, core_compile_items); if (proj_err != OK) { ERR_PRINT("Generation of the Core API C# project failed"); return proj_err; } - proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output); + // Generate GodotSharpEditor source files + + String editor_proj_dir = output_dir.plus_file(EDITOR_API_ASSEMBLY_NAME); + Vector<String> editor_compile_items; + + proj_err = generate_cs_editor_project(editor_proj_dir, editor_compile_items); if (proj_err != OK) { ERR_PRINT("Generation of the Editor API C# project failed"); return proj_err; } - Error sln_error = solution.save(); - if (sln_error != OK) { - ERR_PRINT("Failed to save API solution"); - return sln_error; + // Generate solution + + if (!CSharpProject::generate_api_solution(output_dir, + core_proj_dir, core_compile_items, editor_proj_dir, editor_compile_items)) { + return ERR_CANT_CREATE; } + _log("The solution for the Godot API was generated successfully\n"); + return OK; } @@ -703,62 +1134,64 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - if (verbose_output) - OS::get_singleton()->print(String("Generating " + itype.proxy_name + ".cs...\n").utf8()); + _log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data()); String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types - List<String> output; + StringBuilder output; - output.push_back("using System;\n"); // IntPtr - output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable + output.append("using System;\n"); // IntPtr + output.append("using System.Diagnostics;\n"); // DebuggerBrowsable - output.push_back("\n#pragma warning disable CS1591 // Disable warning: " - "'Missing XML comment for publicly visible type or member'\n"); + output.append("\n" + "#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n" + "#pragma warning disable CS1573 // Disable warning: " + "'Parameter has no matching param tag in the XML comment'\n"); - output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); const DocData::ClassDoc *class_doc = itype.class_doc; if (class_doc && class_doc->description.size()) { - output.push_back(INDENT1 "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = class_doc->description.split("\n"); + if (summary_lines.size()) { + output.append(INDENT1 "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - output.push_back(INDENT1 "/// "); - output.push_back(description_line.xml_escape()); - output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + output.append(INDENT1 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - } - output.push_back(INDENT1 "/// </summary>\n"); + output.append(INDENT1 "/// </summary>\n"); + } } - output.push_back(INDENT1 "public "); + output.append(INDENT1 "public "); if (itype.is_singleton) { - output.push_back("static partial class "); + output.append("static partial class "); } else { - output.push_back(itype.is_instantiable ? "partial class " : "abstract partial class "); + output.append(itype.is_instantiable ? "partial class " : "abstract partial class "); } - output.push_back(itype.proxy_name); + output.append(itype.proxy_name); if (itype.is_singleton) { - output.push_back("\n"); + output.append("\n"); } else if (is_derived_type) { if (obj_types.has(itype.base_name)) { - output.push_back(" : "); - output.push_back(obj_types[itype.base_name].proxy_name); - output.push_back("\n"); + output.append(" : "); + output.append(obj_types[itype.base_name].proxy_name); + output.append("\n"); } else { ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class " + itype.name); return ERR_INVALID_DATA; } } - output.push_back(INDENT1 "{"); + output.append(INDENT1 "{"); if (class_doc) { @@ -768,31 +1201,31 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str const ConstantInterface &iconstant = E->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - output.push_back(MEMBER_BEGIN "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + output.append(MEMBER_BEGIN "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - output.push_back(INDENT2 "/// "); - output.push_back(description_line.xml_escape()); - output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + output.append(INDENT2 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - } - output.push_back(INDENT2 "/// </summary>"); + output.append(INDENT2 "/// </summary>"); + } } - output.push_back(MEMBER_BEGIN "public const int "); - output.push_back(iconstant.proxy_name); - output.push_back(" = "); - output.push_back(itos(iconstant.value)); - output.push_back(";"); + output.append(MEMBER_BEGIN "public const int "); + output.append(iconstant.proxy_name); + output.append(" = "); + output.append(itos(iconstant.value)); + output.append(";"); } if (itype.constants.size()) - output.push_back("\n"); + output.append("\n"); // Add enums @@ -801,38 +1234,38 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG); - output.push_back(MEMBER_BEGIN "public enum "); - output.push_back(ienum.cname.operator String()); - output.push_back(MEMBER_BEGIN OPEN_BLOCK); + output.append(MEMBER_BEGIN "public enum "); + output.append(ienum.cname.operator String()); + output.append(MEMBER_BEGIN OPEN_BLOCK); - for (const List<ConstantInterface>::Element *E = ienum.constants.front(); E; E = E->next()) { - const ConstantInterface &iconstant = E->get(); + for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) { + const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - output.push_back(INDENT3 "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + output.append(INDENT3 "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - output.push_back(INDENT3 "/// "); - output.push_back(description_line.xml_escape()); - output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + output.append(INDENT3 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - } - output.push_back(INDENT3 "/// </summary>\n"); + output.append(INDENT3 "/// </summary>\n"); + } } - output.push_back(INDENT3); - output.push_back(iconstant.proxy_name); - output.push_back(" = "); - output.push_back(itos(iconstant.value)); - output.push_back(E != ienum.constants.back() ? ",\n" : "\n"); + output.append(INDENT3); + output.append(iconstant.proxy_name); + output.append(" = "); + output.append(itos(iconstant.value)); + output.append(F != ienum.constants.back() ? ",\n" : "\n"); } - output.push_back(INDENT2 CLOSE_BLOCK); + output.append(INDENT2 CLOSE_BLOCK); } // Add properties @@ -848,50 +1281,59 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } } + // TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C# + if (itype.is_singleton) { // Add the type name and the singleton pointer as static fields - output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.push_back(itype.name); - output.push_back("\";\n"); - - output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); - output.push_back("." ICALL_PREFIX); - output.push_back(itype.name); - output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); + output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n"); + output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3 + "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5 + "singleton = Engine.GetSingleton(typeof("); + output.append(itype.proxy_name); + output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); + + output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); + + output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); + output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); + output.append("." ICALL_PREFIX); + output.append(itype.name); + output.append(SINGLETON_ICALL_SUFFIX "();\n"); } else if (is_derived_type) { // Add member fields - output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.push_back(itype.name); - output.push_back("\";\n"); + output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); // Add default constructor if (itype.is_instantiable) { - output.push_back(MEMBER_BEGIN "public "); - output.push_back(itype.proxy_name); - output.push_back("() : this("); - output.push_back(itype.memory_own ? "true" : "false"); + output.append(MEMBER_BEGIN "public "); + output.append(itype.proxy_name); + output.append("() : this("); + output.append(itype.memory_own ? "true" : "false"); // The default constructor may also be called by the engine when instancing existing native objects // The engine will initialize the pointer field of the managed side before calling the constructor // This is why we only allocate a new native object from the constructor if the pointer field is not set - output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); - output.push_back("." + ctor_method); - output.push_back("(this);\n" CLOSE_BLOCK_L2); + output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); + output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); + output.append("." + ctor_method); + output.append("(this);\n" CLOSE_BLOCK_L2); } else { // Hide the constructor - output.push_back(MEMBER_BEGIN "internal "); - output.push_back(itype.proxy_name); - output.push_back("() {}\n"); + output.append(MEMBER_BEGIN "internal "); + output.append(itype.proxy_name); + output.append("() {}\n"); } // Add.. em.. trick constructor. Sort of. - output.push_back(MEMBER_BEGIN "internal "); - output.push_back(itype.proxy_name); - output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); + output.append(MEMBER_BEGIN "internal "); + output.append(itype.proxy_name); + output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); } int method_bind_count = 0; @@ -918,15 +1360,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str custom_icalls.push_back(ctor_icall); } - output.push_back(INDENT1 CLOSE_BLOCK /* class */ + output.append(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); - output.push_back("\n#pragma warning restore CS1591\n"); + output.append("\n" + "#pragma warning restore CS1591\n" + "#pragma warning restore CS1573\n"); return _save_file(p_output_file, output); } -Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output) { +Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output) { const MethodInterface *setter = p_itype.find_method_by_name(p_iprop.setter); @@ -971,86 +1415,96 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte const TypeInterface *prop_itype = _get_type_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found - String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname)); - - // Prevent property and enclosing type from sharing the same name - if (prop_proxy_name == p_itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" + - p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`"); - } - - prop_proxy_name += "_"; - } - if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = p_iprop.prop_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); - p_output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - } - p_output.push_back(INDENT2 "/// </summary>"); + p_output.append(INDENT2 "/// </summary>"); + } } - p_output.push_back(MEMBER_BEGIN "public "); + p_output.append(MEMBER_BEGIN "public "); if (p_itype.is_singleton) - p_output.push_back("static "); + p_output.append("static "); - p_output.push_back(prop_itype->cs_type); - p_output.push_back(" "); - p_output.push_back(prop_proxy_name.replace("/", "__")); - p_output.push_back("\n" INDENT2 OPEN_BLOCK); + p_output.append(prop_itype->cs_type); + p_output.append(" "); + p_output.append(p_iprop.proxy_name); + p_output.append("\n" INDENT2 OPEN_BLOCK); if (getter) { - p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3); - p_output.push_back("return "); - p_output.push_back(getter->proxy_name + "("); + p_output.append(INDENT3 "get\n" + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning disable CS0618 // Disable warning about obsolete method\n" + + OPEN_BLOCK_L3); + + p_output.append("return "); + p_output.append(getter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = getter->arguments.front()->get(); if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); - p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); + p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); } else { - p_output.push_back(itos(p_iprop.index)); + p_output.append(itos(p_iprop.index)); } } - p_output.push_back(");\n" CLOSE_BLOCK_L3); + p_output.append(");\n" + + CLOSE_BLOCK_L3 + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning restore CS0618\n"); } if (setter) { - p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3); - p_output.push_back(setter->proxy_name + "("); + p_output.append(INDENT3 "set\n" + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning disable CS0618 // Disable warning about obsolete method\n" + + OPEN_BLOCK_L3); + + p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = setter->arguments.front()->get(); if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); - p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); + p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); } else { - p_output.push_back(itos(p_iprop.index) + ", "); + p_output.append(itos(p_iprop.index) + ", "); } } - p_output.push_back("value);\n" CLOSE_BLOCK_L3); + p_output.append("value);\n" + + CLOSE_BLOCK_L3 + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning restore CS0618\n"); } - p_output.push_back(CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); return OK; } -Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { +Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) { const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); @@ -1062,7 +1516,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String icall_params = method_bind_field + ", "; icall_params += sformat(p_itype.cs_in, "this"); - List<String> default_args_doc; + StringBuilder default_args_doc; // Retrieve information from the arguments for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { @@ -1128,7 +1582,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in); - default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n"); + // Apparently the name attribute must not include the @ + String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name; + + default_args_doc.append(INDENT2 "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>\n"); } else { icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } @@ -1137,61 +1594,67 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Generate method { if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.push_back(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); - p_output.push_back(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); - p_output.push_back(p_imethod.name); - p_output.push_back("\");\n"); + p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); + p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.append(p_imethod.name); + p_output.append("\");\n"); } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - Vector<String> description_lines = p_imethod.method_doc->description.split("\n"); + if (summary_lines.size() || default_args_doc.get_string_length()) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); - p_output.push_back("\n"); + for (int i = 0; i < summary_lines.size(); i++) { + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - } - for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) { - p_output.push_back(E->get().xml_escape()); + p_output.append(default_args_doc.as_string()); + p_output.append(INDENT2 "/// </summary>"); } - - p_output.push_back(INDENT2 "/// </summary>"); } if (!p_imethod.is_internal) { - p_output.push_back(MEMBER_BEGIN "[GodotMethod(\""); - p_output.push_back(p_imethod.name); - p_output.push_back("\")]"); + p_output.append(MEMBER_BEGIN "[GodotMethod(\""); + p_output.append(p_imethod.name); + p_output.append("\")]"); } - p_output.push_back(MEMBER_BEGIN); - p_output.push_back(p_imethod.is_internal ? "internal " : "public "); + if (p_imethod.is_deprecated) { + if (p_imethod.deprecation_message.empty()) + WARN_PRINTS("An empty deprecation message is discouraged. Method: " + p_imethod.proxy_name); + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_imethod.deprecation_message); + p_output.append("\")]"); + } + + p_output.append(MEMBER_BEGIN); + p_output.append(p_imethod.is_internal ? "internal " : "public "); if (p_itype.is_singleton) { - p_output.push_back("static "); + p_output.append("static "); } else if (p_imethod.is_virtual) { - p_output.push_back("virtual "); + p_output.append("virtual "); } - p_output.push_back(return_type->cs_type + " "); - p_output.push_back(p_imethod.proxy_name + "("); - p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2); + p_output.append(return_type->cs_type + " "); + p_output.append(p_imethod.proxy_name + "("); + p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2); if (p_imethod.is_virtual) { // Godot virtual method must be overridden, therefore we return a default value by default. if (return_type->cname == name_cache.type_void) { - p_output.push_back("return;\n" CLOSE_BLOCK_L2); + p_output.append("return;\n" CLOSE_BLOCK_L2); } else { - p_output.push_back("return default("); - p_output.push_back(return_type->cs_type); - p_output.push_back(");\n" CLOSE_BLOCK_L2); + p_output.append("return default("); + p_output.append(return_type->cs_type); + p_output.append(");\n" CLOSE_BLOCK_L2); } return OK; // Won't increment method bind count @@ -1200,16 +1663,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf if (p_imethod.requires_object_call) { // Fallback to Godot's object.Call(string, params) - p_output.push_back(CS_METHOD_CALL "(\""); - p_output.push_back(p_imethod.name); - p_output.push_back("\""); + p_output.append(CS_METHOD_CALL "(\""); + p_output.append(p_imethod.name); + p_output.append("\""); for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { - p_output.push_back(", "); - p_output.push_back(F->get().name); + p_output.append(", "); + p_output.append(F->get().name); } - p_output.push_back(");\n" CLOSE_BLOCK_L2); + p_output.append(");\n" CLOSE_BLOCK_L2); return OK; // Won't increment method bind count } @@ -1223,37 +1686,36 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf im_call += "." + im_icall->name + "(" + icall_params + ")"; if (p_imethod.arguments.size()) - p_output.push_back(cs_in_statements); + p_output.append(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.push_back(im_call + ";\n"); + p_output.append(im_call + ";\n"); } else if (return_type->cs_out.empty()) { - p_output.push_back("return " + im_call + ";\n"); + p_output.append("return " + im_call + ";\n"); } else { - p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); - p_output.push_back("\n"); + p_output.append(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.append("\n"); } - p_output.push_back(CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); } p_method_bind_count++; + return OK; } Error BindingsGenerator::generate_glue(const String &p_output_dir) { - verbose_output = true; - bool dir_exists = DirAccess::exists(p_output_dir); ERR_EXPLAIN("The output directory does not exist."); ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH); - List<String> output; + StringBuilder output; - output.push_back("/* THIS FILE IS GENERATED DO NOT EDIT */\n"); - output.push_back("#include \"" GLUE_HEADER_FILE "\"\n"); - output.push_back("\n#ifdef MONO_GLUE_ENABLED\n"); + output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n"); + output.append("#include \"" GLUE_HEADER_FILE "\"\n"); + output.append("\n#ifdef MONO_GLUE_ENABLED\n"); generated_icall_funcs.clear(); @@ -1273,7 +1735,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8()); + OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data()); String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types @@ -1293,11 +1755,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (!find_icall_by_name(singleton_icall.name, custom_icalls)) custom_icalls.push_back(singleton_icall); - output.push_back("Object* "); - output.push_back(singleton_icall_name); - output.push_back("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\""); - output.push_back(itype.proxy_name); - output.push_back("\");\n" CLOSE_BLOCK "\n"); + output.append("Object* "); + output.append(singleton_icall_name); + output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\""); + output.append(itype.proxy_name); + output.append("\");\n" CLOSE_BLOCK "\n"); } if (is_derived_type && itype.is_instantiable) { @@ -1306,43 +1768,43 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (!find_icall_by_name(ctor_icall.name, custom_icalls)) custom_icalls.push_back(ctor_icall); - output.push_back("Object* "); - output.push_back(ctor_method); - output.push_back("(MonoObject* obj) " OPEN_BLOCK - "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); - output.push_back(itype.name); - output.push_back("\");\n" - "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" - "\treturn instance;\n" CLOSE_BLOCK "\n"); + output.append("Object* "); + output.append(ctor_method); + output.append("(MonoObject* obj) " OPEN_BLOCK + "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); + output.append(itype.name); + output.append("\");\n" + "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" + "\treturn instance;\n" CLOSE_BLOCK "\n"); } } - output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); + output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); - output.push_back("uint64_t get_core_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); + output.append("uint64_t get_core_api_hash() { return "); + output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); - output.push_back("#ifdef TOOLS_ENABLED\n" - "uint64_t get_editor_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n"); - output.push_back("#endif // TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n" + "uint64_t get_editor_api_hash() { return "); + output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n"); + output.append("#endif // TOOLS_ENABLED\n"); - output.push_back("uint32_t get_bindings_version() { return "); - output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + output.append("uint32_t get_bindings_version() { return "); + output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); - output.push_back("\nvoid register_generated_icalls() " OPEN_BLOCK); - output.push_back("\tgodot_register_glue_header_icalls();\n"); + output.append("\nvoid register_generated_icalls() " OPEN_BLOCK); + output.append("\tgodot_register_glue_header_icalls();\n"); -#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ - { \ - output.push_back("\tmono_add_internal_call("); \ - output.push_back("\"" BINDINGS_NAMESPACE "."); \ - output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ - output.push_back("::"); \ - output.push_back(m_icall.name); \ - output.push_back("\", (void*)"); \ - output.push_back(m_icall.name); \ - output.push_back(");\n"); \ +#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ + { \ + output.append("\tmono_add_internal_call("); \ + output.append("\"" BINDINGS_NAMESPACE "."); \ + output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ + output.append("::"); \ + output.append(m_icall.name); \ + output.append("\", (void*)"); \ + output.append(m_icall.name); \ + output.append(");\n"); \ } bool tools_sequence = false; @@ -1351,11 +1813,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { if (!E->get().editor_only) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } } else { if (E->get().editor_only) { - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); tools_sequence = true; } } @@ -1365,23 +1827,23 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL_REGISTRATION(E->get()); - output.push_back("#endif // TOOLS_ENABLED\n"); + output.append("#endif // TOOLS_ENABLED\n"); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) { if (tools_sequence) { if (!E->get().editor_only) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } } else { if (E->get().editor_only) { - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); tools_sequence = true; } } @@ -1391,16 +1853,16 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } #undef ADD_INTERNAL_CALL_REGISTRATION - output.push_back(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n"); + output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n"); - output.push_back("\n#endif // MONO_GLUE_ENABLED\n"); + output.append("\n#endif // MONO_GLUE_ENABLED\n"); - Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output); + Error save_err = _save_file(path::join(p_output_dir, "mono_glue.gen.cpp"), output); if (save_err != OK) return save_err; @@ -1413,23 +1875,20 @@ uint32_t BindingsGenerator::get_version() { return BINDINGS_GENERATOR_VERSION; } -Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { +Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); ERR_EXPLAIN("Cannot open file: " + p_path); ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); - for (const List<String>::Element *E = p_content.front(); E; E = E->next()) { - file->store_string(E->get()); - } - + file->store_string(p_content.as_string()); file->close(); return OK; } -Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) { +Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) { if (p_imethod.is_virtual) return OK; // Ignore @@ -1485,15 +1944,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte generated_icall_funcs.push_back(im_icall); if (im_icall->editor_only) - p_output.push_back("#ifdef TOOLS_ENABLED\n"); + p_output.append("#ifdef TOOLS_ENABLED\n"); // Generate icall function - p_output.push_back(ret_void ? "void " : return_type->c_type_out + " "); - p_output.push_back(icall_method); - p_output.push_back("("); - p_output.push_back(c_func_sig); - p_output.push_back(") " OPEN_BLOCK); + p_output.append(ret_void ? "void " : return_type->c_type_out + " "); + p_output.append(icall_method); + p_output.append("("); + p_output.append(c_func_sig); + p_output.append(") " OPEN_BLOCK); String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()"); @@ -1501,6 +1960,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte String ptrcall_return_type; String initialization; + if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) { + // VarArg methods always return Variant, but there are some cases in which MethodInfo provides + // a specific return type. We trust this information is valid. We need a temporary local to keep + // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr, + // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT. + // Alternatively, we could just return Variant, but that would result in a worse API. + p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n"); + } + if (return_type->is_object_type) { ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type; initialization = return_type->is_reference ? "" : " = NULL"; @@ -1508,79 +1976,82 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte ptrcall_return_type = return_type->c_type; } - p_output.push_back("\t" + ptrcall_return_type); - p_output.push_back(" " C_LOCAL_RET); - p_output.push_back(initialization + ";\n"); - p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); - p_output.push_back(fail_ret); - p_output.push_back(");\n"); + p_output.append("\t" + ptrcall_return_type); + p_output.append(" " C_LOCAL_RET); + p_output.append(initialization + ";\n"); + p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); + p_output.append(fail_ret); + p_output.append(");\n"); } else { - p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); + p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); } if (p_imethod.arguments.size()) { if (p_imethod.is_vararg) { - String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V"; String vararg_arg = "arg" + argc_str; String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg - p_output.push_back("\tVector<Variant> varargs;\n" - "\tint vararg_length = mono_array_length("); - p_output.push_back(vararg_arg); - p_output.push_back(");\n\tint total_length = "); - p_output.push_back(real_argc_str); - p_output.push_back(" + vararg_length;\n\t"); - p_output.push_back(err_fail_macro); - p_output.push_back("(varargs.resize(vararg_length) != OK"); - p_output.push_back(fail_ret); - p_output.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t"); - p_output.push_back(err_fail_macro); - p_output.push_back("(call_args.resize(total_length) != OK"); - p_output.push_back(fail_ret); - p_output.push_back(");\n"); - p_output.push_back(c_in_statements); - p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK - "\t\tMonoObject* elem = mono_array_get("); - p_output.push_back(vararg_arg); - p_output.push_back(", MonoObject*, i);\n" - "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" - "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); - p_output.push_back(real_argc_str); - p_output.push_back(" + i, &varargs.write[i]);\n\t" CLOSE_BLOCK); + p_output.append("\tint vararg_length = mono_array_length("); + p_output.append(vararg_arg); + p_output.append(");\n\tint total_length = "); + p_output.append(real_argc_str); + p_output.append(" + vararg_length;\n" + "\tArgumentsVector<Variant> varargs(vararg_length);\n" + "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n"); + p_output.append(c_in_statements); + p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK + "\t\tMonoObject* elem = mono_array_get("); + p_output.append(vararg_arg); + p_output.append(", MonoObject*, i);\n" + "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" + "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); + p_output.append(real_argc_str); + p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK); } else { - p_output.push_back(c_in_statements); - p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); - p_output.push_back(argc_str + "] = { "); - p_output.push_back(c_args_var_content + " };\n"); + p_output.append(c_in_statements); + p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); + p_output.append(argc_str + "] = { "); + p_output.append(c_args_var_content + " };\n"); } } if (p_imethod.is_vararg) { - p_output.push_back("\tVariant::CallError vcall_error;\n\t"); + p_output.append("\tVariant::CallError vcall_error;\n\t"); + + if (!ret_void) { + // See the comment on the C_LOCAL_VARARG_RET declaration + if (return_type->cname != name_cache.type_Variant) { + p_output.append(C_LOCAL_VARARG_RET " = "); + } else { + p_output.append(C_LOCAL_RET " = "); + } + } - if (!ret_void) - p_output.push_back(C_LOCAL_RET " = "); + p_output.append(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); + p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); + p_output.append(", total_length, vcall_error);\n"); - p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); - p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); - p_output.push_back(", total_length, vcall_error);\n"); + // See the comment on the C_LOCAL_VARARG_RET declaration + if (return_type->cname != name_cache.type_Variant) { + p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n"); + } } else { - p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); - p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); - p_output.push_back(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n"); + p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); + p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); + p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n"); } if (!ret_void) { if (return_type->c_out.empty()) - p_output.push_back("\treturn " C_LOCAL_RET ";\n"); + p_output.append("\treturn " C_LOCAL_RET ";\n"); else - p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); + p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); } - p_output.push_back(CLOSE_BLOCK "\n"); + p_output.append(CLOSE_BLOCK "\n"); if (im_icall->editor_only) - p_output.push_back("#endif // TOOLS_ENABLED\n"); + p_output.append("#endif // TOOLS_ENABLED\n"); } return OK; @@ -1633,6 +2104,58 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } +StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { + + switch (p_meta) { + case GodotTypeInfo::METADATA_INT_IS_INT8: + return "sbyte"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT16: + return "short"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT32: + return "int"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT64: + return "long"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT8: + return "byte"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT16: + return "ushort"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT32: + return "uint"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT64: + return "ulong"; + break; + default: + // Assume INT32 + return "int"; + } +} + +StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { + + switch (p_meta) { + case GodotTypeInfo::METADATA_REAL_IS_FLOAT: + return "float"; + break; + case GodotTypeInfo::METADATA_REAL_IS_DOUBLE: + return "double"; + break; + default: + // Assume real_t (float or double depending of REAL_T_IS_DOUBLE) +#ifdef REAL_T_IS_DOUBLE + return "double"; +#else + return "float"; +#endif + } +} + void BindingsGenerator::_populate_object_type_interfaces() { obj_types.clear(); @@ -1652,15 +2175,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } if (!ClassDB::is_class_exposed(type_cname)) { - if (verbose_output) - WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not exposed"); + _log("Ignoring type `%s` because it's not exposed\n", String(type_cname).utf8().get_data()); class_list.pop_front(); continue; } if (!ClassDB::is_class_enabled(type_cname)) { - if (verbose_output) - WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled"); + _log("Ignoring type `%s` because it's not enabled\n", String(type_cname).utf8().get_data()); class_list.pop_front(); continue; } @@ -1671,7 +2192,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.base_name = ClassDB::get_parent_class(type_cname); itype.is_singleton = Engine::get_singleton()->has_singleton(itype.proxy_name); - itype.is_instantiable = ClassDB::can_instance(type_cname) && !itype.is_singleton; + itype.is_instantiable = class_info->creation_func && !itype.is_singleton; itype.is_reference = ClassDB::is_parent_class(type_cname, name_cache.type_Reference); itype.memory_own = itype.is_reference; @@ -1688,10 +2209,12 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.im_type_in = "IntPtr"; itype.im_type_out = itype.proxy_name; + // Populate properties + List<PropertyInfo> property_list; ClassDB::get_property_list(type_cname, &property_list, true); - // Populate properties + Map<StringName, StringName> accessor_methods; for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { const PropertyInfo &property = E->get(); @@ -1701,24 +2224,30 @@ void BindingsGenerator::_populate_object_type_interfaces() { PropertyInterface iprop; iprop.cname = property.name; - iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname); iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname); + if (iprop.setter != StringName()) + accessor_methods[iprop.setter] = iprop.cname; + if (iprop.getter != StringName()) + accessor_methods[iprop.getter] = iprop.cname; + bool valid = false; iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid); ERR_FAIL_COND(!valid); - // Prevent property and enclosing type from sharing the same name + iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); + + // Prevent the property and its enclosing type from sharing the same name if (iprop.proxy_name == itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of property `" + iprop.proxy_name + "` is ambiguous with the name of its class `" + - itype.proxy_name + "`. Renaming property to `" + iprop.proxy_name + "_`"); - } + _log("Name of property `%s` is ambiguous with the name of its enclosing class `%s`. Renaming property to `%s_`\n", + iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data()); iprop.proxy_name += "_"; } + iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash... + iprop.prop_doc = NULL; for (int i = 0; i < itype.class_doc->properties.size(); i++) { @@ -1750,9 +2279,14 @@ void BindingsGenerator::_populate_object_type_interfaces() { if (method_info.name.empty()) continue; + String cname = method_info.name; + + if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname)) + continue; + MethodInterface imethod; imethod.name = method_info.name; - imethod.cname = imethod.name; + imethod.cname = cname; if (method_info.flags & METHOD_FLAG_VIRTUAL) imethod.is_virtual = true; @@ -1779,20 +2313,26 @@ void BindingsGenerator::_populate_object_type_interfaces() { // which could actually will return something different. // Let's put this to notify us if that ever happens. if (itype.cname != name_cache.type_Object || imethod.name != "free") { - if (verbose_output) { - WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n" - "We only expected Object.free, but found " + - itype.name + "." + imethod.name); - } + ERR_PRINTS("Notification: New unexpected virtual non-overridable method found.\n" + "We only expected Object.free, but found " + + itype.name + "." + imethod.name); } } else { - ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); + ERR_EXPLAIN("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); + ERR_FAIL(); } } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { imethod.return_type.cname = return_info.class_name; imethod.return_type.is_enum = true; } else if (return_info.class_name != StringName()) { imethod.return_type.cname = return_info.class_name; + if (!imethod.is_virtual && ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference) && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE) { + /* clang-format off */ + ERR_PRINTS("Return type is reference but hint is not " _STR(PROPERTY_HINT_RESOURCE_TYPE) "." + " Are you returning a reference type by pointer? Method: " + itype.name + "." + imethod.name); + /* clang-format on */ + ERR_FAIL(); + } } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { @@ -1800,7 +2340,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else if (return_info.type == Variant::NIL) { imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type.cname = Variant::get_type_name(return_info.type); + if (return_info.type == Variant::INT) { + imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); + } else if (return_info.type == Variant::REAL) { + imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); + } else { + imethod.return_type.cname = Variant::get_type_name(return_info.type); + } } for (int i = 0; i < argc; i++) { @@ -1819,7 +2365,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - iarg.type.cname = Variant::get_type_name(arginfo.type); + if (arginfo.type == Variant::INT) { + iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); + } else if (arginfo.type == Variant::REAL) { + iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); + } else { + iarg.type.cname = Variant::get_type_name(arginfo.type); + } } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); @@ -1840,16 +2392,24 @@ void BindingsGenerator::_populate_object_type_interfaces() { imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name)); - // Prevent naming the property and its enclosing type from sharing the same name + // Prevent the method and its enclosing type from sharing the same name if (imethod.proxy_name == itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of method `" + imethod.proxy_name + "` is ambiguous with the name of its class `" + - itype.proxy_name + "`. Renaming method to `" + imethod.proxy_name + "_`"); - } + _log("Name of method `%s` is ambiguous with the name of its enclosing class `%s`. Renaming method to `%s_`\n", + imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data()); imethod.proxy_name += "_"; } + Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname); + if (accessor) { + const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value()); + + // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also + // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched. + imethod.is_deprecated = true; + imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead."; + } + if (itype.class_doc) { for (int i = 0; i < itype.class_doc->methods.size(); i++) { if (itype.class_doc->methods[i].name == imethod.name) { @@ -1860,8 +2420,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { } if (!imethod.is_virtual && imethod.name[0] == '_') { - for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) { - const PropertyInterface &iprop = E->get(); + for (const List<PropertyInterface>::Element *F = itype.properties.front(); F; F = F->next()) { + const PropertyInterface &iprop = F->get(); if (iprop.setter == imethod.name || iprop.getter == imethod.name) { imethod.is_internal = true; @@ -1876,8 +2436,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { // Populate enums and constants - List<String> constant_list; - ClassDB::get_integer_constant_list(type_cname, &constant_list, true); + List<String> constants; + ClassDB::get_integer_constant_list(type_cname, &constants, true); const HashMap<StringName, List<StringName> > &enum_map = class_info->enum_map; const StringName *k = NULL; @@ -1892,13 +2452,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { enum_proxy_cname = StringName(enum_proxy_name); } EnumInterface ienum(enum_proxy_cname); - const List<StringName> &constants = enum_map.get(*k); - for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) { + const List<StringName> &enum_constants = enum_map.get(*k); + for (const List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) { const StringName &constant_cname = E->get(); String constant_name = constant_cname.operator String(); int *value = class_info->constant_map.getptr(constant_cname); ERR_FAIL_NULL(value); - constant_list.erase(constant_name); + constants.erase(constant_name); ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); @@ -1930,7 +2490,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { enum_types.insert(enum_itype.cname, enum_itype); } - for (const List<String>::Element *E = constant_list.front(); E; E = E->next()) { + for (const List<String>::Element *E = constants.front(); E; E = E->next()) { const String &constant_name = E->get(); int *value = class_info->constant_map.getptr(StringName(E->get())); ERR_FAIL_NULL(value); @@ -1962,13 +2522,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg switch (p_val.get_type()) { case Variant::NIL: - if (ClassDB::class_exists(r_iarg.type.cname)) { - // Object type - r_iarg.default_argument = "null"; - } else { - // Variant - r_iarg.default_argument = "null"; - } + // Either Object type or Variant + r_iarg.default_argument = "null"; break; // Atomic types case Variant::BOOL: @@ -2011,6 +2566,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = "null"; break; } + FALLTHROUGH; case Variant::DICTIONARY: case Variant::_RID: r_iarg.default_argument = "new %s()"; @@ -2033,7 +2589,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; - default: {} + default: { + } } if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") @@ -2075,7 +2632,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // bool itype = TypeInterface::create_value_type(String("bool")); - { // MonoBoolean <---> bool itype.c_in = "\t%0 %1_in = (%0)%1;\n"; @@ -2089,45 +2645,73 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = itype.name; builtin_types.insert(itype.cname, itype); - // int - // C interface is the same as that of enums. Remember to apply any - // changes done here to TypeInterface::postsetup_enum_type as well - itype = TypeInterface::create_value_type(String("int")); - itype.c_arg_in = "&%s_in"; + // Integer types { - // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "int64_t"; + // C interface for 'uint32_t' is the same as that of enums. Remember to apply + // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well. +#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \ + { \ + itype = TypeInterface::create_value_type(String(m_name)); \ + { \ + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \ + itype.c_out = "\treturn (%0)%1;\n"; \ + itype.c_type = #m_c_type; \ + itype.c_arg_in = "&%s_in"; \ + } \ + itype.c_type_in = #m_c_type_in_out; \ + itype.c_type_out = itype.c_type_in; \ + itype.im_type_in = itype.name; \ + itype.im_type_out = itype.name; \ + builtin_types.insert(itype.cname, itype); \ } - itype.c_type_in = "int32_t"; - itype.c_type_out = itype.c_type_in; - itype.im_type_in = itype.name; - itype.im_type_out = itype.name; - builtin_types.insert(itype.cname, itype); - // real_t - itype = TypeInterface(); - itype.name = "float"; // The name is always "float" in Variant, even with REAL_T_IS_DOUBLE. - itype.cname = itype.name; -#ifdef REAL_T_IS_DOUBLE - itype.proxy_name = "double"; -#else - itype.proxy_name = "float"; -#endif + // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type' + + INSERT_INT_TYPE("sbyte", int8_t, int64_t); + INSERT_INT_TYPE("short", int16_t, int64_t); + INSERT_INT_TYPE("int", int32_t, int64_t); + INSERT_INT_TYPE("long", int64_t, int64_t); + INSERT_INT_TYPE("byte", uint8_t, int64_t); + INSERT_INT_TYPE("ushort", uint16_t, int64_t); + INSERT_INT_TYPE("uint", uint32_t, int64_t); + INSERT_INT_TYPE("ulong", uint64_t, int64_t); + } + + // Floating point types { - // The expected type for parameters and return value in ptrcall is 'double'. - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; + // float + itype = TypeInterface(); + itype.name = "float"; + itype.cname = itype.name; + itype.proxy_name = "float"; + { + // The expected type for 'float' in ptrcall is 'double' + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "double"; + itype.c_type_in = "float"; + itype.c_type_out = "float"; + itype.c_arg_in = "&%s_in"; + } + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.cname, itype); + + // double + itype = TypeInterface(); + itype.name = "double"; + itype.cname = itype.name; + itype.proxy_name = "double"; itype.c_type = "double"; - itype.c_type_in = "real_t"; - itype.c_type_out = "real_t"; - itype.c_arg_in = "&%s_in"; + itype.c_type_in = "double"; + itype.c_type_out = "double"; + itype.c_arg_in = "&%s"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.cname, itype); } - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; - builtin_types.insert(itype.cname, itype); // String itype = TypeInterface(); @@ -2323,9 +2907,9 @@ void BindingsGenerator::_populate_global_constants() { if (enum_name != StringName()) { EnumInterface ienum(enum_name); - List<EnumInterface>::Element *match = global_enums.find(ienum); - if (match) { - match->get().constants.push_back(iconstant); + List<EnumInterface>::Element *enum_match = global_enums.find(ienum); + if (enum_match) { + enum_match->get().constants.push_back(iconstant); } else { ienum.constants.push_back(iconstant); global_enums.push_back(ienum); @@ -2377,12 +2961,32 @@ void BindingsGenerator::_populate_global_constants() { } } -void BindingsGenerator::initialize() { +void BindingsGenerator::_initialize_blacklisted_methods() { + + blacklisted_methods["Object"].push_back("to_string"); // there is already ToString + blacklisted_methods["Object"].push_back("_to_string"); // override ToString instead + blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it) +} + +void BindingsGenerator::_log(const char *p_format, ...) { + + if (log_print_enabled) { + va_list list; + + va_start(list, p_format); + OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data()); + va_end(list); + } +} + +void BindingsGenerator::_initialize() { EditorHelp::generate_doc(); enum_types.clear(); + _initialize_blacklisted_methods(); + _populate_object_type_interfaces(); _populate_builtin_type_interfaces(); @@ -2400,41 +3004,49 @@ void BindingsGenerator::initialize() { void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { const int NUM_OPTIONS = 2; - int options_left = NUM_OPTIONS; + String generate_all_glue_option = "--generate-mono-glue"; + String generate_cs_glue_option = "--generate-mono-cs-glue"; + String generate_cpp_glue_option = "--generate-mono-cpp-glue"; - String mono_glue_option = "--generate-mono-glue"; - String cs_api_option = "--generate-cs-api"; + String glue_dir_path; + String cs_dir_path; + String cpp_dir_path; - verbose_output = true; + int options_left = NUM_OPTIONS; const List<String>::Element *elem = p_cmdline_args.front(); while (elem && options_left) { - - if (elem->get() == mono_glue_option) { - + if (elem->get() == generate_all_glue_option) { const List<String>::Element *path_elem = elem->next(); if (path_elem) { - if (get_singleton()->generate_glue(path_elem->get()) != OK) - ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue"); + glue_dir_path = path_elem->get(); elem = elem->next(); } else { - ERR_PRINTS(mono_glue_option + ": No output directory specified"); + ERR_PRINTS(generate_all_glue_option + ": No output directory specified (expected path to {GODOT_ROOT}/modules/mono/glue)"); } --options_left; + } else if (elem->get() == generate_cs_glue_option) { + const List<String>::Element *path_elem = elem->next(); - } else if (elem->get() == cs_api_option) { + if (path_elem) { + cs_dir_path = path_elem->get(); + elem = elem->next(); + } else { + ERR_PRINTS(generate_cs_glue_option + ": No output directory specified"); + } + --options_left; + } else if (elem->get() == generate_cpp_glue_option) { const List<String>::Element *path_elem = elem->next(); if (path_elem) { - if (get_singleton()->generate_cs_api(path_elem->get()) != OK) - ERR_PRINTS(cs_api_option + ": Failed to generate the C# API"); + cpp_dir_path = path_elem->get(); elem = elem->next(); } else { - ERR_PRINTS(cs_api_option + ": No output directory specified"); + ERR_PRINTS(generate_cpp_glue_option + ": No output directory specified"); } --options_left; @@ -2443,10 +3055,31 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) elem = elem->next(); } - verbose_output = false; + if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) { + BindingsGenerator bindings_generator; + bindings_generator.set_log_print_enabled(true); - if (options_left != NUM_OPTIONS) + if (glue_dir_path.length()) { + if (bindings_generator.generate_glue(glue_dir_path) != OK) + ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C++ glue"); + + if (bindings_generator.generate_cs_api(glue_dir_path.plus_file("Managed/Generated")) != OK) + ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C# API"); + } + + if (cs_dir_path.length()) { + if (bindings_generator.generate_cs_api(cs_dir_path) != OK) + ERR_PRINTS(generate_cs_glue_option + ": Failed to generate the C# API"); + } + + if (cpp_dir_path.length()) { + if (bindings_generator.generate_glue(cpp_dir_path) != OK) + ERR_PRINTS(generate_cpp_glue_option + ": Failed to generate the C++ glue"); + } + + // Exit once done ::exit(0); + } } #endif diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 91c474c4f0..8be51a6c55 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #define BINDINGS_GENERATOR_H #include "core/class_db.h" -#include "dotnet_solution.h" +#include "core/string_builder.h" #include "editor/doc/doc_data.h" #include "editor/editor_help.h" @@ -87,8 +87,13 @@ class BindingsGenerator { StringName cname; bool is_enum; - TypeReference() { - is_enum = false; + TypeReference() : + is_enum(false) { + } + + TypeReference(const StringName &p_cname) : + cname(p_cname), + is_enum(false) { } }; @@ -153,17 +158,20 @@ class BindingsGenerator { const DocData::MethodDoc *method_doc; + bool is_deprecated; + String deprecation_message; + void add_argument(const ArgumentInterface &argument) { arguments.push_back(argument); } MethodInterface() { - return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void; is_vararg = false; is_virtual = false; requires_object_call = false; is_internal = false; method_doc = NULL; + is_deprecated = false; } }; @@ -321,6 +329,15 @@ class BindingsGenerator { return NULL; } + const PropertyInterface *find_property_by_name(const StringName &p_cname) const { + for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) { + if (E->get().cname == p_cname) + return &E->get(); + } + + return NULL; + } + const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const { for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) { if (E->get().proxy_name == p_proxy_name) @@ -385,8 +402,8 @@ class BindingsGenerator { } static void postsetup_enum_type(TypeInterface &r_enum_itype) { - // C interface is the same as that of 'int'. Remember to apply any - // changes done here to the 'int' type interface as well + // C interface for enums is the same as that of 'uint32_t'. Remember to apply + // any of the changes done here to the 'uint32_t' type interface as well. r_enum_itype.c_arg_in = "&%s_in"; { @@ -454,7 +471,7 @@ class BindingsGenerator { } }; - static bool verbose_output; + bool log_print_enabled; OrderedHashMap<StringName, TypeInterface> obj_types; @@ -473,29 +490,58 @@ class BindingsGenerator { List<InternalCall> core_custom_icalls; List<InternalCall> editor_custom_icalls; + Map<StringName, List<StringName> > blacklisted_methods; + + void _initialize_blacklisted_methods(); + struct NameCache { StringName type_void; - StringName type_int; StringName type_Array; StringName type_Dictionary; StringName type_Variant; StringName type_VarArg; StringName type_Object; StringName type_Reference; + StringName type_String; + StringName type_at_GlobalScope; StringName enum_Error; + StringName type_sbyte; + StringName type_short; + StringName type_int; + StringName type_long; + StringName type_byte; + StringName type_ushort; + StringName type_uint; + StringName type_ulong; + StringName type_float; + StringName type_double; + NameCache() { type_void = StaticCString::create("void"); - type_int = StaticCString::create("int"); type_Array = StaticCString::create("Array"); type_Dictionary = StaticCString::create("Dictionary"); type_Variant = StaticCString::create("Variant"); type_VarArg = StaticCString::create("VarArg"); type_Object = StaticCString::create("Object"); type_Reference = StaticCString::create("Reference"); + type_String = StaticCString::create("String"); + type_at_GlobalScope = StaticCString::create("@GlobalScope"); enum_Error = StaticCString::create("Error"); + + type_sbyte = StaticCString::create("sbyte"); + type_short = StaticCString::create("short"); + type_int = StaticCString::create("int"); + type_long = StaticCString::create("long"); + type_byte = StaticCString::create("byte"); + type_ushort = StaticCString::create("ushort"); + type_uint = StaticCString::create("uint"); + type_ulong = StaticCString::create("ulong"); + type_float = StaticCString::create("float"); + type_double = StaticCString::create("double"); } + private: NameCache(const NameCache &); NameCache &operator=(const NameCache &); }; @@ -511,6 +557,15 @@ class BindingsGenerator { return NULL; } + const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const { + for (const List<ConstantInterface>::Element *E = p_constants.front(); E; E = E->next()) { + if (E->get().name == p_name) + return &E->get(); + } + + return NULL; + } + inline String get_unique_sig(const TypeInterface &p_type) { if (p_type.is_reference) return "Ref"; @@ -522,6 +577,8 @@ class BindingsGenerator { return p_type.name; } + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); + int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); @@ -530,6 +587,9 @@ class BindingsGenerator { const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); + StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); + StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); + void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); void _populate_object_type_interfaces(); @@ -539,42 +599,36 @@ class BindingsGenerator { Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); - Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output); - Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output); + Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output); + Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); - void _generate_global_constants(List<String> &p_output); + void _generate_global_constants(StringBuilder &p_output); - Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output); + Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output); - Error _save_file(const String &p_path, const List<String> &p_content); + Error _save_file(const String &p_path, const StringBuilder &p_content); - BindingsGenerator() {} + void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; - BindingsGenerator(const BindingsGenerator &); - BindingsGenerator &operator=(const BindingsGenerator &); - - friend class CSharpLanguage; - static BindingsGenerator *singleton; + void _initialize(); public: - Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); - Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); - Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true); + Error generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_files); + Error generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items); + Error generate_cs_api(const String &p_output_dir); Error generate_glue(const String &p_output_dir); + _FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; } + _FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } + static uint32_t get_version(); - void initialize(); + static void handle_cmdline_args(const List<String> &p_cmdline_args); - _FORCE_INLINE_ static BindingsGenerator *get_singleton() { - if (!singleton) { - singleton = memnew(BindingsGenerator); - singleton->initialize(); - } - return singleton; + BindingsGenerator() : + log_print_enabled(true) { + _initialize(); } - - static void handle_cmdline_args(const List<String> &p_cmdline_args); }; #endif diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 416108603b..d88b08c646 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,66 +44,54 @@ namespace CSharpProject { -String generate_core_api_project(const String &p_dir, const Vector<String> &p_files) { +bool generate_api_solution_impl(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items, + const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items, + GDMonoAssembly *p_tools_project_editor_assembly) { - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + GDMonoClass *klass = p_tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ApiSolutionGenerator"); - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); - - Variant dir = p_dir; - Variant compile_items = p_files; - const Variant *args[2] = { &dir, &compile_items }; + Variant solution_dir = p_solution_dir; + Variant core_proj_dir = p_core_proj_dir; + Variant core_compile_items = p_core_compile_items; + Variant editor_proj_dir = p_editor_proj_dir; + Variant editor_compile_items = p_editor_compile_items; + const Variant *args[5] = { &solution_dir, &core_proj_dir, &core_compile_items, &editor_proj_dir, &editor_compile_items }; MonoException *exc = NULL; - MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc); + klass->get_method("GenerateApiSolution", 5)->invoke(NULL, args, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL_V(String()); + ERR_FAIL_V(false); } - return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String(); + return true; } -String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) { - - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) - - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); - - Variant dir = p_dir; - Variant core_proj_path = p_core_proj_path; - Variant compile_items = p_files; - const Variant *args[3] = { &dir, &core_proj_path, &compile_items }; - MonoException *exc = NULL; - MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL_V(String()); - } - - return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String(); -} +bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items, + const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items) { -String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files) { + if (GDMono::get_singleton()->get_tools_project_editor_assembly()) { + return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items, + p_editor_proj_dir, p_editor_compile_items, + GDMono::get_singleton()->get_tools_project_editor_assembly()); + } else { + MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.ApiSolutionGenerationDomain"); + CRASH_COND(temp_domain == NULL); + _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + _GDMONO_SCOPE_DOMAIN_(temp_domain); - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); + GDMonoAssembly *tools_project_editor_assembly = NULL; - Variant dir = p_dir; - Variant name = p_name; - Variant compile_items = p_files; - const Variant *args[3] = { &dir, &name, &compile_items }; - MonoException *exc = NULL; - MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc); + if (!GDMono::get_singleton()->load_assembly("GodotTools.ProjectEditor", &tools_project_editor_assembly)) { + ERR_EXPLAIN("Failed to load assembly: 'GodotTools.ProjectEditor'"); + ERR_FAIL_V(false); + } - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL_V(String()); + return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items, + p_editor_proj_dir, p_editor_compile_items, + tools_project_editor_assembly); } - - return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String(); } void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) { @@ -111,9 +99,9 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str if (!GLOBAL_DEF("mono/project/auto_update_project", true)) return; - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly(); - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); + GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils"); Variant project_path = p_project_path; Variant item_type = p_item_type; @@ -128,117 +116,4 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str } } -Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) { - - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) - - if (FileAccess::exists(p_output_path)) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error rm_err = da->remove(p_output_path); - - ERR_EXPLAIN("Failed to remove old scripts metadata file"); - ERR_FAIL_COND_V(rm_err != OK, rm_err); - } - - GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); - - void *args[2] = { - GDMonoMarshal::mono_string_from_godot(p_project_path), - GDMonoMarshal::mono_string_from_godot("Compile") - }; - - MonoException *exc = NULL; - MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL_V(FAILED); - } - - PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret); - PoolStringArray::Read r = project_files.read(); - - Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata(); - Dictionary new_dict; - - for (int i = 0; i < project_files.size(); i++) { - const String &project_file = ("res://" + r[i]).simplify_path(); - - uint64_t modified_time = FileAccess::get_modified_time(project_file); - - const Variant *old_file_var = old_dict.getptr(project_file); - if (old_file_var) { - Dictionary old_file_dict = old_file_var->operator Dictionary(); - - if (old_file_dict["modified_time"].operator uint64_t() == modified_time) { - // No changes so no need to parse again - new_dict[project_file] = old_file_dict; - continue; - } - } - - ScriptClassParser scp; - Error err = scp.parse_file(project_file); - if (err != OK) { - ERR_PRINTS("Parse error: " + scp.get_error()); - ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file); - ERR_FAIL_V(err); - } - - Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes(); - - bool found = false; - Dictionary class_dict; - - String search_name = project_file.get_file().get_basename(); - - for (int j = 0; j < classes.size(); j++) { - const ScriptClassParser::ClassDecl &class_decl = classes[j]; - - if (class_decl.base.size() == 0) - continue; // Does not inherit nor implement anything, so it can't be a script class - - String class_cmp; - - if (class_decl.nested) { - class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1); - } else { - class_cmp = class_decl.name; - } - - if (class_cmp != search_name) - continue; - - class_dict["namespace"] = class_decl.namespace_; - class_dict["class_name"] = class_decl.name; - class_dict["nested"] = class_decl.nested; - - found = true; - break; - } - - if (found) { - Dictionary file_dict; - file_dict["modified_time"] = modified_time; - file_dict["class"] = class_dict; - new_dict[project_file] = file_dict; - } - } - - if (new_dict.size()) { - String json = JSON::print(new_dict, "", false); - - Error ferr; - FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr); - ERR_EXPLAIN("Cannot open file for writing: " + p_output_path); - ERR_FAIL_COND_V(ferr != OK, ferr); - f->store_string(json); - f->flush(); - f->close(); - memdelete(f); - } - - return OK; -} - } // namespace CSharpProject diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h index aeeeff50f0..b42762cea2 100644 --- a/modules/mono/editor/csharp_project.h +++ b/modules/mono/editor/csharp_project.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,14 +35,11 @@ namespace CSharpProject { -String generate_core_api_project(const String &p_dir, const Vector<String> &p_files = Vector<String>()); -String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files = Vector<String>()); -String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>()); +bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items, + const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items); void add_item(const String &p_project_path, const String &p_item_type, const String &p_include); -Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path); - } // namespace CSharpProject #endif // CSHARP_PROJECT_H diff --git a/modules/mono/editor/dotnet_solution.cpp b/modules/mono/editor/dotnet_solution.cpp deleted file mode 100644 index ab92e2e378..0000000000 --- a/modules/mono/editor/dotnet_solution.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/*************************************************************************/ -/* dotnet_solution.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "dotnet_solution.h" - -#include "core/os/dir_access.h" -#include "core/os/file_access.h" - -#include "../utils/path_utils.h" -#include "../utils/string_utils.h" -#include "csharp_project.h" - -#define SOLUTION_TEMPLATE \ - "Microsoft Visual Studio Solution File, Format Version 12.00\n" \ - "# Visual Studio 2012\n" \ - "%0\n" \ - "Global\n" \ - "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" \ - "%1\n" \ - "\tEndGlobalSection\n" \ - "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" \ - "%2\n" \ - "\tEndGlobalSection\n" \ - "EndGlobal\n" - -#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject" - -#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU" - -#define PROJECT_PLATFORMS_CONFIG \ - "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ - "\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU" - -void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) { - projects[p_name] = p_project_info; -} - -bool DotNetSolution::has_project(const String &p_name) const { - return projects.find(p_name) != NULL; -} - -const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const { - return projects[p_name]; -} - -bool DotNetSolution::remove_project(const String &p_name) { - return projects.erase(p_name); -} - -Error DotNetSolution::save() { - bool dir_exists = DirAccess::exists(path); - ERR_EXPLAIN("The directory does not exist."); - ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND); - - String projs_decl; - String sln_platform_cfg; - String proj_platform_cfg; - - for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) { - const String &name = E->key(); - const ProjectInfo &proj_info = E->value(); - - bool is_front = E == projects.front(); - - if (!is_front) - projs_decl += "\n"; - - projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid); - - for (int i = 0; i < proj_info.configs.size(); i++) { - const String &config = proj_info.configs[i]; - - if (i != 0 || !is_front) { - sln_platform_cfg += "\n"; - proj_platform_cfg += "\n"; - } - - sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config); - proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config); - } - } - - String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg); - - FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE); - ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE); - file->store_string(content); - file->close(); - memdelete(file); - - return OK; -} - -bool DotNetSolution::set_path(const String &p_existing_path) { - if (p_existing_path.is_abs_path()) { - path = p_existing_path; - } else { - String abspath; - if (!rel_path_to_abs(p_existing_path, abspath)) - return false; - path = abspath; - } - - return true; -} - -String DotNetSolution::get_path() { - return path; -} - -DotNetSolution::DotNetSolution(const String &p_name) { - name = p_name; -} diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp new file mode 100644 index 0000000000..0014aaca70 --- /dev/null +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -0,0 +1,427 @@ +/*************************************************************************/ +/* editor_internal_calls.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_internal_calls.h" + +#include "core/os/os.h" +#include "core/version.h" +#include "editor/editor_node.h" +#include "editor/plugins/script_editor_plugin.h" +#include "editor/script_editor_debugger.h" +#include "main/main.h" + +#include "../csharp_script.h" +#include "../glue/cs_glue_version.gen.h" +#include "../godotsharp_dirs.h" +#include "../mono_gd/gd_mono_marshal.h" +#include "../utils/osx_utils.h" +#include "bindings_generator.h" +#include "godotsharp_export.h" +#include "script_class_parser.h" + +MonoString *godot_icall_GodotSharpDirs_ResDataDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResConfigDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResTempDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_MonoUserDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() { +#ifdef TOOLS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir()); +#else + return NULL; +#endif +} + +MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() { + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir()); +} + +MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() { +#ifdef WINDOWS_ENABLED + return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir()); +#else + return NULL; +#endif +} + +void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) { + String task = GDMonoMarshal::mono_string_to_godot(p_task); + String label = GDMonoMarshal::mono_string_to_godot(p_label); + EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel); +} + +void godot_icall_EditorProgress_Dispose(MonoString *p_task) { + String task = GDMonoMarshal::mono_string_to_godot(p_task); + EditorNode::progress_end_task(task); +} + +MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) { + String task = GDMonoMarshal::mono_string_to_godot(p_task); + String state = GDMonoMarshal::mono_string_to_godot(p_state); + return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh); +} + +BindingsGenerator *godot_icall_BindingsGenerator_Ctor() { + return memnew(BindingsGenerator); +} + +void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) { + memdelete(p_handle); +} + +MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) { + return p_handle->is_log_print_enabled(); +} + +void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) { + p_handle.set_log_print_enabled(p_enabled); +} + +int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) { + String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir); + return p_handle->generate_cs_api(output_dir); +} + +uint32_t godot_icall_BindingsGenerator_Version() { + return BindingsGenerator::get_version(); +} + +uint32_t godot_icall_BindingsGenerator_CsGlueVersion() { + return CS_GLUE_VERSION; +} + +int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes) { + String filepath = GDMonoMarshal::mono_string_to_godot(p_filepath); + + ScriptClassParser scp; + Error err = scp.parse_file(filepath); + if (err == OK) { + Array classes = GDMonoMarshal::mono_object_to_variant(p_classes); + const Vector<ScriptClassParser::ClassDecl> &class_decls = scp.get_classes(); + + for (int i = 0; i < class_decls.size(); i++) { + const ScriptClassParser::ClassDecl &classDecl = class_decls[i]; + + Dictionary classDeclDict; + classDeclDict["name"] = classDecl.name; + classDeclDict["namespace"] = classDecl.namespace_; + classDeclDict["nested"] = classDecl.nested; + classDeclDict["base_count"] = classDecl.base.size(); + classes.push_back(classDeclDict); + } + } + return err; +} + +uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path, + MonoString *p_build_config, MonoString *p_custom_lib_dir, MonoObject *r_dependencies) { + String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name); + String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path); + String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config); + String custom_lib_dir = GDMonoMarshal::mono_string_to_godot(p_custom_lib_dir); + Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies); + + return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies); +} + +float godot_icall_Globals_EditorScale() { + return EDSCALE; +} + +MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { + String setting = GDMonoMarshal::mono_string_to_godot(p_setting); + Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); + Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); + return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { + String setting = GDMonoMarshal::mono_string_to_godot(p_setting); + Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); + Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); + return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoString *godot_icall_Globals_TTR(MonoString *p_text) { + String text = GDMonoMarshal::mono_string_to_godot(p_text); + return GDMonoMarshal::mono_string_from_godot(TTR(text)); +} + +MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() { + String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(); + return GDMonoMarshal::mono_string_from_godot(error_str); +} + +MonoString *godot_icall_Internal_FullTemplatesDir() { + String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG); + return GDMonoMarshal::mono_string_from_godot(full_templates_dir); +} + +MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) { + String path = GDMonoMarshal::mono_string_to_godot(p_path); + return GDMonoMarshal::mono_string_from_godot(path.simplify_path()); +} + +MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) { +#ifdef OSX_ENABLED + String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id); + return (MonoBoolean)osx_is_app_bundle_installed; +#else + (void)p_bundle_id; // UNUSED + return (MonoBoolean) false; +#endif +} + +MonoBoolean godot_icall_Internal_GodotIs32Bits() { + return sizeof(void *) == 4; +} + +MonoBoolean godot_icall_Internal_GodotIsRealTDouble() { +#ifdef REAL_T_IS_DOUBLE + return (MonoBoolean) true; +#else + return (MonoBoolean) false; +#endif +} + +void godot_icall_Internal_GodotMainIteration() { + Main::iteration(); +} + +uint64_t godot_icall_Internal_GetCoreApiHash() { + return ClassDB::get_api_hash(ClassDB::API_CORE); +} + +uint64_t godot_icall_Internal_GetEditorApiHash() { + return ClassDB::get_api_hash(ClassDB::API_EDITOR); +} + +MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() { +#ifdef GD_MONO_HOT_RELOAD + return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed(); +#else + return (MonoBoolean) false; +#endif +} + +void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) { +#ifdef GD_MONO_HOT_RELOAD + _GodotSharp::get_singleton()->call_deferred("_reload_assemblies", (bool)p_soft_reload); +#endif +} + +void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() { + ScriptEditor::get_singleton()->get_debugger()->reload_scripts(); +} + +MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) { + Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource); + return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus); +} + +void godot_icall_Internal_EditorNodeShowScriptScreen() { + EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); +} + +MonoObject *godot_icall_Internal_GetScriptsMetadataOrNothing(MonoReflectionType *p_dict_reftype) { + Dictionary maybe_metadata = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing(); + + MonoType *dict_type = mono_reflection_type_get_type(p_dict_reftype); + + uint32_t type_encoding = mono_type_get_type(dict_type); + MonoClass *type_class_raw = mono_class_from_mono_type(dict_type); + GDMonoClass *type_class = GDMono::get_singleton()->get_class(type_class_raw); + + return GDMonoMarshal::variant_to_mono_object(maybe_metadata, ManagedType(type_encoding, type_class)); +} + +MonoString *godot_icall_Internal_MonoWindowsInstallRoot() { +#ifdef WINDOWS_ENABLED + String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir; + return GDMonoMarshal::mono_string_from_godot(install_root_dir); +#else + return NULL; +#endif +} + +MonoString *godot_icall_Utils_OS_GetPlatformName() { + String os_name = OS::get_singleton()->get_name(); + return GDMonoMarshal::mono_string_from_godot(os_name); +} + +void register_editor_internal_calls() { + + // GodotSharpDirs + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir); + mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir); + + // EditorProgress + mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create); + mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose); + mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step); + + // BiningsGenerator + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version); + mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion); + + // ScriptClassParser + mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile); + + // GodotSharpExport + mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies); + + // Internals + mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); + mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir); + mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath); + mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash); + mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded); + mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies); + mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts); + mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit); + mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen); + mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing); + mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot); + + // Globals + mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", (void *)godot_icall_Globals_EditorScale); + mono_add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", (void *)godot_icall_Globals_GlobalDef); + mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", (void *)godot_icall_Globals_EditorDef); + mono_add_internal_call("GodotTools.Internals.Globals::internal_TTR", (void *)godot_icall_Globals_TTR); + + // Utils.OS + mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName); +} diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h new file mode 100644 index 0000000000..1682da66e5 --- /dev/null +++ b/modules/mono/editor/editor_internal_calls.h @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* editor_internal_calls.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_INTERNAL_CALL_H +#define EDITOR_INTERNAL_CALL_H + +void register_editor_internal_calls(); + +#endif // EDITOR_INTERNAL_CALL_H diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp deleted file mode 100644 index 0f12fc5243..0000000000 --- a/modules/mono/editor/godotsharp_builds.cpp +++ /dev/null @@ -1,600 +0,0 @@ -/*************************************************************************/ -/* godotsharp_builds.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godotsharp_builds.h" - -#include "core/vector.h" -#include "main/main.h" - -#include "../glue/cs_glue_version.gen.h" -#include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../utils/path_utils.h" -#include "bindings_generator.h" -#include "csharp_project.h" -#include "godotsharp_editor.h" - -#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)" -#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)" -#define PROP_NAME_XBUILD "xbuild (Deprecated)" - -void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) { - - String solution = GDMonoMarshal::mono_string_to_godot(p_solution); - String config = GDMonoMarshal::mono_string_to_godot(p_config); - GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code); -} - -static Vector<const char *> _get_msbuild_hint_dirs() { - Vector<const char *> ret; -#ifdef OSX_ENABLED - ret.push_back("/Library/Frameworks/Mono.framework/Versions/Current/bin/"); - ret.push_back("/usr/local/var/homebrew/linked/mono/bin/"); -#endif - ret.push_back("/opt/novell/mono/bin/"); - return ret; -} - -#ifdef UNIX_ENABLED -String _find_build_engine_on_unix(const String &p_name) { - String ret = path_which(p_name); - - if (ret.length()) - return ret; - - String ret_fallback = path_which(p_name + ".exe"); - if (ret_fallback.length()) - return ret_fallback; - - static Vector<const char *> locations = _get_msbuild_hint_dirs(); - - for (int i = 0; i < locations.size(); i++) { - String hint_path = locations[i] + p_name; - - if (FileAccess::exists(hint_path)) { - return hint_path; - } - } - - return String(); -} -#endif - -MonoString *godot_icall_BuildInstance_get_MSBuildPath() { - - GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))); - -#if defined(WINDOWS_ENABLED) - switch (build_tool) { - case GodotSharpBuilds::MSBUILD_VS: { - static String msbuild_tools_path; - - if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) { - // Try to search it again if it wasn't found last time or if it was removed from its location - msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); - } - - if (msbuild_tools_path.length()) { - if (!msbuild_tools_path.ends_with("\\")) - msbuild_tools_path += "\\"; - - return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe"); - } - - print_verbose("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'..."); - } // FALL THROUGH - case GodotSharpBuilds::MSBUILD_MONO: { - String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat"); - - if (!FileAccess::exists(msbuild_path)) { - WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path); - } - - return GDMonoMarshal::mono_string_from_godot(msbuild_path); - } break; - case GodotSharpBuilds::XBUILD: { - String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat"); - - if (!FileAccess::exists(xbuild_path)) { - WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path); - } - - return GDMonoMarshal::mono_string_from_godot(xbuild_path); - } break; - default: - ERR_EXPLAIN("You don't deserve to live"); - CRASH_NOW(); - } -#elif defined(UNIX_ENABLED) - static String msbuild_path; - static String xbuild_path; - - if (build_tool == GodotSharpBuilds::XBUILD) { - if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) { - // Try to search it again if it wasn't found last time or if it was removed from its location - xbuild_path = _find_build_engine_on_unix("msbuild"); - } - - if (xbuild_path.empty()) { - WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'"); - return NULL; - } - } else { - if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) { - // Try to search it again if it wasn't found last time or if it was removed from its location - msbuild_path = _find_build_engine_on_unix("msbuild"); - } - - if (msbuild_path.empty()) { - WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'"); - return NULL; - } - } - - return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path); -#else - (void)build_tool; // UNUSED - - ERR_EXPLAIN("Not implemented on this platform"); - ERR_FAIL_V(NULL); -#endif -} - -MonoString *godot_icall_BuildInstance_get_FrameworkPath() { - -#if defined(WINDOWS_ENABLED) - const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); - if (mono_reg_info.assembly_dir.length()) { - String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5"); - return GDMonoMarshal::mono_string_from_godot(framework_path); - } - - ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry"); - ERR_FAIL_V(NULL); -#else - return NULL; -#endif -} - -MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() { - -#if defined(WINDOWS_ENABLED) - const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); - if (mono_reg_info.bin_dir.length()) { - return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir); - } - - ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry"); - ERR_FAIL_V(NULL); -#else - return NULL; -#endif -} - -MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() { - -#if defined(WINDOWS_ENABLED) - return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO; -#else - return false; -#endif -} - -void GodotSharpBuilds::register_internal_calls() { - - static bool registered = false; - ERR_FAIL_COND(registered); - registered = true; - - mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); - mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath); - mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath); - mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir); - mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows); -} - -void GodotSharpBuilds::show_build_error_dialog(const String &p_message) { - - GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error"); - MonoBottomPanel::get_singleton()->show_build_tab(); -} - -bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) { - - String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln"); - - String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config); - String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - - String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config); - String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) { - MonoBuildInfo api_build_info(api_sln_file, p_config); - // TODO Replace this global NoWarn with '#pragma warning' directives on generated files, - // once we start to actively document manually maintained C# classes - api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings - - if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) { - show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution."); - return false; - } - } - - return true; -} - -bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) { - - // Create destination directory if needed - if (!DirAccess::exists(p_dst_dir)) { - DirAccess *da = DirAccess::create_for_path(p_dst_dir); - Error err = da->make_dir_recursive(p_dst_dir); - memdelete(da); - - if (err != OK) { - show_build_error_dialog("Failed to create destination directory for the API assemblies. Error: " + itos(err)); - return false; - } - } - - String assembly_file = p_assembly_name + ".dll"; - String assembly_src = p_src_dir.plus_file(assembly_file); - String assembly_dst = p_dst_dir.plus_file(assembly_file); - - if (!FileAccess::exists(assembly_dst) || - FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || - GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - - String xml_file = p_assembly_name + ".xml"; - if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) - WARN_PRINTS("Failed to copy " + xml_file); - - String pdb_file = p_assembly_name + ".pdb"; - if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK) - WARN_PRINTS("Failed to copy " + pdb_file); - - Error err = da->copy(assembly_src, assembly_dst); - - if (err != OK) { - show_build_error_dialog("Failed to copy " + assembly_file); - return false; - } - - GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false); - } - - return true; -} - -String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) { - - uint64_t api_hash = p_api_type == APIAssembly::API_CORE ? - GDMono::get_singleton()->get_api_core_hash() : - GDMono::get_singleton()->get_api_editor_hash(); - return String::num_uint64(api_hash) + - "_" + String::num_uint64(BindingsGenerator::get_version()) + - "_" + String::num_uint64(CS_GLUE_VERSION); -} - -bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) { - - String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; - - String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir(); - String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); - - if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(api_name + ".dll"))) { - EditorProgress pr("mono_copy_prebuilt_api_assembly", "Copying prebuilt " + api_name + " assembly...", 1); - pr.step("Copying " + api_name + " assembly", 0); - return GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir, api_name, p_api_type); - } - - String api_build_config = "Release"; - - EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3); - - pr.step("Generating " API_SOLUTION_NAME " solution", 0); - - String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() - .plus_file(_api_folder_name(APIAssembly::API_CORE)); - - String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln"); - - if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) { - BindingsGenerator *gen = BindingsGenerator::get_singleton(); - bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); - - Error err = gen->generate_cs_api(api_sln_dir, gen_verbose); - if (err != OK) { - show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err)); - return false; - } - } - - pr.step("Building " API_SOLUTION_NAME " solution", 1); - - if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config)) - return false; - - pr.step("Copying " + api_name + " assembly", 2); - - // Copy the built assembly to the assemblies directory - String api_assembly_dir = api_sln_dir.plus_file(api_name).plus_file("bin").plus_file(api_build_config); - if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type)) - return false; - - return true; -} - -bool GodotSharpBuilds::build_project_blocking(const String &p_config) { - - if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) - return true; // No solution to build - - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) - return false; - - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) - return false; - - EditorProgress pr("mono_project_debug_build", "Building project solution...", 1); - pr.step("Building project solution", 0); - - MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config); - if (!GodotSharpBuilds::get_singleton()->build(build_info)) { - GodotSharpBuilds::show_build_error_dialog("Failed to build project solution"); - return false; - } - - return true; -} - -bool GodotSharpBuilds::editor_build_callback() { - - String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); - String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player"); - - Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); - ERR_FAIL_COND_V(metadata_err != OK, false); - - if (FileAccess::exists(scripts_metadata_path_editor)) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); - - ERR_EXPLAIN("Failed to copy scripts metadata file"); - ERR_FAIL_COND_V(copy_err != OK, false); - } - - return build_project_blocking("Tools"); -} - -GodotSharpBuilds *GodotSharpBuilds::singleton = NULL; - -void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) { - - BuildProcess *match = builds.getptr(p_build_info); - ERR_FAIL_NULL(match); - - BuildProcess &bp = *match; - bp.on_exit(p_exit_code); -} - -void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) { -} - -void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) { -} - -bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) { - - BuildProcess *match = builds.getptr(p_build_info); - - if (match) { - BuildProcess &bp = *match; - bp.start(true); - return bp.exit_code == 0; - } else { - BuildProcess bp = BuildProcess(p_build_info); - bp.start(true); - builds.set(p_build_info, bp); - return bp.exit_code == 0; - } -} - -bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) { - - BuildProcess *match = builds.getptr(p_build_info); - - if (match) { - BuildProcess &bp = *match; - bp.start(); - return !bp.exited; // failed to start - } else { - BuildProcess bp = BuildProcess(p_build_info, p_callback); - bp.start(); - builds.set(p_build_info, bp); - return !bp.exited; // failed to start - } -} - -GodotSharpBuilds::GodotSharpBuilds() { - - singleton = this; - - EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback); - - // Build tool settings - EditorSettings *ed_settings = EditorSettings::get_singleton(); - - EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO); - - ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, - PROP_NAME_MSBUILD_MONO -#ifdef WINDOWS_ENABLED - "," PROP_NAME_MSBUILD_VS -#endif - "," PROP_NAME_XBUILD)); -} - -GodotSharpBuilds::~GodotSharpBuilds() { - - singleton = NULL; -} - -void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) { - - exited = true; - exit_code = p_exit_code; - build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); - build_instance.unref(); - - if (exit_callback) - exit_callback(exit_code); -} - -void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { - - _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) - - exit_code = -1; - - String log_dirpath = build_info.get_log_dirpath(); - - if (build_tab) { - build_tab->on_build_start(); - } else { - build_tab = memnew(MonoBuildTab(build_info, log_dirpath)); - MonoBottomPanel::get_singleton()->add_build_tab(build_tab); - } - - if (p_blocking) { - // Required in order to update the build tasks list - Main::iteration(); - } - - if (!exited) { - exited = true; - String message = "Tried to start build process, but it is already running"; - build_tab->on_build_exec_failed(message); - ERR_EXPLAIN(message); - ERR_FAIL(); - } - - exited = false; - - // Remove old issues file - - String issues_file = "msbuild_issues.csv"; - DirAccessRef d = DirAccess::create_for_path(log_dirpath); - if (d->file_exists(issues_file)) { - Error err = d->remove(issues_file); - if (err != OK) { - exited = true; - String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file); - String message = "Cannot remove issues file: " + file_path; - build_tab->on_build_exec_failed(message); - ERR_EXPLAIN(message); - ERR_FAIL(); - } - } - - GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance"); - - MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_mono_ptr()); - - // Construct - - Variant solution = build_info.solution; - Variant config = build_info.configuration; - - const Variant *ctor_args[2] = { &solution, &config }; - - MonoException *exc = NULL; - GDMonoMethod *ctor = klass->get_method(".ctor", 2); - ctor->invoke(mono_object, ctor_args, &exc); - - if (exc) { - exited = true; - GDMonoUtils::debug_unhandled_exception(exc); - String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); - build_tab->on_build_exec_failed(message); - ERR_EXPLAIN(message); - ERR_FAIL(); - } - - // Call Build - - String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path(); - Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path); - Variant logger_output_dir = log_dirpath; - Variant custom_props = build_info.custom_props; - - const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props }; - - exc = NULL; - GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3); - build_method->invoke(mono_object, args, &exc); - - if (exc) { - exited = true; - GDMonoUtils::debug_unhandled_exception(exc); - String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); - build_tab->on_build_exec_failed(message); - ERR_EXPLAIN(message); - ERR_FAIL(); - } - - // Build returned - - if (p_blocking) { - exited = true; - exit_code = klass->get_field("exitCode")->get_int_value(mono_object); - - if (exit_code != 0) { - print_verbose("MSBuild finished with exit code " + itos(exit_code)); - } - - build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); - } else { - build_instance = MonoGCHandle::create_strong(mono_object); - exited = false; - } -} - -GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) : - build_info(p_build_info), - build_tab(NULL), - exit_callback(p_callback), - exited(true), - exit_code(-1) { -} diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h deleted file mode 100644 index 7f38b0aa49..0000000000 --- a/modules/mono/editor/godotsharp_builds.h +++ /dev/null @@ -1,100 +0,0 @@ -/*************************************************************************/ -/* godotsharp_builds.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOTSHARP_BUILDS_H -#define GODOTSHARP_BUILDS_H - -#include "../mono_gd/gd_mono.h" -#include "mono_bottom_panel.h" -#include "mono_build_info.h" - -typedef void (*GodotSharpBuild_ExitCallback)(int); - -class GodotSharpBuilds { - -private: - struct BuildProcess { - Ref<MonoGCHandle> build_instance; - MonoBuildInfo build_info; - MonoBuildTab *build_tab; - GodotSharpBuild_ExitCallback exit_callback; - bool exited; - int exit_code; - - void on_exit(int p_exit_code); - void start(bool p_blocking = false); - - BuildProcess() {} - BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); - }; - - HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds; - - static String _api_folder_name(APIAssembly::Type p_api_type); - - static GodotSharpBuilds *singleton; - -public: - enum BuildTool { - MSBUILD_MONO, -#ifdef WINDOWS_ENABLED - MSBUILD_VS, -#endif - XBUILD // Deprecated - }; - - _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } - - static void register_internal_calls(); - - static void show_build_error_dialog(const String &p_message); - - void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); - - void restart_build(MonoBuildTab *p_build_tab); - void stop_build(MonoBuildTab *p_build_tab); - - bool build(const MonoBuildInfo &p_build_info); - bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); - - static bool build_api_sln(const String &p_api_sln_dir, const String &p_config); - static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type); - - static bool make_api_assembly(APIAssembly::Type p_api_type); - - static bool build_project_blocking(const String &p_config); - - static bool editor_build_callback(); - - GodotSharpBuilds(); - ~GodotSharpBuilds(); -}; - -#endif // GODOTSHARP_BUILDS_H diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp deleted file mode 100644 index cce86efbf5..0000000000 --- a/modules/mono/editor/godotsharp_editor.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/*************************************************************************/ -/* godotsharp_editor.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godotsharp_editor.h" - -#include "core/os/os.h" -#include "core/project_settings.h" -#include "scene/gui/control.h" -#include "scene/main/node.h" - -#include "../csharp_script.h" -#include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../utils/path_utils.h" -#include "bindings_generator.h" -#include "csharp_project.h" -#include "dotnet_solution.h" -#include "godotsharp_export.h" - -#ifdef OSX_ENABLED -#include "../utils/osx_utils.h" -#endif - -#ifdef WINDOWS_ENABLED -#include "../utils/mono_reg_utils.h" -#endif - -GodotSharpEditor *GodotSharpEditor::singleton = NULL; - -bool GodotSharpEditor::_create_project_solution() { - - EditorProgress pr("create_csharp_solution", TTR("Generating solution..."), 2); - - pr.step(TTR("Generating C# project...")); - - String path = OS::get_singleton()->get_resource_dir(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; - } - - String guid = CSharpProject::generate_game_project(path, name); - - if (guid.length()) { - - DotNetSolution solution(name); - - if (!solution.set_path(path)) { - show_error_dialog(TTR("Failed to create solution.")); - return false; - } - - DotNetSolution::ProjectInfo proj_info; - proj_info.guid = guid; - proj_info.relpath = name + ".csproj"; - proj_info.configs.push_back("Debug"); - proj_info.configs.push_back("Release"); - proj_info.configs.push_back("Tools"); - - solution.add_new_project(name, proj_info); - - Error sln_error = solution.save(); - - if (sln_error != OK) { - show_error_dialog(TTR("Failed to save solution.")); - return false; - } - - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) - return false; - - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) - return false; - - pr.step(TTR("Done")); - - // Here, after all calls to progress_task_step - call_deferred("_remove_create_sln_menu_option"); - - } else { - show_error_dialog(TTR("Failed to create C# project.")); - } - - return true; -} - -void GodotSharpEditor::_make_api_solutions_if_needed() { - // I'm sick entirely of ProgressDialog - static bool recursion_guard = false; - if (!recursion_guard) { - recursion_guard = true; - _make_api_solutions_if_needed_impl(); - recursion_guard = false; - } -} - -void GodotSharpEditor::_make_api_solutions_if_needed_impl() { - // If the project has a solution and C# project make sure the API assemblies are present and up to date - String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); - - if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) || - GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) - return; - } - - if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) || - GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { - if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) - return; // Redundant? I don't think so - } -} - -void GodotSharpEditor::_remove_create_sln_menu_option() { - - menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN)); - - if (menu_popup->get_item_count() == 0) - menu_button->hide(); - - bottom_panel_btn->show(); -} - -void GodotSharpEditor::_show_about_dialog() { - - bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); - about_dialog_checkbox->set_pressed(show_on_start); - about_dialog->popup_centered_minsize(); -} - -void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) { - - bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); - if (show_on_start != p_enabled) { - EditorSettings::get_singleton()->set_setting("mono/editor/show_info_on_start", p_enabled); - } -} - -void GodotSharpEditor::_menu_option_pressed(int p_id) { - - switch (p_id) { - case MENU_CREATE_SLN: { - - _create_project_solution(); - } break; - case MENU_ABOUT_CSHARP: { - - _show_about_dialog(); - } break; - default: - ERR_FAIL(); - } -} - -void GodotSharpEditor::_notification(int p_notification) { - - switch (p_notification) { - - case NOTIFICATION_READY: { - - bool show_info_dialog = EDITOR_GET("mono/editor/show_info_on_start"); - if (show_info_dialog) { - about_dialog->set_exclusive(true); - _show_about_dialog(); - // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive then. - about_dialog->set_exclusive(false); - } - } - } -} - -void GodotSharpEditor::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution); - ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed); - ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option); - ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start); - ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); -} - -MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) { -#ifdef OSX_ENABLED - return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id)); -#else - (void)p_bundle_id; // UNUSED - ERR_FAIL_V(false); -#endif -} - -MonoString *godot_icall_Utils_OS_GetPlatformName() { - return GDMonoMarshal::mono_string_from_godot(OS::get_singleton()->get_name()); -} - -void GodotSharpEditor::register_internal_calls() { - - static bool registered = false; - ERR_FAIL_COND(registered); - registered = true; - - mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled); - mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName); - - GodotSharpBuilds::register_internal_calls(); - GodotSharpExport::register_internal_calls(); -} - -void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) { - - error_dialog->set_title(p_title); - error_dialog->set_text(p_message); - error_dialog->popup_centered_minsize(); -} - -Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { - - ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))); - - switch (editor) { - case EDITOR_VSCODE: { - static String vscode_path; - - if (vscode_path.empty() || !FileAccess::exists(vscode_path)) { - // Try to search it again if it wasn't found last time or if it was removed from its location - vscode_path = path_which("code"); - } - - List<String> args; - -#ifdef OSX_ENABLED - // The package path is '/Applications/Visual Studio Code.app' - static const String vscode_bundle_id = "com.microsoft.VSCode"; - static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id); - - if (osx_app_bundle_installed) { - args.push_back("-b"); - args.push_back(vscode_bundle_id); - - // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is - // editing our folder. It's better to ask for a new window and let VSCode do the window management. - args.push_back("-n"); - - // The open process must wait until the application finishes (which is instant in VSCode's case) - args.push_back("--wait-apps"); - - args.push_back("--args"); - } -#endif - - args.push_back(ProjectSettings::get_singleton()->get_resource_path()); - - String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); - - if (p_line >= 0) { - args.push_back("-g"); - args.push_back(script_path + ":" + itos(p_line + 1) + ":" + itos(p_col)); - } else { - args.push_back(script_path); - } - -#ifdef OSX_ENABLED - ERR_EXPLAIN("Cannot find code editor: VSCode"); - ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND); - - String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path; -#else - ERR_EXPLAIN("Cannot find code editor: VSCode"); - ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND); - - String command = vscode_path; -#endif - - Error err = OS::get_singleton()->execute(command, args, false); - - if (err != OK) { - ERR_PRINT("Error when trying to execute code editor: VSCode"); - return err; - } - } break; -#ifdef OSX_ENABLED - case EDITOR_VISUALSTUDIO_MAC: - // [[fallthrough]]; -#endif - case EDITOR_MONODEVELOP: { -#ifdef OSX_ENABLED - bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC; - - MonoDevelopInstance **instance = is_visualstudio ? - &visualstudio_mac_instance : - &monodevelop_instance; - - MonoDevelopInstance::EditorId editor_id = is_visualstudio ? - MonoDevelopInstance::VISUALSTUDIO_FOR_MAC : - MonoDevelopInstance::MONODEVELOP; -#else - MonoDevelopInstance **instance = &monodevelop_instance; - MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP; -#endif - - if (!*instance) - *instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id)); - - String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); - - if (p_line >= 0) { - script_path += ";" + itos(p_line + 1) + ";" + itos(p_col); - } - - (*instance)->execute(script_path); - } break; - default: - return ERR_UNAVAILABLE; - } - - return OK; -} - -bool GodotSharpEditor::overrides_external_editor() { - - return ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))) != EDITOR_NONE; -} - -GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { - - singleton = this; - - monodevelop_instance = NULL; -#ifdef OSX_ENABLED - visualstudio_mac_instance = NULL; -#endif - - editor = p_editor; - - error_dialog = memnew(AcceptDialog); - editor->get_gui_base()->add_child(error_dialog); - - bottom_panel_btn = editor->add_bottom_panel_item(TTR("Mono"), memnew(MonoBottomPanel(editor))); - - godotsharp_builds = memnew(GodotSharpBuilds); - - editor->add_child(memnew(MonoReloadNode)); - - menu_button = memnew(MenuButton); - menu_button->set_text(TTR("Mono")); - menu_popup = menu_button->get_popup(); - - // TODO: Remove or edit this info dialog once Mono support is no longer in alpha - { - menu_popup->add_item(TTR("About C# support"), MENU_ABOUT_CSHARP); - about_dialog = memnew(AcceptDialog); - editor->get_gui_base()->add_child(about_dialog); - about_dialog->set_title("Important: C# support is not feature-complete"); - - // We don't use set_text() as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox - // we'll add. Instead we add containers and a new autowrapped Label inside. - - // Main VBoxContainer (icon + label on top, checkbox at bottom) - VBoxContainer *about_vbc = memnew(VBoxContainer); - about_dialog->add_child(about_vbc); - - // HBoxContainer for icon + label - HBoxContainer *about_hbc = memnew(HBoxContainer); - about_vbc->add_child(about_hbc); - - TextureRect *about_icon = memnew(TextureRect); - about_hbc->add_child(about_icon); - Ref<Texture> about_icon_tex = about_icon->get_icon("NodeWarning", "EditorIcons"); - about_icon->set_texture(about_icon_tex); - - Label *about_label = memnew(Label); - about_hbc->add_child(about_label); - about_label->set_custom_minimum_size(Size2(600, 150) * EDSCALE); - about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); - about_label->set_autowrap(true); - String about_text = - String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + - "It is currently in an alpha stage and is not suitable for use in production.\n\n" + - "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " + - "Bugs and usability issues will be addressed gradually over future 3.x releases, " + - "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + - "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc:\n\n" + - " https://github.com/godotengine/godot/issues\n\n" + - "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; - about_label->set_text(about_text); - - EDITOR_DEF("mono/editor/show_info_on_start", true); - - // CheckBox in main container - about_dialog_checkbox = memnew(CheckBox); - about_vbc->add_child(about_dialog_checkbox); - about_dialog_checkbox->set_text("Show this warning when starting the editor"); - about_dialog_checkbox->connect("toggled", this, "_toggle_about_dialog_on_start"); - } - - String sln_path = GodotSharpDirs::get_project_sln_path(); - String csproj_path = GodotSharpDirs::get_project_csproj_path(); - - if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) { - // We can't use EditorProgress here. It calls Main::iterarion() and the main loop is not initialized yet. - call_deferred("_make_api_solutions_if_needed"); - } else { - bottom_panel_btn->hide(); - menu_popup->add_item(TTR("Create C# solution"), MENU_CREATE_SLN); - } - - menu_popup->connect("id_pressed", this, "_menu_option_pressed"); - - if (menu_popup->get_item_count() == 0) - menu_button->hide(); - - editor->get_menu_hb()->add_child(menu_button); - - // External editor settings - EditorSettings *ed_settings = EditorSettings::get_singleton(); - EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); - - String settings_hint_str = "None"; - -#ifdef WINDOWS_ENABLED - settings_hint_str += ",MonoDevelop,Visual Studio Code"; -#elif OSX_ENABLED - settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code"; -#elif UNIX_ENABLED - settings_hint_str += ",MonoDevelop,Visual Studio Code"; -#endif - - ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str)); - - // Export plugin - Ref<GodotSharpExport> godotsharp_export; - godotsharp_export.instance(); - EditorExport::get_singleton()->add_export_plugin(godotsharp_export); -} - -GodotSharpEditor::~GodotSharpEditor() { - - singleton = NULL; - - memdelete(godotsharp_builds); - - if (monodevelop_instance) { - memdelete(monodevelop_instance); - monodevelop_instance = NULL; - } -} - -MonoReloadNode *MonoReloadNode::singleton = NULL; - -void MonoReloadNode::_reload_timer_timeout() { - - if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { - CSharpLanguage::get_singleton()->reload_assemblies(false); - } -} - -void MonoReloadNode::restart_reload_timer() { - - reload_timer->stop(); - reload_timer->start(); -} - -void MonoReloadNode::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout); -} - -void MonoReloadNode::_notification(int p_what) { - switch (p_what) { - case MainLoop::NOTIFICATION_WM_FOCUS_IN: { - restart_reload_timer(); - if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { - CSharpLanguage::get_singleton()->reload_assemblies(false); - } - } break; - default: { - } break; - }; -} - -MonoReloadNode::MonoReloadNode() { - - singleton = this; - - reload_timer = memnew(Timer); - add_child(reload_timer); - reload_timer->set_one_shot(false); - reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5)); - reload_timer->connect("timeout", this, "_reload_timer_timeout"); - reload_timer->start(); -} - -MonoReloadNode::~MonoReloadNode() { - - singleton = NULL; -} diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h deleted file mode 100644 index 9fb0e40132..0000000000 --- a/modules/mono/editor/godotsharp_editor.h +++ /dev/null @@ -1,132 +0,0 @@ -/*************************************************************************/ -/* godotsharp_editor.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOTSHARP_EDITOR_H -#define GODOTSHARP_EDITOR_H - -#include "godotsharp_builds.h" -#include "monodevelop_instance.h" - -class GodotSharpEditor : public Node { - GDCLASS(GodotSharpEditor, Object) - - EditorNode *editor; - - MenuButton *menu_button; - PopupMenu *menu_popup; - - AcceptDialog *error_dialog; - AcceptDialog *about_dialog; - CheckBox *about_dialog_checkbox; - - ToolButton *bottom_panel_btn; - - GodotSharpBuilds *godotsharp_builds; - - MonoDevelopInstance *monodevelop_instance; -#ifdef OSX_ENABLED - MonoDevelopInstance *visualstudio_mac_instance; -#endif - - bool _create_project_solution(); - void _make_api_solutions_if_needed(); - void _make_api_solutions_if_needed_impl(); - - void _remove_create_sln_menu_option(); - void _show_about_dialog(); - void _toggle_about_dialog_on_start(bool p_enabled); - - void _menu_option_pressed(int p_id); - - static GodotSharpEditor *singleton; - -protected: - void _notification(int p_notification); - static void _bind_methods(); - -public: - enum MenuOptions { - MENU_CREATE_SLN, - MENU_ABOUT_CSHARP, - }; - - enum ExternalEditor { - EDITOR_NONE, -#ifdef WINDOWS_ENABLED - //EDITOR_VISUALSTUDIO, // TODO - EDITOR_MONODEVELOP, - EDITOR_VSCODE -#elif OSX_ENABLED - EDITOR_VISUALSTUDIO_MAC, - EDITOR_MONODEVELOP, - EDITOR_VSCODE -#elif UNIX_ENABLED - EDITOR_MONODEVELOP, - EDITOR_VSCODE -#endif - }; - - _FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; } - - static void register_internal_calls(); - - void show_error_dialog(const String &p_message, const String &p_title = "Error"); - - Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col); - bool overrides_external_editor(); - - GodotSharpEditor(EditorNode *p_editor); - ~GodotSharpEditor(); -}; - -class MonoReloadNode : public Node { - GDCLASS(MonoReloadNode, Node) - - Timer *reload_timer; - - void _reload_timer_timeout(); - - static MonoReloadNode *singleton; - -protected: - static void _bind_methods(); - - void _notification(int p_what); - -public: - _FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; } - - void restart_reload_timer(); - - MonoReloadNode(); - ~MonoReloadNode(); -}; - -#endif // GODOTSHARP_EDITOR_H diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 34c710320a..020bb70a08 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,170 +30,36 @@ #include "godotsharp_export.h" -#include "core/version.h" +#include <mono/metadata/image.h> -#include "../csharp_script.h" -#include "../godotsharp_defs.h" -#include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono_class.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "csharp_project.h" -#include "godotsharp_builds.h" +#include "../mono_gd/gd_mono.h" +#include "../mono_gd/gd_mono_assembly.h" -static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() { - String current_version = VERSION_FULL_CONFIG; - String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version); - return GDMonoMarshal::mono_string_from_godot(ProjectSettings::get_singleton()->globalize_path(templates_dir)); -} - -static MonoString *godot_icall_GodotSharpExport_GetDataDirName() { - String appname = ProjectSettings::get_singleton()->get("application/config/name"); - String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); - return GDMonoMarshal::mono_string_from_godot("data_" + appname_safe); -} - -void GodotSharpExport::register_internal_calls() { - static bool registered = false; - ERR_FAIL_COND(registered); - registered = true; - - mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetTemplatesDir", (void *)godot_icall_GodotSharpExport_GetTemplatesDir); - mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetDataDirName", (void *)godot_icall_GodotSharpExport_GetDataDirName); -} - -void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &) { - - if (p_type != CSharpLanguage::get_singleton()->get_type()) - return; - - ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension()); - - // TODO what if the source file is not part of the game's C# project - - if (!GLOBAL_GET("mono/export/include_scripts_content")) { - // We don't want to include the source code on exported games - add_file(p_path, Vector<uint8_t>(), false); - skip(); - } -} - -void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) { - - // TODO right now there is no way to stop the export process with an error - - ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - ERR_FAIL_NULL(TOOLS_DOMAIN); - ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly()); - - String build_config = p_debug ? "Debug" : "Release"; - - String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); - Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); - ERR_FAIL_COND(metadata_err != OK); - - ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); - - ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); - - // Add dependency assemblies - - Map<String, String> dependencies; - - String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); - if (project_dll_name.empty()) { - project_dll_name = "UnnamedProject"; - } - - String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config); - String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll"); - dependencies.insert(project_dll_name, project_dll_src_path); +String get_assemblyref_name(MonoImage *p_image, int index) { + const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF); - { - MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); - ERR_FAIL_NULL(export_domain); - _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); + uint32_t cols[MONO_ASSEMBLYREF_SIZE]; - _GDMONO_SCOPE_DOMAIN_(export_domain); + mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE); - GDMonoAssembly *scripts_assembly = NULL; - bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, - project_dll_src_path, &scripts_assembly, /* refonly: */ true); - - ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); - ERR_FAIL_COND(!load_success); - - Vector<String> search_dirs; - GDMonoAssembly::fill_search_dirs(search_dirs); - Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies); - ERR_FAIL_COND(depend_error != OK); - } - - for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { - String depend_src_path = E->value(); - String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); - ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); - } - - // Mono specific export template extras (data dir) - - GDMonoClass *export_class = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "GodotSharpExport"); - ERR_FAIL_NULL(export_class); - GDMonoMethod *export_begin_method = export_class->get_method("_ExportBegin", 4); - ERR_FAIL_NULL(export_begin_method); - - MonoArray *features = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_features.size()); - int i = 0; - for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) { - MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get()); - mono_array_set(features, MonoString *, i, boxed); - i++; - } - - MonoBoolean debug = p_debug; - MonoString *path = GDMonoMarshal::mono_string_from_godot(p_path); - uint32_t flags = p_flags; - void *args[4] = { features, &debug, path, &flags }; - MonoException *exc = NULL; - export_begin_method->invoke_raw(NULL, args, &exc); - - if (exc) { - GDMonoUtils::debug_print_unhandled_exception(exc); - ERR_FAIL(); - } + return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])); } -bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) { - - FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, false); - - Vector<uint8_t> data; - data.resize(f->get_len()); - f->get_buffer(data.ptrw(), data.size()); - - add_file(p_dst_path, data, p_remap); - - return true; -} - -Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies) { - +Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { MonoImage *image = p_assembly->get_image(); for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { - MonoAssemblyName *ref_aname = aname_prealloc; - mono_assembly_get_assemblyref(image, i, ref_aname); - String ref_name = mono_assembly_name_get_name(ref_aname); + String ref_name = get_assemblyref_name(image, i); - if (r_dependencies.find(ref_name)) + if (r_dependencies.has(ref_name)) continue; GDMonoAssembly *ref_assembly = NULL; String path; bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe"); - for (int i = 0; i < p_search_dirs.size(); i++) { - const String &search_dir = p_search_dirs[i]; + for (int j = 0; j < p_search_dirs.size(); j++) { + const String &search_dir = p_search_dirs[j]; if (has_extension) { path = search_dir.plus_file(ref_name); @@ -224,24 +90,34 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c ERR_FAIL_V(ERR_CANT_RESOLVE); } - r_dependencies.insert(ref_name, ref_assembly->get_path()); + r_dependencies[ref_name] = ref_assembly->get_path(); - Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); - if (err != OK) - return err; + Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); + if (err != OK) { + ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name); + ERR_FAIL_V(err); + } } return OK; } -GodotSharpExport::GodotSharpExport() { - // MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves. - // There isn't any api to allocate an empty one either, so we need to do it this way. - aname_prealloc = mono_assembly_name_new("whatever"); - mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included) -} +Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies) { + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + ERR_FAIL_NULL_V(export_domain, FAILED); + _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); + + _GDMONO_SCOPE_DOMAIN_(export_domain); + + GDMonoAssembly *scripts_assembly = NULL; + bool load_success = GDMono::get_singleton()->load_assembly_from(p_project_dll_name, + p_project_dll_src_path, &scripts_assembly, /* refonly: */ true); + + ERR_EXPLAIN("Cannot load assembly (refonly): " + p_project_dll_name); + ERR_FAIL_COND_V(!load_success, ERR_CANT_RESOLVE); + + Vector<String> search_dirs; + GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir); -GodotSharpExport::~GodotSharpExport() { - if (aname_prealloc) - mono_free(aname_prealloc); + return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies); } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 10d4375567..8d121a6bc3 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,29 +31,19 @@ #ifndef GODOTSHARP_EXPORT_H #define GODOTSHARP_EXPORT_H -#include <mono/metadata/image.h> - -#include "editor/editor_export.h" +#include "core/dictionary.h" +#include "core/error_list.h" +#include "core/ustring.h" #include "../mono_gd/gd_mono_header.h" -class GodotSharpExport : public EditorExportPlugin { - - MonoAssemblyName *aname_prealloc; - - bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false); - - Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies); - -protected: - virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); - virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags); +namespace GodotSharpExport { -public: - static void register_internal_calls(); +Error get_exported_assembly_dependencies(const String &p_project_dll_name, + const String &p_project_dll_src_path, const String &p_build_config, + const String &p_custom_lib_dir, Dictionary &r_dependencies); +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); - GodotSharpExport(); - ~GodotSharpExport(); -}; +} // namespace GodotSharpExport #endif // GODOTSHARP_EXPORT_H diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp deleted file mode 100644 index e89d21d92d..0000000000 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ /dev/null @@ -1,499 +0,0 @@ -/*************************************************************************/ -/* mono_bottom_panel.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "mono_bottom_panel.h" - -#include "../csharp_script.h" -#include "../godotsharp_dirs.h" -#include "csharp_project.h" -#include "godotsharp_editor.h" - -MonoBottomPanel *MonoBottomPanel::singleton = NULL; - -void MonoBottomPanel::_update_build_tabs_list() { - - build_tabs_list->clear(); - - int current_tab = build_tabs->get_current_tab(); - - bool no_current_tab = current_tab < 0 || current_tab >= build_tabs->get_tab_count(); - - for (int i = 0; i < build_tabs->get_child_count(); i++) { - - MonoBuildTab *tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(i)); - - if (tab) { - String item_name = tab->build_info.solution.get_file().get_basename(); - item_name += " [" + tab->build_info.configuration + "]"; - - build_tabs_list->add_item(item_name, tab->get_icon_texture()); - - String item_tooltip = "Solution: " + tab->build_info.solution; - item_tooltip += "\nConfiguration: " + tab->build_info.configuration; - item_tooltip += "\nStatus: "; - - if (tab->build_exited) { - item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored"; - } else { - item_tooltip += "Running"; - } - - if (!tab->build_exited || tab->build_result == MonoBuildTab::RESULT_ERROR) { - item_tooltip += "\nErrors: " + itos(tab->error_count); - } - - item_tooltip += "\nWarnings: " + itos(tab->warning_count); - - build_tabs_list->set_item_tooltip(i, item_tooltip); - - if (no_current_tab || current_tab == i) { - build_tabs_list->select(i); - _build_tabs_item_selected(i); - } - } - } -} - -void MonoBottomPanel::add_build_tab(MonoBuildTab *p_build_tab) { - - build_tabs->add_child(p_build_tab); - raise_build_tab(p_build_tab); -} - -void MonoBottomPanel::raise_build_tab(MonoBuildTab *p_build_tab) { - - ERR_FAIL_COND(p_build_tab->get_parent() != build_tabs); - build_tabs->move_child(p_build_tab, 0); - _update_build_tabs_list(); -} - -void MonoBottomPanel::show_build_tab() { - - for (int i = 0; i < panel_tabs->get_tab_count(); i++) { - if (panel_tabs->get_tab_control(i) == panel_builds_tab) { - panel_tabs->set_current_tab(i); - editor->make_bottom_panel_item_visible(this); - return; - } - } - - ERR_PRINT("Builds tab not found"); -} - -void MonoBottomPanel::_build_tabs_item_selected(int p_idx) { - - ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count()); - - build_tabs->set_current_tab(p_idx); - if (!build_tabs->is_visible()) - build_tabs->set_visible(true); - - warnings_btn->set_visible(true); - errors_btn->set_visible(true); - view_log_btn->set_visible(true); -} - -void MonoBottomPanel::_build_tabs_nothing_selected() { - - if (build_tabs->get_tab_count() != 0) // just in case - build_tabs->set_visible(false); - - warnings_btn->set_visible(false); - errors_btn->set_visible(false); - view_log_btn->set_visible(false); -} - -void MonoBottomPanel::_warnings_toggled(bool p_pressed) { - - int current_tab = build_tabs->get_current_tab(); - ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count()); - MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab)); - build_tab->warnings_visible = p_pressed; - build_tab->_update_issues_list(); -} - -void MonoBottomPanel::_errors_toggled(bool p_pressed) { - - int current_tab = build_tabs->get_current_tab(); - ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count()); - MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab)); - build_tab->errors_visible = p_pressed; - build_tab->_update_issues_list(); -} - -void MonoBottomPanel::_build_project_pressed() { - - String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); - Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); - ERR_FAIL_COND(metadata_err != OK); - - bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); - - if (build_success) { - MonoReloadNode::get_singleton()->restart_reload_timer(); - if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { - CSharpLanguage::get_singleton()->reload_assemblies(false); - } - } -} - -void MonoBottomPanel::_view_log_pressed() { - - if (build_tabs_list->is_anything_selected()) { - Vector<int> selected_items = build_tabs_list->get_selected_items(); - CRASH_COND(selected_items.size() != 1); - int selected_item = selected_items[0]; - - MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_tab_control(selected_item)); - ERR_FAIL_NULL(build_tab); - - String log_dirpath = build_tab->get_build_info().get_log_dirpath(); - - OS::get_singleton()->shell_open(log_dirpath.plus_file("msbuild_log.txt")); - } -} - -void MonoBottomPanel::_notification(int p_what) { - - switch (p_what) { - - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); - panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); - panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); - } break; - } -} - -void MonoBottomPanel::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed); - ClassDB::bind_method(D_METHOD("_view_log_pressed"), &MonoBottomPanel::_view_log_pressed); - ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled); - ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled); - ClassDB::bind_method(D_METHOD("_build_tabs_item_selected", "idx"), &MonoBottomPanel::_build_tabs_item_selected); - ClassDB::bind_method(D_METHOD("_build_tabs_nothing_selected"), &MonoBottomPanel::_build_tabs_nothing_selected); -} - -MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { - - singleton = this; - - editor = p_editor; - - set_v_size_flags(SIZE_EXPAND_FILL); - set_anchors_and_margins_preset(Control::PRESET_WIDE); - - panel_tabs = memnew(TabContainer); - panel_tabs->set_tab_align(TabContainer::ALIGN_LEFT); - panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles")); - panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles")); - panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); - panel_tabs->set_custom_minimum_size(Size2(0, 228) * EDSCALE); - panel_tabs->set_v_size_flags(SIZE_EXPAND_FILL); - add_child(panel_tabs); - - { // Builds - panel_builds_tab = memnew(VBoxContainer); - panel_builds_tab->set_name(TTR("Builds")); - panel_builds_tab->set_h_size_flags(SIZE_EXPAND_FILL); - panel_tabs->add_child(panel_builds_tab); - - HBoxContainer *toolbar_hbc = memnew(HBoxContainer); - toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL); - panel_builds_tab->add_child(toolbar_hbc); - - Button *build_project_btn = memnew(Button); - build_project_btn->set_text(TTR("Build Project")); - build_project_btn->set_focus_mode(FOCUS_NONE); - build_project_btn->connect("pressed", this, "_build_project_pressed"); - toolbar_hbc->add_child(build_project_btn); - - toolbar_hbc->add_spacer(); - - warnings_btn = memnew(ToolButton); - warnings_btn->set_text(TTR("Warnings")); - warnings_btn->set_toggle_mode(true); - warnings_btn->set_pressed(true); - warnings_btn->set_visible(false); - warnings_btn->set_focus_mode(FOCUS_NONE); - warnings_btn->connect("toggled", this, "_warnings_toggled"); - toolbar_hbc->add_child(warnings_btn); - - errors_btn = memnew(ToolButton); - errors_btn->set_text(TTR("Errors")); - errors_btn->set_toggle_mode(true); - errors_btn->set_pressed(true); - errors_btn->set_visible(false); - errors_btn->set_focus_mode(FOCUS_NONE); - errors_btn->connect("toggled", this, "_errors_toggled"); - toolbar_hbc->add_child(errors_btn); - - toolbar_hbc->add_spacer(); - - view_log_btn = memnew(Button); - view_log_btn->set_text(TTR("View log")); - view_log_btn->set_focus_mode(FOCUS_NONE); - view_log_btn->set_visible(false); - view_log_btn->connect("pressed", this, "_view_log_pressed"); - toolbar_hbc->add_child(view_log_btn); - - HSplitContainer *hsc = memnew(HSplitContainer); - hsc->set_h_size_flags(SIZE_EXPAND_FILL); - hsc->set_v_size_flags(SIZE_EXPAND_FILL); - panel_builds_tab->add_child(hsc); - - build_tabs_list = memnew(ItemList); - build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL); - build_tabs_list->connect("item_selected", this, "_build_tabs_item_selected"); - build_tabs_list->connect("nothing_selected", this, "_build_tabs_nothing_selected"); - hsc->add_child(build_tabs_list); - - build_tabs = memnew(TabContainer); - build_tabs->set_tab_align(TabContainer::ALIGN_LEFT); - build_tabs->set_h_size_flags(SIZE_EXPAND_FILL); - build_tabs->set_tabs_visible(false); - hsc->add_child(build_tabs); - } -} - -MonoBottomPanel::~MonoBottomPanel() { - - singleton = NULL; -} - -void MonoBuildTab::_load_issues_from_file(const String &p_csv_file) { - - FileAccessRef f = FileAccess::open(p_csv_file, FileAccess::READ); - - if (!f) - return; - - while (!f->eof_reached()) { - Vector<String> csv_line = f->get_csv_line(); - - if (csv_line.size() == 1 && csv_line[0].empty()) - return; - - ERR_CONTINUE(csv_line.size() != 7); - - BuildIssue issue; - issue.warning = csv_line[0] == "warning"; - issue.file = csv_line[1]; - issue.line = csv_line[2].to_int(); - issue.column = csv_line[3].to_int(); - issue.code = csv_line[4]; - issue.message = csv_line[5]; - issue.project_file = csv_line[6]; - - if (issue.warning) - warning_count += 1; - else - error_count += 1; - - issues.push_back(issue); - } -} - -void MonoBuildTab::_update_issues_list() { - - issues_list->clear(); - - Ref<Texture> warning_icon = get_icon("Warning", "EditorIcons"); - Ref<Texture> error_icon = get_icon("Error", "EditorIcons"); - - for (int i = 0; i < issues.size(); i++) { - - const BuildIssue &issue = issues[i]; - - if (!(issue.warning ? warnings_visible : errors_visible)) - continue; - - String tooltip; - tooltip += String("Message: ") + issue.message; - - if (issue.code.length()) { - tooltip += String("\nCode: ") + issue.code; - } - - tooltip += String("\nType: ") + (issue.warning ? "warning" : "error"); - - String text; - - if (issue.file.length()) { - String sline = String::num_int64(issue.line); - String scolumn = String::num_int64(issue.column); - - text += issue.file + "("; - text += sline + ","; - text += scolumn + "): "; - - tooltip += "\nFile: " + issue.file; - tooltip += "\nLine: " + sline; - tooltip += "\nColumn: " + scolumn; - } - - if (issue.project_file.length()) { - tooltip += "\nProject: " + issue.project_file; - } - - text += issue.message; - - int line_break_idx = text.find("\n"); - issues_list->add_item(line_break_idx == -1 ? text : text.substr(0, line_break_idx), - issue.warning ? warning_icon : error_icon); - int index = issues_list->get_item_count() - 1; - issues_list->set_item_tooltip(index, tooltip); - issues_list->set_item_metadata(index, i); - } -} - -Ref<Texture> MonoBuildTab::get_icon_texture() const { - - if (build_exited) { - if (build_result == RESULT_ERROR) { - return get_icon("StatusError", "EditorIcons"); - } else { - return get_icon("StatusSuccess", "EditorIcons"); - } - } else { - return get_icon("Stop", "EditorIcons"); - } -} - -MonoBuildInfo MonoBuildTab::get_build_info() { - - return build_info; -} - -void MonoBuildTab::on_build_start() { - - build_exited = false; - - issues.clear(); - warning_count = 0; - error_count = 0; - _update_issues_list(); - - MonoBottomPanel::get_singleton()->raise_build_tab(this); -} - -void MonoBuildTab::on_build_exit(BuildResult result) { - - build_exited = true; - build_result = result; - - _load_issues_from_file(logs_dir.plus_file("msbuild_issues.csv")); - _update_issues_list(); - - MonoBottomPanel::get_singleton()->raise_build_tab(this); -} - -void MonoBuildTab::on_build_exec_failed(const String &p_cause) { - - build_exited = true; - build_result = RESULT_ERROR; - - issues_list->clear(); - - BuildIssue issue; - issue.message = p_cause; - issue.warning = false; - - error_count += 1; - issues.push_back(issue); - - _update_issues_list(); - - MonoBottomPanel::get_singleton()->raise_build_tab(this); -} - -void MonoBuildTab::restart_build() { - - ERR_FAIL_COND(!build_exited); - GodotSharpBuilds::get_singleton()->restart_build(this); -} - -void MonoBuildTab::stop_build() { - - ERR_FAIL_COND(build_exited); - GodotSharpBuilds::get_singleton()->stop_build(this); -} - -void MonoBuildTab::_issue_activated(int p_idx) { - - ERR_FAIL_INDEX(p_idx, issues_list->get_item_count()); - - // Get correct issue idx from issue list - int issue_idx = this->issues_list->get_item_metadata(p_idx); - - ERR_FAIL_INDEX(issue_idx, issues.size()); - - const BuildIssue &issue = issues[issue_idx]; - - if (issue.project_file.empty() && issue.file.empty()) - return; - - String project_dir = issue.project_file.length() ? issue.project_file.get_base_dir() : build_info.solution.get_base_dir(); - - String file = project_dir.simplify_path().plus_file(issue.file.simplify_path()); - - if (!FileAccess::exists(file)) - return; - - file = ProjectSettings::get_singleton()->localize_path(file); - - if (file.begins_with("res://")) { - Ref<Script> script = ResourceLoader::load(file, CSharpLanguage::get_singleton()->get_type()); - - if (script.is_valid() && ScriptEditor::get_singleton()->edit(script, issue.line, issue.column)) { - EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); - } - } -} - -void MonoBuildTab::_bind_methods() { - - ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated); -} - -MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) : - build_exited(false), - issues_list(memnew(ItemList)), - error_count(0), - warning_count(0), - errors_visible(true), - warnings_visible(true), - logs_dir(p_logs_dir), - build_info(p_build_info) { - issues_list->set_v_size_flags(SIZE_EXPAND_FILL); - issues_list->connect("item_activated", this, "_issue_activated"); - add_child(issues_list); -} diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h deleted file mode 100644 index 03240e9a13..0000000000 --- a/modules/mono/editor/mono_bottom_panel.h +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************/ -/* mono_bottom_panel.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MONO_BOTTOM_PANEL_H -#define MONO_BOTTOM_PANEL_H - -#include "editor/editor_node.h" -#include "scene/gui/control.h" - -#include "mono_build_info.h" - -class MonoBuildTab; - -class MonoBottomPanel : public VBoxContainer { - - GDCLASS(MonoBottomPanel, VBoxContainer) - - EditorNode *editor; - - TabContainer *panel_tabs; - - VBoxContainer *panel_builds_tab; - - ItemList *build_tabs_list; - TabContainer *build_tabs; - - Button *warnings_btn; - Button *errors_btn; - Button *view_log_btn; - - void _update_build_tabs_list(); - - void _build_tabs_item_selected(int p_idx); - void _build_tabs_nothing_selected(); - - void _warnings_toggled(bool p_pressed); - void _errors_toggled(bool p_pressed); - - void _build_project_pressed(); - void _view_log_pressed(); - - static MonoBottomPanel *singleton; - -protected: - void _notification(int p_what); - - static void _bind_methods(); - -public: - _FORCE_INLINE_ static MonoBottomPanel *get_singleton() { return singleton; } - - void add_build_tab(MonoBuildTab *p_build_tab); - void raise_build_tab(MonoBuildTab *p_build_tab); - - void show_build_tab(); - - MonoBottomPanel(EditorNode *p_editor = NULL); - ~MonoBottomPanel(); -}; - -class MonoBuildTab : public VBoxContainer { - - GDCLASS(MonoBuildTab, VBoxContainer) - -public: - enum BuildResult { - RESULT_ERROR, - RESULT_SUCCESS - }; - - struct BuildIssue { - bool warning; - String file; - int line; - int column; - String code; - String message; - String project_file; - }; - -private: - friend class MonoBottomPanel; - - bool build_exited; - BuildResult build_result; - - Vector<BuildIssue> issues; - ItemList *issues_list; - - int error_count; - int warning_count; - - bool errors_visible; - bool warnings_visible; - - String logs_dir; - - MonoBuildInfo build_info; - - void _load_issues_from_file(const String &p_csv_file); - void _update_issues_list(); - - void _issue_activated(int p_idx); - -protected: - static void _bind_methods(); - -public: - Ref<Texture> get_icon_texture() const; - - MonoBuildInfo get_build_info(); - - void on_build_start(); - void on_build_exit(BuildResult result); - void on_build_exec_failed(const String &p_cause); - - void restart_build(); - void stop_build(); - - MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir); -}; - -#endif // MONO_BOTTOM_PANEL_H diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 9042bff74a..dfb652a7aa 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* script_class_parser.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #include "script_class_parser.h" #include "core/map.h" @@ -236,6 +266,20 @@ Error ScriptClassParser::_skip_generic_type_params() { if (tk == TK_IDENTIFIER) { tk = get_token(); + // Type specifications can end with "?" to denote nullable types, such as IList<int?> + if (tk == TK_SYMBOL) { + tk = get_token(); + if (value.operator String() != "?") { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found unexpected symbol '" + value + "'"; + error = true; + return ERR_PARSE_ERROR; + } + if (tk != TK_OP_GREATER && tk != TK_COMMA) { + error_str = "Nullable type symbol '?' is only allowed after an identifier, but found " + get_token_name(tk) + " next."; + error = true; + return ERR_PARSE_ERROR; + } + } if (tk == TK_PERIOD) { while (true) { @@ -292,6 +336,15 @@ Error ScriptClassParser::_parse_type_full_name(String &r_full_name) { r_full_name += String(value); + if (code[idx] == '<') { + idx++; + + // We don't mind if the base is generic, but we skip it any ways since this information is not needed + Error err = _skip_generic_type_params(); + if (err) + return err; + } + if (code[idx] != '.') // We only want to take the next token if it's a period return OK; @@ -314,22 +367,12 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { Token tk = get_token(); - bool generic = false; - if (tk == TK_OP_LESS) { - Error err = _skip_generic_type_params(); - if (err) - return err; - // We don't add it to the base list if it's generic - generic = true; - tk = get_token(); - } - if (tk == TK_COMMA) { - Error err = _parse_class_base(r_base); + err = _parse_class_base(r_base); if (err) return err; } else if (tk == TK_IDENTIFIER && String(value) == "where") { - Error err = _parse_type_constraints(); + err = _parse_type_constraints(); if (err) { return err; } @@ -343,9 +386,7 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { return ERR_PARSE_ERROR; } - if (!generic) { - r_base.push_back(name); - } + r_base.push_back(name); return OK; } @@ -537,7 +578,7 @@ Error ScriptClassParser::parse(const String &p_code) { if (full_name.length()) full_name += "."; full_name += class_decl.name; - OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8()); + OS::get_singleton()->print("Ignoring generic class declaration: %s\n", class_decl.name.utf8().get_data()); } } } else if (tk == TK_IDENTIFIER && String(value) == "struct") { diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h index 184adebaf2..39003336ac 100644 --- a/modules/mono/editor/script_class_parser.h +++ b/modules/mono/editor/script_class_parser.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* script_class_parser.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #ifndef SCRIPT_CLASS_PARSER_H #define SCRIPT_CLASS_PARSER_H diff --git a/modules/mono/glue/Managed/.gitignore b/modules/mono/glue/Managed/.gitignore new file mode 100644 index 0000000000..146421cac8 --- /dev/null +++ b/modules/mono/glue/Managed/.gitignore @@ -0,0 +1,2 @@ +# Generated Godot API solution folder +Generated diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs index 33b2b46712..a2ebbc0736 100644 --- a/modules/mono/glue/Managed/Files/AABB.cs +++ b/modules/mono/glue/Managed/Files/AABB.cs @@ -414,6 +414,21 @@ namespace Godot _position = position; _size = size; } + public AABB(Vector3 position, real_t width, real_t height, real_t depth) + { + _position = position; + _size = new Vector3(width, height, depth); + } + public AABB(real_t x, real_t y, real_t z, Vector3 size) + { + _position = new Vector3(x, y, z); + _size = size; + } + public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) + { + _position = new Vector3(x, y, z); + _size = new Vector3(width, height, depth); + } public static bool operator ==(AABB left, AABB right) { diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index d5a35d7ae0..0e7b0362e0 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -28,7 +28,7 @@ namespace Godot.Collections } } - public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + public class Array : IList, IDisposable { ArraySafeHandle safeHandle; bool disposed = false; @@ -38,6 +38,14 @@ namespace Godot.Collections safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); } + public Array(IEnumerable collection) : this() + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + MarshalUtils.EnumerableToArray(collection, GetPtr()); + } + internal Array(ArraySafeHandle handle) { safeHandle = handle; @@ -50,9 +58,19 @@ namespace Godot.Collections internal IntPtr GetPtr() { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + return safeHandle.DangerousGetHandle(); } + public Error Resize(int newSize) + { + return godot_icall_Array_Resize(GetPtr(), newSize); + } + + // IDisposable + public void Dispose() { if (disposed) @@ -67,62 +85,55 @@ namespace Godot.Collections disposed = true; } + // IList + + public bool IsReadOnly => false; + + public bool IsFixedSize => false; + public object this[int index] { - get - { - return godot_icall_Array_At(GetPtr(), index); - } - set - { - godot_icall_Array_SetAt(GetPtr(), index, value); - } + get => godot_icall_Array_At(GetPtr(), index); + set => godot_icall_Array_SetAt(GetPtr(), index, value); } - public int Count - { - get - { - return godot_icall_Array_Count(GetPtr()); - } - } + public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); - public bool IsReadOnly - { - get - { - return false; - } - } + public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); - public void Add(object item) - { - godot_icall_Array_Add(GetPtr(), item); - } + public void Clear() => godot_icall_Array_Clear(GetPtr()); - public void Clear() - { - godot_icall_Array_Clear(GetPtr()); - } + public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); - public bool Contains(object item) - { - return godot_icall_Array_Contains(GetPtr(), item); - } + public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); + + public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); - public void CopyTo(object[] array, int arrayIndex) + public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index); + + // ICollection + + public int Count => godot_icall_Array_Count(GetPtr()); + + public object SyncRoot => this; + + public bool IsSynchronized => false; + + public void CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + godot_icall_Array_CopyTo(GetPtr(), array, index); } - public IEnumerator<object> GetEnumerator() + // IEnumerable + + public IEnumerator GetEnumerator() { int count = Count; @@ -132,29 +143,9 @@ namespace Godot.Collections } } - public int IndexOf(object item) + public override string ToString() { - return godot_icall_Array_IndexOf(GetPtr(), item); - } - - public void Insert(int index, object item) - { - godot_icall_Array_Insert(GetPtr(), index, item); - } - - public bool Remove(object item) - { - return godot_icall_Array_Remove(GetPtr(), item); - } - - public void RemoveAt(int index) - { - godot_icall_Array_RemoveAt(GetPtr(), index); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); + return godot_icall_Array_ToString(GetPtr()); } [MethodImpl(MethodImplOptions.InternalCall)] @@ -176,7 +167,7 @@ namespace Godot.Collections internal extern static int godot_icall_Array_Count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + internal extern static int godot_icall_Array_Add(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Clear(IntPtr ptr); @@ -185,7 +176,7 @@ namespace Godot.Collections internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); @@ -200,7 +191,13 @@ namespace Godot.Collections internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Array_ToString(IntPtr ptr); } public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> @@ -220,6 +217,14 @@ namespace Godot.Collections objectArray = new Array(); } + public Array(IEnumerable<T> collection) + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + objectArray = new Array(collection); + } + public Array(Array array) { objectArray = array; @@ -235,11 +240,23 @@ namespace Godot.Collections objectArray = new Array(handle); } + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + public static explicit operator Array(Array<T> from) { return from.objectArray; } + public Error Resize(int newSize) + { + return objectArray.Resize(newSize); + } + + // IList<T> + public T this[int index] { get @@ -252,6 +269,23 @@ namespace Godot.Collections } } + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + // ICollection<T> + public int Count { get @@ -306,6 +340,13 @@ namespace Godot.Collections } } + public bool Remove(T item) + { + return Array.godot_icall_Array_Remove(GetPtr(), item); + } + + // IEnumerable<T> + public IEnumerator<T> GetEnumerator() { int count = objectArray.Count; @@ -316,34 +357,11 @@ namespace Godot.Collections } } - public int IndexOf(T item) - { - return objectArray.IndexOf(item); - } - - public void Insert(int index, T item) - { - objectArray.Insert(index, item); - } - - public bool Remove(T item) - { - return objectArray.Remove(item); - } - - public void RemoveAt(int index) - { - objectArray.RemoveAt(index); - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - internal IntPtr GetPtr() - { - return objectArray.GetPtr(); - } + public override string ToString() => objectArray.ToString(); } } diff --git a/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs index 2398e10135..1bf6d5199a 100644 --- a/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs @@ -2,27 +2,27 @@ using System; namespace Godot { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class RemoteAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class SyncAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class MasterAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class PuppetAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class SlaveAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class RemoteSyncAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class MasterSyncAttribute : Attribute {} - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] public class PuppetSyncAttribute : Attribute {} } diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index b318d96bb9..9cc31a0557 100644 --- a/modules/mono/glue/Managed/Files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -45,74 +45,119 @@ namespace Godot new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) }; + // NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally. + + /// <summary> + /// Returns the basis matrix’s x vector. + /// This is equivalent to <see cref="Column0"/>. + /// </summary> public Vector3 x { - get { return GetAxis(0); } - set { SetAxis(0, value); } + get => Column0; + set => Column0 = value; } + /// <summary> + /// Returns the basis matrix’s y vector. + /// This is equivalent to <see cref="Column1"/>. + /// </summary> public Vector3 y { - get { return GetAxis(1); } - set { SetAxis(1, value); } + + get => Column1; + set => Column1 = value; } + /// <summary> + /// Returns the basis matrix’s z vector. + /// This is equivalent to <see cref="Column2"/>. + /// </summary> public Vector3 z { - get { return GetAxis(2); } - set { SetAxis(2, value); } + + get => Column2; + set => Column2 = value; } - private Vector3 _x; - private Vector3 _y; - private Vector3 _z; + public Vector3 Row0; + public Vector3 Row1; + public Vector3 Row2; - public static Basis Identity + public Vector3 Column0 + { + get => new Vector3(Row0.x, Row1.x, Row2.x); + set + { + this.Row0.x = value.x; + this.Row1.x = value.y; + this.Row2.x = value.z; + } + } + public Vector3 Column1 + { + get => new Vector3(Row0.y, Row1.y, Row2.y); + set + { + this.Row0.y = value.x; + this.Row1.y = value.y; + this.Row2.y = value.z; + } + } + public Vector3 Column2 { - get { return identity; } + get => new Vector3(Row0.z, Row1.z, Row2.z); + set + { + this.Row0.z = value.x; + this.Row1.z = value.y; + this.Row2.z = value.z; + } } + public static Basis Identity => identity; + public Vector3 Scale { get { - return new Vector3 + real_t detSign = Mathf.Sign(Determinant()); + return detSign * new Vector3 ( - new Vector3(this[0, 0], this[1, 0], this[2, 0]).Length(), - new Vector3(this[0, 1], this[1, 1], this[2, 1]).Length(), - new Vector3(this[0, 2], this[1, 2], this[2, 2]).Length() + new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(), + new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(), + new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length() ); } } - public Vector3 this[int index] + public Vector3 this[int columnIndex] { get { - switch (index) + switch (columnIndex) { case 0: - return _x; + return Column0; case 1: - return _y; + return Column1; case 2: - return _z; + return Column2; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (columnIndex) { case 0: - _x = value; + Column0 = value; return; case 1: - _y = value; + Column1 = value; return; case 2: - _z = value; + Column2 = value; return; default: throw new IndexOutOfRangeException(); @@ -120,51 +165,53 @@ namespace Godot } } - public real_t this[int index, int axis] + public real_t this[int columnIndex, int rowIndex] { get { - switch (index) + switch (columnIndex) { case 0: - return _x[axis]; + return Column0[rowIndex]; case 1: - return _y[axis]; + return Column1[rowIndex]; case 2: - return _z[axis]; + return Column2[rowIndex]; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (columnIndex) { case 0: - _x[axis] = value; + { + var column0 = Column0; + column0[rowIndex] = value; + Column0 = column0; return; + } case 1: - _y[axis] = value; + { + var column1 = Column1; + column1[rowIndex] = value; + Column1 = column1; return; + } case 2: - _z[axis] = value; + { + var column2 = Column2; + column2[rowIndex] = value; + Column2 = column2; return; + } default: throw new IndexOutOfRangeException(); } } } - internal static Basis CreateFromAxes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) - { - return new Basis - ( - xAxis.x, yAxis.x, zAxis.x, - xAxis.y, yAxis.y, zAxis.y, - xAxis.z, yAxis.z, zAxis.z - ); - } - internal Quat RotationQuat() { Basis orthonormalizedBasis = Orthonormalized(); @@ -191,29 +238,19 @@ namespace Godot private void SetDiagonal(Vector3 diagonal) { - _x = new Vector3(diagonal.x, 0, 0); - _y = new Vector3(0, diagonal.y, 0); - _z = new Vector3(0, 0, diagonal.z); + Row0 = new Vector3(diagonal.x, 0, 0); + Row1 = new Vector3(0, diagonal.y, 0); + Row2 = new Vector3(0, 0, diagonal.z); } public real_t Determinant() { - return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - - this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) + - this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]); - } + real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; + real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2]; + real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0]; - public Vector3 GetAxis(int axis) - { - return new Vector3(this[0, axis], this[1, axis], this[2, axis]); - } - - public void SetAxis(int axis, Vector3 value) - { - this[0, axis] = value.x; - this[1, axis] = value.y; - this[2, axis] = value.z; + return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; } public Vector3 GetEuler() @@ -223,32 +260,80 @@ namespace Godot Vector3 euler; euler.z = 0.0f; - real_t mxy = m[1, 2]; + real_t mzy = m.Row1[2]; - - if (mxy < 1.0f) + if (mzy < 1.0f) { - if (mxy > -1.0f) + if (mzy > -1.0f) { - euler.x = Mathf.Asin(-mxy); - euler.y = Mathf.Atan2(m[0, 2], m[2, 2]); - euler.z = Mathf.Atan2(m[1, 0], m[1, 1]); + euler.x = Mathf.Asin(-mzy); + euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]); + euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]); } else { euler.x = Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); } } else { euler.x = -Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); } return euler; } + public Vector3 GetRow(int index) + { + switch (index) + { + case 0: + return Row0; + case 1: + return Row1; + case 2: + return Row2; + default: + throw new IndexOutOfRangeException(); + } + } + + public void SetRow(int index, Vector3 value) + { + switch (index) + { + case 0: + Row0 = value; + return; + case 1: + Row1 = value; + return; + case 2: + Row2 = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + + public Vector3 GetColumn(int index) + { + return this[index]; + } + + public void SetColumn(int index, Vector3 value) + { + this[index] = value; + } + + [Obsolete("GetAxis is deprecated. Use GetColumn instead.")] + public Vector3 GetAxis(int axis) + { + return new Vector3(this.Row0[axis], this.Row1[axis], this.Row2[axis]); + } + public int GetOrthogonalIndex() { var orth = this; @@ -257,7 +342,9 @@ namespace Godot { for (int j = 0; j < 3; j++) { - real_t v = orth[i, j]; + var row = orth.GetRow(i); + + real_t v = row[j]; if (v > 0.5f) v = 1.0f; @@ -266,7 +353,9 @@ namespace Godot else v = 0f; - orth[i, j] = v; + row[j] = v; + + orth.SetRow(i, row); } } @@ -281,57 +370,45 @@ namespace Godot public Basis Inverse() { - var inv = this; - - real_t[] co = { - inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1], - inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2], - inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0] - }; + real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; + real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2]; + real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0]; - real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; + real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; if (det == 0) - { - return new Basis - ( - real_t.NaN, real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN, real_t.NaN - ); - } + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); + + real_t detInv = 1.0f / det; - real_t s = 1.0f / det; + real_t cofac01 = Row0[2] * Row2[1] - Row0[1] * Row2[2]; + real_t cofac02 = Row0[1] * Row1[2] - Row0[2] * Row1[1]; + real_t cofac11 = Row0[0] * Row2[2] - Row0[2] * Row2[0]; + real_t cofac12 = Row0[2] * Row1[0] - Row0[0] * Row1[2]; + real_t cofac21 = Row0[1] * Row2[0] - Row0[0] * Row2[1]; + real_t cofac22 = Row0[0] * Row1[1] - Row0[1] * Row1[0]; - inv = new Basis + return new Basis ( - co[0] * s, - inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s, - inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s, - co[1] * s, - inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s, - inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s, - co[2] * s, - inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s, - inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s + cofac00 * detInv, cofac01 * detInv, cofac02 * detInv, + cofac10 * detInv, cofac11 * detInv, cofac12 * detInv, + cofac20 * detInv, cofac21 * detInv, cofac22 * detInv ); - - return inv; } public Basis Orthonormalized() { - Vector3 xAxis = GetAxis(0); - Vector3 yAxis = GetAxis(1); - Vector3 zAxis = GetAxis(2); + Vector3 column0 = GetColumn(0); + Vector3 column1 = GetColumn(1); + Vector3 column2 = GetColumn(2); - xAxis.Normalize(); - yAxis = yAxis - xAxis * xAxis.Dot(yAxis); - yAxis.Normalize(); - zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis); - zAxis.Normalize(); + column0.Normalize(); + column1 = column1 - column0 * column0.Dot(column1); + column1.Normalize(); + column2 = column2 - column0 * column0.Dot(column2) - column1 * column1.Dot(column2); + column2.Normalize(); - return CreateFromAxes(xAxis, yAxis, zAxis); + return new Basis(column0, column1, column2); } public Basis Rotated(Vector3 axis, real_t phi) @@ -341,51 +418,43 @@ namespace Godot public Basis Scaled(Vector3 scale) { - var m = this; - - m[0, 0] *= scale.x; - m[0, 1] *= scale.x; - m[0, 2] *= scale.x; - m[1, 0] *= scale.y; - m[1, 1] *= scale.y; - m[1, 2] *= scale.y; - m[2, 0] *= scale.z; - m[2, 1] *= scale.z; - m[2, 2] *= scale.z; - - return m; + var b = this; + b.Row0 *= scale.x; + b.Row1 *= scale.y; + b.Row2 *= scale.z; + return b; } public real_t Tdotx(Vector3 with) { - return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; + return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2]; } public real_t Tdoty(Vector3 with) { - return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; + return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2]; } public real_t Tdotz(Vector3 with) { - return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; + return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2]; } public Basis Transposed() { var tr = this; - real_t temp = tr[0, 1]; - tr[0, 1] = tr[1, 0]; - tr[1, 0] = temp; + real_t temp = tr.Row0[1]; + tr.Row0[1] = tr.Row1[0]; + tr.Row1[0] = temp; - temp = tr[0, 2]; - tr[0, 2] = tr[2, 0]; - tr[2, 0] = temp; + temp = tr.Row0[2]; + tr.Row0[2] = tr.Row2[0]; + tr.Row2[0] = temp; - temp = tr[1, 2]; - tr[1, 2] = tr[2, 1]; - tr[2, 1] = temp; + temp = tr.Row1[2]; + tr.Row1[2] = tr.Row2[1]; + tr.Row2[1] = temp; return tr; } @@ -394,9 +463,9 @@ namespace Godot { return new Vector3 ( - this[0].Dot(v), - this[1].Dot(v), - this[2].Dot(v) + this.Row0.Dot(v), + this.Row1.Dot(v), + this.Row2.Dot(v) ); } @@ -404,60 +473,60 @@ namespace Godot { return new Vector3 ( - this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z, - this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z, - this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z + this.Row0[0] * v.x + this.Row1[0] * v.y + this.Row2[0] * v.z, + this.Row0[1] * v.x + this.Row1[1] * v.y + this.Row2[1] * v.z, + this.Row0[2] * v.x + this.Row1[2] * v.y + this.Row2[2] * v.z ); } public Quat Quat() { - real_t trace = _x[0] + _y[1] + _z[2]; + real_t trace = Row0[0] + Row1[1] + Row2[2]; if (trace > 0.0f) { real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_z[1] - _y[2]) * inv_s, - (_x[2] - _z[0]) * inv_s, - (_y[0] - _x[1]) * inv_s, + (Row2[1] - Row1[2]) * inv_s, + (Row0[2] - Row2[0]) * inv_s, + (Row1[0] - Row0[1]) * inv_s, s * 0.25f ); } - if (_x[0] > _y[1] && _x[0] > _z[2]) + if (Row0[0] > Row1[1] && Row0[0] > Row2[2]) { - real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( s * 0.25f, - (_x[1] + _y[0]) * inv_s, - (_x[2] + _z[0]) * inv_s, - (_z[1] - _y[2]) * inv_s + (Row0[1] + Row1[0]) * inv_s, + (Row0[2] + Row2[0]) * inv_s, + (Row2[1] - Row1[2]) * inv_s ); } - if (_y[1] > _z[2]) + if (Row1[1] > Row2[2]) { - real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_x[1] + _y[0]) * inv_s, + (Row0[1] + Row1[0]) * inv_s, s * 0.25f, - (_y[2] + _z[1]) * inv_s, - (_x[2] - _z[0]) * inv_s + (Row1[2] + Row2[1]) * inv_s, + (Row0[2] - Row2[0]) * inv_s ); } else { - real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_x[2] + _z[0]) * inv_s, - (_y[2] + _z[1]) * inv_s, + (Row0[2] + Row2[0]) * inv_s, + (Row1[2] + Row2[1]) * inv_s, s * 0.25f, - (_y[0] - _x[1]) * inv_s + (Row1[0] - Row0[1]) * inv_s ); } } @@ -479,9 +548,9 @@ namespace Godot real_t yz = quat.y * zs; real_t zz = quat.z * zs; - _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); - _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); - _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + Row0 = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + Row1 = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); } public Basis(Vector3 euler) @@ -506,59 +575,58 @@ namespace Godot public Basis(Vector3 axis, real_t phi) { - var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - + Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); real_t cosine = Mathf.Cos(phi); + Row0.x = axisSq.x + cosine * (1.0f - axisSq.x); + Row1.y = axisSq.y + cosine * (1.0f - axisSq.y); + Row2.z = axisSq.z + cosine * (1.0f - axisSq.z); + real_t sine = Mathf.Sin(phi); + real_t t = 1.0f - cosine; - _x = new Vector3 - ( - axis_sq.x + cosine * (1.0f - axis_sq.x), - axis.x * axis.y * (1.0f - cosine) - axis.z * sine, - axis.z * axis.x * (1.0f - cosine) + axis.y * sine - ); + real_t xyzt = axis.x * axis.y * t; + real_t zyxs = axis.z * sine; + Row0.y = xyzt - zyxs; + Row1.x = xyzt + zyxs; - _y = new Vector3 - ( - axis.x * axis.y * (1.0f - cosine) + axis.z * sine, - axis_sq.y + cosine * (1.0f - axis_sq.y), - axis.y * axis.z * (1.0f - cosine) - axis.x * sine - ); + xyzt = axis.x * axis.z * t; + zyxs = axis.y * sine; + Row0.z = xyzt + zyxs; + Row2.x = xyzt - zyxs; - _z = new Vector3 - ( - axis.z * axis.x * (1.0f - cosine) - axis.y * sine, - axis.y * axis.z * (1.0f - cosine) + axis.x * sine, - axis_sq.z + cosine * (1.0f - axis_sq.z) - ); + xyzt = axis.y * axis.z * t; + zyxs = axis.x * sine; + Row1.z = xyzt - zyxs; + Row2.y = xyzt + zyxs; } - public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + public Basis(Vector3 column0, Vector3 column1, Vector3 column2) { - _x = new Vector3(xAxis.x, yAxis.x, zAxis.x); - _y = new Vector3(xAxis.y, yAxis.y, zAxis.y); - _z = new Vector3(xAxis.z, yAxis.z, zAxis.z); + Row0 = new Vector3(column0.x, column1.x, column2.x); + Row1 = new Vector3(column0.y, column1.y, column2.y); + Row2 = new Vector3(column0.z, column1.z, column2.z); // Same as: - // SetAxis(0, xAxis); - // SetAxis(1, yAxis); - // SetAxis(2, zAxis); - // We need to assign the struct fields so we can't do that... + // Column0 = column0; + // Column1 = column1; + // Column2 = column2; + // We need to assign the struct fields here first so we can't do it that way... } - internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) + // Arguments are named such that xy is equal to calling x.y + internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) { - _x = new Vector3(xx, xy, xz); - _y = new Vector3(yx, yy, yz); - _z = new Vector3(zx, zy, zz); + Row0 = new Vector3(xx, yx, zx); + Row1 = new Vector3(xy, yy, zy); + Row2 = new Vector3(xz, yz, zz); } public static Basis operator *(Basis left, Basis right) { return new Basis ( - right.Tdotx(left[0]), right.Tdoty(left[0]), right.Tdotz(left[0]), - right.Tdotx(left[1]), right.Tdoty(left[1]), right.Tdotz(left[1]), - right.Tdotx(left[2]), right.Tdoty(left[2]), right.Tdotz(left[2]) + right.Tdotx(left.Row0), right.Tdoty(left.Row0), right.Tdotz(left.Row0), + right.Tdotx(left.Row1), right.Tdoty(left.Row1), right.Tdotz(left.Row1), + right.Tdotx(left.Row2), right.Tdoty(left.Row2), right.Tdotz(left.Row2) ); } @@ -584,21 +652,21 @@ namespace Godot public bool Equals(Basis other) { - return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]); + return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); } public override int GetHashCode() { - return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode(); + return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1}, {2})", new object[] { - _x.ToString(), - _y.ToString(), - _z.ToString() + Row0.ToString(), + Row1.ToString(), + Row2.ToString() }); } @@ -606,9 +674,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - _x.ToString(format), - _y.ToString(format), - _z.ToString(format) + Row0.ToString(format), + Row1.ToString(format), + Row2.ToString(format) }); } } diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs index fc5bb010a9..84ff19fc54 100644 --- a/modules/mono/glue/Managed/Files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -104,17 +104,26 @@ namespace Godot } } - private static readonly Color black = new Color(0f, 0f, 0f); - - public Color Black + public static Color ColorN(string name, float alpha = 1f) { - get + name = name.Replace(" ", String.Empty); + name = name.Replace("-", String.Empty); + name = name.Replace("_", String.Empty); + name = name.Replace("'", String.Empty); + name = name.Replace(".", String.Empty); + name = name.ToLower(); + + if (!Colors.namedColors.ContainsKey(name)) { - return black; + throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}"); } + + Color color = Colors.namedColors[name]; + color.a = alpha; + return color; } - public float this [int index] + public float this[int index] { get { @@ -159,7 +168,7 @@ namespace Godot int max = Mathf.Max(color.r8, Mathf.Max(color.g8, color.b8)); int min = Mathf.Min(color.r8, Mathf.Min(color.g8, color.b8)); - float delta = max - min; + int delta = max - min; if (delta == 0) { @@ -380,7 +389,7 @@ namespace Godot return txt; } - // Constructors + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; @@ -582,11 +591,11 @@ namespace Godot public static bool operator <(Color left, Color right) { - if (left.r == right.r) + if (Mathf.IsEqualApprox(left.r, right.r)) { - if (left.g == right.g) + if (Mathf.IsEqualApprox(left.g, right.g)) { - if (left.b == right.b) + if (Mathf.IsEqualApprox(left.b, right.b)) return left.a < right.a; return left.b < right.b; } @@ -599,11 +608,11 @@ namespace Godot public static bool operator >(Color left, Color right) { - if (left.r == right.r) + if (Mathf.IsEqualApprox(left.r, right.r)) { - if (left.g == right.g) + if (Mathf.IsEqualApprox(left.g, right.g)) { - if (left.b == right.b) + if (Mathf.IsEqualApprox(left.b, right.b)) return left.a > right.a; return left.b > right.b; } @@ -626,7 +635,7 @@ namespace Godot public bool Equals(Color other) { - return r == other.r && g == other.g && b == other.b && a == other.a; + return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Colors.cs b/modules/mono/glue/Managed/Files/Colors.cs new file mode 100644 index 0000000000..bc2a1a3bd7 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Colors.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; + +namespace Godot +{ + public static class Colors + { + // Color names and values are derived from core/color_names.inc + internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> { + {"aliceblue", new Color(0.94f, 0.97f, 1.00f)}, + {"antiquewhite", new Color(0.98f, 0.92f, 0.84f)}, + {"aqua", new Color(0.00f, 1.00f, 1.00f)}, + {"aquamarine", new Color(0.50f, 1.00f, 0.83f)}, + {"azure", new Color(0.94f, 1.00f, 1.00f)}, + {"beige", new Color(0.96f, 0.96f, 0.86f)}, + {"bisque", new Color(1.00f, 0.89f, 0.77f)}, + {"black", new Color(0.00f, 0.00f, 0.00f)}, + {"blanchedalmond", new Color(1.00f, 0.92f, 0.80f)}, + {"blue", new Color(0.00f, 0.00f, 1.00f)}, + {"blueviolet", new Color(0.54f, 0.17f, 0.89f)}, + {"brown", new Color(0.65f, 0.16f, 0.16f)}, + {"burlywood", new Color(0.87f, 0.72f, 0.53f)}, + {"cadetblue", new Color(0.37f, 0.62f, 0.63f)}, + {"chartreuse", new Color(0.50f, 1.00f, 0.00f)}, + {"chocolate", new Color(0.82f, 0.41f, 0.12f)}, + {"coral", new Color(1.00f, 0.50f, 0.31f)}, + {"cornflower", new Color(0.39f, 0.58f, 0.93f)}, + {"cornsilk", new Color(1.00f, 0.97f, 0.86f)}, + {"crimson", new Color(0.86f, 0.08f, 0.24f)}, + {"cyan", new Color(0.00f, 1.00f, 1.00f)}, + {"darkblue", new Color(0.00f, 0.00f, 0.55f)}, + {"darkcyan", new Color(0.00f, 0.55f, 0.55f)}, + {"darkgoldenrod", new Color(0.72f, 0.53f, 0.04f)}, + {"darkgray", new Color(0.66f, 0.66f, 0.66f)}, + {"darkgreen", new Color(0.00f, 0.39f, 0.00f)}, + {"darkkhaki", new Color(0.74f, 0.72f, 0.42f)}, + {"darkmagenta", new Color(0.55f, 0.00f, 0.55f)}, + {"darkolivegreen", new Color(0.33f, 0.42f, 0.18f)}, + {"darkorange", new Color(1.00f, 0.55f, 0.00f)}, + {"darkorchid", new Color(0.60f, 0.20f, 0.80f)}, + {"darkred", new Color(0.55f, 0.00f, 0.00f)}, + {"darksalmon", new Color(0.91f, 0.59f, 0.48f)}, + {"darkseagreen", new Color(0.56f, 0.74f, 0.56f)}, + {"darkslateblue", new Color(0.28f, 0.24f, 0.55f)}, + {"darkslategray", new Color(0.18f, 0.31f, 0.31f)}, + {"darkturquoise", new Color(0.00f, 0.81f, 0.82f)}, + {"darkviolet", new Color(0.58f, 0.00f, 0.83f)}, + {"deeppink", new Color(1.00f, 0.08f, 0.58f)}, + {"deepskyblue", new Color(0.00f, 0.75f, 1.00f)}, + {"dimgray", new Color(0.41f, 0.41f, 0.41f)}, + {"dodgerblue", new Color(0.12f, 0.56f, 1.00f)}, + {"firebrick", new Color(0.70f, 0.13f, 0.13f)}, + {"floralwhite", new Color(1.00f, 0.98f, 0.94f)}, + {"forestgreen", new Color(0.13f, 0.55f, 0.13f)}, + {"fuchsia", new Color(1.00f, 0.00f, 1.00f)}, + {"gainsboro", new Color(0.86f, 0.86f, 0.86f)}, + {"ghostwhite", new Color(0.97f, 0.97f, 1.00f)}, + {"gold", new Color(1.00f, 0.84f, 0.00f)}, + {"goldenrod", new Color(0.85f, 0.65f, 0.13f)}, + {"gray", new Color(0.75f, 0.75f, 0.75f)}, + {"green", new Color(0.00f, 1.00f, 0.00f)}, + {"greenyellow", new Color(0.68f, 1.00f, 0.18f)}, + {"honeydew", new Color(0.94f, 1.00f, 0.94f)}, + {"hotpink", new Color(1.00f, 0.41f, 0.71f)}, + {"indianred", new Color(0.80f, 0.36f, 0.36f)}, + {"indigo", new Color(0.29f, 0.00f, 0.51f)}, + {"ivory", new Color(1.00f, 1.00f, 0.94f)}, + {"khaki", new Color(0.94f, 0.90f, 0.55f)}, + {"lavender", new Color(0.90f, 0.90f, 0.98f)}, + {"lavenderblush", new Color(1.00f, 0.94f, 0.96f)}, + {"lawngreen", new Color(0.49f, 0.99f, 0.00f)}, + {"lemonchiffon", new Color(1.00f, 0.98f, 0.80f)}, + {"lightblue", new Color(0.68f, 0.85f, 0.90f)}, + {"lightcoral", new Color(0.94f, 0.50f, 0.50f)}, + {"lightcyan", new Color(0.88f, 1.00f, 1.00f)}, + {"lightgoldenrod", new Color(0.98f, 0.98f, 0.82f)}, + {"lightgray", new Color(0.83f, 0.83f, 0.83f)}, + {"lightgreen", new Color(0.56f, 0.93f, 0.56f)}, + {"lightpink", new Color(1.00f, 0.71f, 0.76f)}, + {"lightsalmon", new Color(1.00f, 0.63f, 0.48f)}, + {"lightseagreen", new Color(0.13f, 0.70f, 0.67f)}, + {"lightskyblue", new Color(0.53f, 0.81f, 0.98f)}, + {"lightslategray", new Color(0.47f, 0.53f, 0.60f)}, + {"lightsteelblue", new Color(0.69f, 0.77f, 0.87f)}, + {"lightyellow", new Color(1.00f, 1.00f, 0.88f)}, + {"lime", new Color(0.00f, 1.00f, 0.00f)}, + {"limegreen", new Color(0.20f, 0.80f, 0.20f)}, + {"linen", new Color(0.98f, 0.94f, 0.90f)}, + {"magenta", new Color(1.00f, 0.00f, 1.00f)}, + {"maroon", new Color(0.69f, 0.19f, 0.38f)}, + {"mediumaquamarine", new Color(0.40f, 0.80f, 0.67f)}, + {"mediumblue", new Color(0.00f, 0.00f, 0.80f)}, + {"mediumorchid", new Color(0.73f, 0.33f, 0.83f)}, + {"mediumpurple", new Color(0.58f, 0.44f, 0.86f)}, + {"mediumseagreen", new Color(0.24f, 0.70f, 0.44f)}, + {"mediumslateblue", new Color(0.48f, 0.41f, 0.93f)}, + {"mediumspringgreen", new Color(0.00f, 0.98f, 0.60f)}, + {"mediumturquoise", new Color(0.28f, 0.82f, 0.80f)}, + {"mediumvioletred", new Color(0.78f, 0.08f, 0.52f)}, + {"midnightblue", new Color(0.10f, 0.10f, 0.44f)}, + {"mintcream", new Color(0.96f, 1.00f, 0.98f)}, + {"mistyrose", new Color(1.00f, 0.89f, 0.88f)}, + {"moccasin", new Color(1.00f, 0.89f, 0.71f)}, + {"navajowhite", new Color(1.00f, 0.87f, 0.68f)}, + {"navyblue", new Color(0.00f, 0.00f, 0.50f)}, + {"oldlace", new Color(0.99f, 0.96f, 0.90f)}, + {"olive", new Color(0.50f, 0.50f, 0.00f)}, + {"olivedrab", new Color(0.42f, 0.56f, 0.14f)}, + {"orange", new Color(1.00f, 0.65f, 0.00f)}, + {"orangered", new Color(1.00f, 0.27f, 0.00f)}, + {"orchid", new Color(0.85f, 0.44f, 0.84f)}, + {"palegoldenrod", new Color(0.93f, 0.91f, 0.67f)}, + {"palegreen", new Color(0.60f, 0.98f, 0.60f)}, + {"paleturquoise", new Color(0.69f, 0.93f, 0.93f)}, + {"palevioletred", new Color(0.86f, 0.44f, 0.58f)}, + {"papayawhip", new Color(1.00f, 0.94f, 0.84f)}, + {"peachpuff", new Color(1.00f, 0.85f, 0.73f)}, + {"peru", new Color(0.80f, 0.52f, 0.25f)}, + {"pink", new Color(1.00f, 0.75f, 0.80f)}, + {"plum", new Color(0.87f, 0.63f, 0.87f)}, + {"powderblue", new Color(0.69f, 0.88f, 0.90f)}, + {"purple", new Color(0.63f, 0.13f, 0.94f)}, + {"rebeccapurple", new Color(0.40f, 0.20f, 0.60f)}, + {"red", new Color(1.00f, 0.00f, 0.00f)}, + {"rosybrown", new Color(0.74f, 0.56f, 0.56f)}, + {"royalblue", new Color(0.25f, 0.41f, 0.88f)}, + {"saddlebrown", new Color(0.55f, 0.27f, 0.07f)}, + {"salmon", new Color(0.98f, 0.50f, 0.45f)}, + {"sandybrown", new Color(0.96f, 0.64f, 0.38f)}, + {"seagreen", new Color(0.18f, 0.55f, 0.34f)}, + {"seashell", new Color(1.00f, 0.96f, 0.93f)}, + {"sienna", new Color(0.63f, 0.32f, 0.18f)}, + {"silver", new Color(0.75f, 0.75f, 0.75f)}, + {"skyblue", new Color(0.53f, 0.81f, 0.92f)}, + {"slateblue", new Color(0.42f, 0.35f, 0.80f)}, + {"slategray", new Color(0.44f, 0.50f, 0.56f)}, + {"snow", new Color(1.00f, 0.98f, 0.98f)}, + {"springgreen", new Color(0.00f, 1.00f, 0.50f)}, + {"steelblue", new Color(0.27f, 0.51f, 0.71f)}, + {"tan", new Color(0.82f, 0.71f, 0.55f)}, + {"teal", new Color(0.00f, 0.50f, 0.50f)}, + {"thistle", new Color(0.85f, 0.75f, 0.85f)}, + {"tomato", new Color(1.00f, 0.39f, 0.28f)}, + {"turquoise", new Color(0.25f, 0.88f, 0.82f)}, + {"violet", new Color(0.93f, 0.51f, 0.93f)}, + {"webgreen", new Color(0.00f, 0.50f, 0.00f)}, + {"webgray", new Color(0.50f, 0.50f, 0.50f)}, + {"webmaroon", new Color(0.50f, 0.00f, 0.00f)}, + {"webpurple", new Color(0.50f, 0.00f, 0.50f)}, + {"wheat", new Color(0.96f, 0.87f, 0.70f)}, + {"white", new Color(1.00f, 1.00f, 1.00f)}, + {"whitesmoke", new Color(0.96f, 0.96f, 0.96f)}, + {"yellow", new Color(1.00f, 1.00f, 0.00f)}, + {"yellowgreen", new Color(0.60f, 0.80f, 0.20f)}, + }; + + public static Color AliceBlue { get { return namedColors["aliceblue"]; } } + public static Color AntiqueWhite { get { return namedColors["antiquewhite"]; } } + public static Color Aqua { get { return namedColors["aqua"]; } } + public static Color Aquamarine { get { return namedColors["aquamarine"]; } } + public static Color Azure { get { return namedColors["azure"]; } } + public static Color Beige { get { return namedColors["beige"]; } } + public static Color Bisque { get { return namedColors["bisque"]; } } + public static Color Black { get { return namedColors["black"]; } } + public static Color BlanchedAlmond { get { return namedColors["blanchedalmond"]; } } + public static Color Blue { get { return namedColors["blue"]; } } + public static Color BlueViolet { get { return namedColors["blueviolet"]; } } + public static Color Brown { get { return namedColors["brown"]; } } + public static Color BurlyWood { get { return namedColors["burlywood"]; } } + public static Color CadetBlue { get { return namedColors["cadetblue"]; } } + public static Color Chartreuse { get { return namedColors["chartreuse"]; } } + public static Color Chocolate { get { return namedColors["chocolate"]; } } + public static Color Coral { get { return namedColors["coral"]; } } + public static Color Cornflower { get { return namedColors["cornflower"]; } } + public static Color Cornsilk { get { return namedColors["cornsilk"]; } } + public static Color Crimson { get { return namedColors["crimson"]; } } + public static Color Cyan { get { return namedColors["cyan"]; } } + public static Color DarkBlue { get { return namedColors["darkblue"]; } } + public static Color DarkCyan { get { return namedColors["darkcyan"]; } } + public static Color DarkGoldenrod { get { return namedColors["darkgoldenrod"]; } } + public static Color DarkGray { get { return namedColors["darkgray"]; } } + public static Color DarkGreen { get { return namedColors["darkgreen"]; } } + public static Color DarkKhaki { get { return namedColors["darkkhaki"]; } } + public static Color DarkMagenta { get { return namedColors["darkmagenta"]; } } + public static Color DarkOliveGreen { get { return namedColors["darkolivegreen"]; } } + public static Color DarkOrange { get { return namedColors["darkorange"]; } } + public static Color DarkOrchid { get { return namedColors["darkorchid"]; } } + public static Color DarkRed { get { return namedColors["darkred"]; } } + public static Color DarkSalmon { get { return namedColors["darksalmon"]; } } + public static Color DarkSeagreen { get { return namedColors["darkseagreen"]; } } + public static Color DarkSlateBlue { get { return namedColors["darkslateblue"]; } } + public static Color DarkSlateGray { get { return namedColors["darkslategray"]; } } + public static Color DarkTurquoise { get { return namedColors["darkturquoise"]; } } + public static Color DarkViolet { get { return namedColors["darkviolet"]; } } + public static Color DeepPink { get { return namedColors["deeppink"]; } } + public static Color DeepSkyBlue { get { return namedColors["deepskyblue"]; } } + public static Color DimGray { get { return namedColors["dimgray"]; } } + public static Color DodgerBlue { get { return namedColors["dodgerblue"]; } } + public static Color Firebrick { get { return namedColors["firebrick"]; } } + public static Color FloralWhite { get { return namedColors["floralwhite"]; } } + public static Color ForestGreen { get { return namedColors["forestgreen"]; } } + public static Color Fuchsia { get { return namedColors["fuchsia"]; } } + public static Color Gainsboro { get { return namedColors["gainsboro"]; } } + public static Color GhostWhite { get { return namedColors["ghostwhite"]; } } + public static Color Gold { get { return namedColors["gold"]; } } + public static Color Goldenrod { get { return namedColors["goldenrod"]; } } + public static Color Gray { get { return namedColors["gray"]; } } + public static Color Green { get { return namedColors["green"]; } } + public static Color GreenYellow { get { return namedColors["greenyellow"]; } } + public static Color Honeydew { get { return namedColors["honeydew"]; } } + public static Color HotPink { get { return namedColors["hotpink"]; } } + public static Color IndianRed { get { return namedColors["indianred"]; } } + public static Color Indigo { get { return namedColors["indigo"]; } } + public static Color Ivory { get { return namedColors["ivory"]; } } + public static Color Khaki { get { return namedColors["khaki"]; } } + public static Color Lavender { get { return namedColors["lavender"]; } } + public static Color LavenderBlush { get { return namedColors["lavenderblush"]; } } + public static Color LawnGreen { get { return namedColors["lawngreen"]; } } + public static Color LemonChiffon { get { return namedColors["lemonchiffon"]; } } + public static Color LightBlue { get { return namedColors["lightblue"]; } } + public static Color LightCoral { get { return namedColors["lightcoral"]; } } + public static Color LightCyan { get { return namedColors["lightcyan"]; } } + public static Color LightGoldenrod { get { return namedColors["lightgoldenrod"]; } } + public static Color LightGray { get { return namedColors["lightgray"]; } } + public static Color LightGreen { get { return namedColors["lightgreen"]; } } + public static Color LightPink { get { return namedColors["lightpink"]; } } + public static Color LightSalmon { get { return namedColors["lightsalmon"]; } } + public static Color LightSeaGreen { get { return namedColors["lightseagreen"]; } } + public static Color LightSkyBlue { get { return namedColors["lightskyblue"]; } } + public static Color LightSlateGray { get { return namedColors["lightslategray"]; } } + public static Color LightSteelBlue { get { return namedColors["lightsteelblue"]; } } + public static Color LightYellow { get { return namedColors["lightyellow"]; } } + public static Color Lime { get { return namedColors["lime"]; } } + public static Color Limegreen { get { return namedColors["limegreen"]; } } + public static Color Linen { get { return namedColors["linen"]; } } + public static Color Magenta { get { return namedColors["magenta"]; } } + public static Color Maroon { get { return namedColors["maroon"]; } } + public static Color MediumAquamarine { get { return namedColors["mediumaquamarine"]; } } + public static Color MediumBlue { get { return namedColors["mediumblue"]; } } + public static Color MediumOrchid { get { return namedColors["mediumorchid"]; } } + public static Color MediumPurple { get { return namedColors["mediumpurple"]; } } + public static Color MediumSeaGreen { get { return namedColors["mediumseagreen"]; } } + public static Color MediumSlateBlue { get { return namedColors["mediumslateblue"]; } } + public static Color MediumSpringGreen { get { return namedColors["mediumspringgreen"]; } } + public static Color MediumTurquoise { get { return namedColors["mediumturquoise"]; } } + public static Color MediumVioletRed { get { return namedColors["mediumvioletred"]; } } + public static Color MidnightBlue { get { return namedColors["midnightblue"]; } } + public static Color MintCream { get { return namedColors["mintcream"]; } } + public static Color MistyRose { get { return namedColors["mistyrose"]; } } + public static Color Moccasin { get { return namedColors["moccasin"]; } } + public static Color NavajoWhite { get { return namedColors["navajowhite"]; } } + public static Color NavyBlue { get { return namedColors["navyblue"]; } } + public static Color OldLace { get { return namedColors["oldlace"]; } } + public static Color Olive { get { return namedColors["olive"]; } } + public static Color OliveDrab { get { return namedColors["olivedrab"]; } } + public static Color Orange { get { return namedColors["orange"]; } } + public static Color OrangeRed { get { return namedColors["orangered"]; } } + public static Color Orchid { get { return namedColors["orchid"]; } } + public static Color PaleGoldenrod { get { return namedColors["palegoldenrod"]; } } + public static Color PaleGreen { get { return namedColors["palegreen"]; } } + public static Color PaleTurquoise { get { return namedColors["paleturquoise"]; } } + public static Color PaleVioletRed { get { return namedColors["palevioletred"]; } } + public static Color PapayaWhip { get { return namedColors["papayawhip"]; } } + public static Color PeachPuff { get { return namedColors["peachpuff"]; } } + public static Color Peru { get { return namedColors["peru"]; } } + public static Color Pink { get { return namedColors["pink"]; } } + public static Color Plum { get { return namedColors["plum"]; } } + public static Color PowderBlue { get { return namedColors["powderblue"]; } } + public static Color Purple { get { return namedColors["purple"]; } } + public static Color RebeccaPurple { get { return namedColors["rebeccapurple"]; } } + public static Color Red { get { return namedColors["red"]; } } + public static Color RosyBrown { get { return namedColors["rosybrown"]; } } + public static Color RoyalBlue { get { return namedColors["royalblue"]; } } + public static Color SaddleBrown { get { return namedColors["saddlebrown"]; } } + public static Color Salmon { get { return namedColors["salmon"]; } } + public static Color SandyBrown { get { return namedColors["sandybrown"]; } } + public static Color SeaGreen { get { return namedColors["seagreen"]; } } + public static Color SeaShell { get { return namedColors["seashell"]; } } + public static Color Sienna { get { return namedColors["sienna"]; } } + public static Color Silver { get { return namedColors["silver"]; } } + public static Color SkyBlue { get { return namedColors["skyblue"]; } } + public static Color SlateBlue { get { return namedColors["slateblue"]; } } + public static Color SlateGray { get { return namedColors["slategray"]; } } + public static Color Snow { get { return namedColors["snow"]; } } + public static Color SpringGreen { get { return namedColors["springgreen"]; } } + public static Color SteelBlue { get { return namedColors["steelblue"]; } } + public static Color Tan { get { return namedColors["tan"]; } } + public static Color Teal { get { return namedColors["teal"]; } } + public static Color Thistle { get { return namedColors["thistle"]; } } + public static Color Tomato { get { return namedColors["tomato"]; } } + public static Color Turquoise { get { return namedColors["turquoise"]; } } + public static Color Violet { get { return namedColors["violet"]; } } + public static Color WebGreen { get { return namedColors["webgreen"]; } } + public static Color WebGray { get { return namedColors["webgray"]; } } + public static Color WebMaroon { get { return namedColors["webmaroon"]; } } + public static Color WebPurple { get { return namedColors["webpurple"]; } } + public static Color Wheat { get { return namedColors["wheat"]; } } + public static Color White { get { return namedColors["white"]; } } + public static Color WhiteSmoke { get { return namedColors["whitesmoke"]; } } + public static Color Yellow { get { return namedColors["yellow"]; } } + public static Color YellowGreen { get { return namedColors["yellowgreen"]; } } + } +} diff --git a/modules/mono/glue/Managed/Files/DebuggingUtils.cs b/modules/mono/glue/Managed/Files/DebuggingUtils.cs index b27816084e..edfe3464ec 100644 --- a/modules/mono/glue/Managed/Files/DebuggingUtils.cs +++ b/modules/mono/glue/Managed/Files/DebuggingUtils.cs @@ -19,6 +19,12 @@ namespace Godot sb.Append(" "); } + public static void InstallTraceListener() + { + Trace.Listeners.Clear(); + Trace.Listeners.Add(new GodotTraceListener()); + } + public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl) { fileName = frame.GetFileName(); diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index 7695f03cd6..6ab8549a01 100644 --- a/modules/mono/glue/Managed/Files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -29,9 +29,7 @@ namespace Godot.Collections } public class Dictionary : - IDictionary<object, object>, - ICollection<KeyValuePair<object, object>>, - IEnumerable<KeyValuePair<object, object>>, + IDictionary, IDisposable { DictionarySafeHandle safeHandle; @@ -42,6 +40,14 @@ namespace Godot.Collections safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); } + public Dictionary(IDictionary dictionary) : this() + { + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); + } + internal Dictionary(DictionarySafeHandle handle) { safeHandle = handle; @@ -54,6 +60,9 @@ namespace Godot.Collections internal IntPtr GetPtr() { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + return safeHandle.DangerousGetHandle(); } @@ -71,19 +80,9 @@ namespace Godot.Collections disposed = true; } - public object this[object key] - { - get - { - return godot_icall_Dictionary_GetValue(GetPtr(), key); - } - set - { - godot_icall_Dictionary_SetValue(GetPtr(), key, value); - } - } + // IDictionary - public ICollection<object> Keys + public ICollection Keys { get { @@ -92,7 +91,7 @@ namespace Godot.Collections } } - public ICollection<object> Values + public ICollection Values { get { @@ -101,97 +100,102 @@ namespace Godot.Collections } } - public int Count - { - get - { - return godot_icall_Dictionary_Count(GetPtr()); - } - } + public bool IsFixedSize => false; - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; - public void Add(object key, object value) + public object this[object key] { - godot_icall_Dictionary_Add(GetPtr(), key, value); + get => godot_icall_Dictionary_GetValue(GetPtr(), key); + set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); } - public void Add(KeyValuePair<object, object> item) - { - Add(item.Key, item.Value); - } + public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); - public void Clear() - { - godot_icall_Dictionary_Clear(GetPtr()); - } + public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); - public bool Contains(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); - } + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); - public bool ContainsKey(object key) - { - return godot_icall_Dictionary_ContainsKey(GetPtr(), key); - } + public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); - public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + // ICollection + + public object SyncRoot => this; + + public bool IsSynchronized => false; + + public int Count => godot_icall_Dictionary_Count(GetPtr()); + + public void CopyTo(System.Array array, int index) { - // TODO 3 internal calls, can reduce to 1 + // TODO Can be done with single internal call + + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + Array keys = (Array)Keys; Array values = (Array)Values; int count = Count; + if (array.Length < (index + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { - // TODO 2 internal calls, can reduce to 1 - array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); - arrayIndex++; + array.SetValue(new DictionaryEntry(keys[i], values[i]), index); + index++; } } - public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + // IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private class DictionaryEnumerator : IDictionaryEnumerator { - // TODO 3 internal calls, can reduce to 1 - Array keys = (Array)Keys; - Array values = (Array)Values; - int count = Count; + Array keys; + Array values; + int count; + int index = -1; - for (int i = 0; i < count; i++) + public DictionaryEnumerator(Dictionary dictionary) { - // TODO 2 internal calls, can reduce to 1 - yield return new KeyValuePair<object, object>(keys[i], values[i]); + // TODO 3 internal calls, can reduce to 1 + keys = (Array)dictionary.Keys; + values = (Array)dictionary.Values; + count = dictionary.Count; } - } - public bool Remove(object key) - { - return godot_icall_Dictionary_RemoveKey(GetPtr(), key); - } + public object Current => Entry; - public bool Remove(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); - } + public DictionaryEntry Entry => + // TODO 2 internal calls, can reduce to 1 + new DictionaryEntry(keys[index], values[index]); - public bool TryGetValue(object key, out object value) - { - object retValue; - bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); - value = found ? retValue : default(object); - return found; + public object Key => Entry.Key; + + public object Value => Entry.Value; + + public bool MoveNext() + { + index++; + return index < count; + } + + public void Reset() + { + index = -1; + } } - IEnumerator IEnumerable.GetEnumerator() + public override string ToString() { - return GetEnumerator(); + return godot_icall_Dictionary_ToString(GetPtr()); } [MethodImpl(MethodImplOptions.InternalCall)] @@ -244,12 +248,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); } public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue>, - ICollection<KeyValuePair<TKey, TValue>>, - IEnumerable<KeyValuePair<TKey, TValue>> + IDictionary<TKey, TValue> { Dictionary objectDict; @@ -266,6 +271,23 @@ namespace Godot.Collections objectDict = new Dictionary(); } + public Dictionary(IDictionary<TKey, TValue> dictionary) + { + objectDict = new Dictionary(); + + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + // TODO: Can be optimized + + IntPtr godotDictionaryPtr = GetPtr(); + + foreach (KeyValuePair<TKey, TValue> entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } + } + public Dictionary(Dictionary dictionary) { objectDict = dictionary; @@ -286,6 +308,13 @@ namespace Godot.Collections return from.objectDict; } + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + + // IDictionary<TKey, TValue> + public TValue this[TKey key] { get @@ -316,6 +345,31 @@ namespace Godot.Collections } } + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + return objectDict.Contains(key); + } + + public bool Remove(TKey key) + { + return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + // ICollection<KeyValuePair<TKey, TValue>> + public int Count { get @@ -332,11 +386,6 @@ namespace Godot.Collections } } - public void Add(TKey key, TValue value) - { - objectDict.Add(key, value); - } - public void Add(KeyValuePair<TKey, TValue> item) { objectDict.Add(item.Key, item.Value); @@ -352,18 +401,22 @@ namespace Godot.Collections return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); } - public bool ContainsKey(TKey key) - { - return objectDict.ContainsKey(key); - } - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + // TODO 3 internal calls, can reduce to 1 Array<TKey> keys = (Array<TKey>)Keys; Array<TValue> values = (Array<TValue>)Values; int count = Count; + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { // TODO 2 internal calls, can reduce to 1 @@ -372,6 +425,13 @@ namespace Godot.Collections } } + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; + } + + // IEnumerable<KeyValuePair<TKey, TValue>> + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { // TODO 3 internal calls, can reduce to 1 @@ -386,32 +446,11 @@ namespace Godot.Collections } } - public bool Remove(TKey key) - { - return objectDict.Remove(key); - } - - public bool Remove(KeyValuePair<TKey, TValue> item) - { - return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); - } - - public bool TryGetValue(TKey key, out TValue value) - { - object retValue; - bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); - value = found ? (TValue)retValue : default(TValue); - return found; - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - internal IntPtr GetPtr() - { - return objectDict.GetPtr(); - } + public override string ToString() => objectDict.ToString(); } } diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs new file mode 100644 index 0000000000..a0f105d55e --- /dev/null +++ b/modules/mono/glue/Managed/Files/DynamicObject.cs @@ -0,0 +1,213 @@ + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace Godot +{ + /// <summary> + /// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API. + /// </summary> + /// <remarks> + /// <para> + /// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant + /// members of a <see cref="Godot.Object"/> instance at runtime. + /// </para> + /// <para> + /// This allows accessing the class members using their original names in the engine as well as the members from the + /// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in. + /// </para> + /// </remarks> + /// <example> + /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>. + /// <code> + /// dynamic sprite = GetNode("Sprite").DynamicGodotObject; + /// sprite.add_child(this); + /// + /// if ((sprite.hframes * sprite.vframes) > 0) + /// sprite.frame = 0; + /// </code> + /// </example> + /// <example> + /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>. + /// <code> + /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; + /// + /// if (childNode.print_allowed) + /// { + /// childNode.message = "Hello from C#"; + /// childNode.print_message(3); + /// } + /// </code> + /// The <c>ChildNode</c> node has the following GDScript script attached: + /// <code> + /// // # ChildNode.gd + /// // var print_allowed = true + /// // var message = "" + /// // + /// // func print_message(times): + /// // for i in times: + /// // print(message) + /// </code> + /// </example> + public class DynamicGodotObject : DynamicObject + { + /// <summary> + /// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>. + /// </summary> + public Object Value { get; } + + /// <summary> + /// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class. + /// </summary> + /// <param name="godotObject"> + /// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>. + /// </param> + /// <exception cref="System.ArgumentNullException"> + /// Thrown when the <paramref name="godotObject"/> parameter is null. + /// </exception> + public DynamicGodotObject(Object godotObject) + { + if (godotObject == null) + throw new ArgumentNullException(nameof(godotObject)); + + this.Value = godotObject; + } + + public override IEnumerable<string> GetDynamicMemberNames() + { + return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); + } + + public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) + { + switch (binder.Operation) + { + case ExpressionType.Equal: + case ExpressionType.NotEqual: + if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool))) + { + if (arg == null) + { + bool boolResult = Object.IsInstanceValid(Value); + + if (binder.Operation == ExpressionType.Equal) + boolResult = !boolResult; + + result = boolResult; + return true; + } + + if (arg is Object other) + { + bool boolResult = (Value == other); + + if (binder.Operation == ExpressionType.NotEqual) + boolResult = !boolResult; + + result = boolResult; + return true; + } + } + + break; + default: + // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual). + // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly. + break; + } + + return base.TryBinaryOperation(binder, arg, out result); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + if (binder.Type == typeof(Object)) + { + result = Value; + return true; + } + + if (typeof(Object).IsAssignableFrom(binder.Type)) + { + // Throws InvalidCastException when the cast fails + result = Convert.ChangeType(Value, binder.Type); + return true; + } + + return base.TryConvert(binder, out result); + } + + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes.Length == 1) + { + if (indexes[0] is string name) + { + return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result); + } + } + + return base.TryGetIndex(binder, indexes, out result); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); + } + + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes.Length == 1) + { + if (indexes[0] is string name) + { + return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value); + } + } + + return base.TrySetIndex(binder, indexes, value); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); + + #region We don't override these methods + + // Looks like this is not usable from C# + //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); + + // Object members cannot be deleted + //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); + //public override bool TryDeleteMember(DeleteMemberBinder binder); + + // Invocation on the object itself, e.g.: obj(param) + //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result); + + // No unnary operations to handle + //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result); + + #endregion + } +} diff --git a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs index 366d89b1c2..5023725f17 100644 --- a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs +++ b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs @@ -24,12 +24,12 @@ namespace Godot public T GetOwner<T>() where T : class { - return (T)(object)GetOwner(); + return (T)(object)Owner; } public T GetOwnerOrNull<T>() where T : class { - return GetOwner() as T; + return Owner as T; } public T GetParent<T>() where T : class diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs index 75a35a9eea..2068099ac6 100644 --- a/modules/mono/glue/Managed/Files/GD.cs +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; #if REAL_T_IS_DOUBLE using real_t = System.Double; @@ -12,12 +13,12 @@ namespace Godot { public static partial class GD { - public static object Bytes2Var(byte[] bytes) + public static object Bytes2Var(byte[] bytes, bool allow_objects = false) { - return godot_icall_GD_bytes2var(bytes); + return godot_icall_GD_bytes2var(bytes, allow_objects); } - public static object Convert(object what, int type) + public static object Convert(object what, Variant.Type type) { return godot_icall_GD_convert(what, type); } @@ -50,7 +51,7 @@ namespace Godot return godot_icall_GD_hash(var); } - public static Object InstanceFromId(int instanceId) + public static Object InstanceFromId(ulong instanceId) { return godot_icall_GD_instance_from_id(instanceId); } @@ -82,7 +83,7 @@ namespace Godot public static void Print(params object[] what) { - godot_icall_GD_print(what); + godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString())); } public static void PrintStack() @@ -92,89 +93,80 @@ namespace Godot public static void PrintErr(params object[] what) { - godot_icall_GD_printerr(what); + godot_icall_GD_printerr(Array.ConvertAll(what, x => x.ToString())); } public static void PrintRaw(params object[] what) { - godot_icall_GD_printraw(what); + godot_icall_GD_printraw(Array.ConvertAll(what, x => x.ToString())); } public static void PrintS(params object[] what) { - godot_icall_GD_prints(what); + godot_icall_GD_prints(Array.ConvertAll(what, x => x.ToString())); } public static void PrintT(params object[] what) { - godot_icall_GD_printt(what); + godot_icall_GD_printt(Array.ConvertAll(what, x => x.ToString())); } - public static int[] Range(int length) + public static float Randf() { - var ret = new int[length]; - - for (int i = 0; i < length; i++) - { - ret[i] = i; - } - - return ret; + return godot_icall_GD_randf(); } - public static int[] Range(int from, int to) + public static uint Randi() { - if (to < from) - return new int[0]; + return godot_icall_GD_randi(); + } - var ret = new int[to - from]; + public static void Randomize() + { + godot_icall_GD_randomize(); + } - for (int i = from; i < to; i++) - { - ret[i - from] = i; - } + public static double RandRange(double from, double to) + { + return godot_icall_GD_rand_range(from, to); + } - return ret; + public static uint RandSeed(ulong seed, out ulong newSeed) + { + return godot_icall_GD_rand_seed(seed, out newSeed); } - public static int[] Range(int from, int to, int increment) + public static IEnumerable<int> Range(int end) { - if (to < from && increment > 0) - return new int[0]; - if (to > from && increment < 0) - return new int[0]; + return Range(0, end, 1); + } - // Calculate count - int count; + public static IEnumerable<int> Range(int start, int end) + { + return Range(start, end, 1); + } - if (increment > 0) - count = (to - from - 1) / increment + 1; - else - count = (from - to - 1) / -increment + 1; + public static IEnumerable<int> Range(int start, int end, int step) + { + if (end < start && step > 0) + yield break; - var ret = new int[count]; + if (end > start && step < 0) + yield break; - if (increment > 0) + if (step > 0) { - int idx = 0; - for (int i = from; i < to; i += increment) - { - ret[idx++] = i; - } + for (int i = start; i < end; i += step) + yield return i; } else { - int idx = 0; - for (int i = from; i > to; i += increment) - { - ret[idx++] = i; - } + for (int i = start; i > end; i += step) + yield return i; } - - return ret; } - public static void Seed(int seed) + public static void Seed(ulong seed) { godot_icall_GD_seed(seed); } @@ -194,9 +186,9 @@ namespace Godot return godot_icall_GD_type_exists(type); } - public static byte[] Var2Bytes(object var) + public static byte[] Var2Bytes(object var, bool full_objects = false) { - return godot_icall_GD_var2bytes(var); + return godot_icall_GD_var2bytes(var, full_objects); } public static string Var2Str(object var) @@ -205,16 +197,16 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_bytes2var(byte[] bytes); + internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_convert(object what, int type); + internal extern static object godot_icall_GD_convert(object what, Variant.Type type); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_GD_hash(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Object godot_icall_GD_instance_from_id(int instance_id); + internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_GD_print(object[] what); @@ -232,7 +224,23 @@ namespace Godot internal extern static void godot_icall_GD_printt(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_seed(int seed); + internal extern static float godot_icall_GD_randf(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static uint godot_icall_GD_randi(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_randomize(); + + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static double godot_icall_GD_rand_range(double from, double to); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_seed(ulong seed); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_str(object[] what); @@ -244,7 +252,7 @@ namespace Godot internal extern static bool godot_icall_GD_type_exists(string type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_GD_var2bytes(object what); + internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/Managed/Files/GodotTraceListener.cs b/modules/mono/glue/Managed/Files/GodotTraceListener.cs new file mode 100644 index 0000000000..f1a00ae0fa --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotTraceListener.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics; + +namespace Godot +{ + internal class GodotTraceListener : TraceListener + { + public override void Write(string message) + { + GD.PrintRaw(message); + } + + public override void WriteLine(string message) + { + GD.Print(message); + } + + public override void Fail(string message, string detailMessage) + { + GD.PrintErr("Assertion failed: ", message); + if (detailMessage != null) + { + GD.PrintErr(" Details: ", detailMessage); + } + + try + { + var stackTrace = new StackTrace(true).ToString(); + GD.PrintErr(stackTrace); + } + catch + { + // ignored + } + } + } +} diff --git a/modules/mono/glue/Managed/Files/Interfaces/ISerializationListener.cs b/modules/mono/glue/Managed/Files/Interfaces/ISerializationListener.cs new file mode 100644 index 0000000000..c3fa2f3e82 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Interfaces/ISerializationListener.cs @@ -0,0 +1,8 @@ +namespace Godot +{ + public interface ISerializationListener + { + void OnBeforeSerialize(); + void OnAfterDeserialize(); + } +} diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs index f7699a15bf..a1d63a62ef 100644 --- a/modules/mono/glue/Managed/Files/MarshalUtils.cs +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -1,18 +1,212 @@ using System; -using Godot.Collections; +using System.Collections; +using System.Collections.Generic; namespace Godot { + using Array = Godot.Collections.Array; + using Dictionary = Godot.Collections.Dictionary; + static class MarshalUtils { - static bool IsArrayGenericType(Type type) + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> + static bool TypeIsGenericArray(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); + } + + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> + static bool TypeIsGenericDictionary(Type type) { - return type.GetGenericTypeDefinition() == typeof(Array<>); + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); } - static bool IsDictionaryGenericType(Type type) + static void ArrayGetElementType(Type arrayType, out Type elementType) { - return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); + elementType = arrayType.GetGenericArguments()[0]; + } + + static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) + { + var genericArgs = dictionaryType.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + } + + static bool GenericIEnumerableIsAssignableFromType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + return true; + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + return true; + } + + Type baseType = type.BaseType; + + if (baseType == null) + return false; + + return GenericIEnumerableIsAssignableFromType(baseType); + } + + static bool GenericIDictionaryIsAssignableFromType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + } + + Type baseType = type.BaseType; + + if (baseType == null) + return false; + + return GenericIDictionaryIsAssignableFromType(baseType); + } + + static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = interfaceType.GetGenericArguments()[0]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + elementType = null; + return false; + } + + return GenericIEnumerableIsAssignableFromType(baseType, out elementType); + } + + static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = interfaceType.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + keyType = null; + valueType = null; + return false; + } + + return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); + } + + static Type MakeGenericArrayType(Type elemType) + { + return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); + } + + static Type MakeGenericDictionaryType(Type keyType, Type valueType) + { + return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); + } + + // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> + // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized + + internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) + { + if (enumerable is ICollection collection) + { + int count = collection.Count; + + object[] tempArray = new object[count]; + collection.CopyTo(tempArray, 0); + + for (int i = 0; i < count; i++) + { + Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); + } + } + else + { + foreach (object element in enumerable) + { + Array.godot_icall_Array_Add(godotArrayPtr, element); + } + } + } + + internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) + { + foreach (DictionaryEntry entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } + } + + internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) + { +#if DEBUG + if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) + throw new InvalidOperationException("The type does not implement IDictionary<,>"); +#endif + + // TODO: Can we optimize this? + + var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); + var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); + + while (keys.MoveNext() && values.MoveNext()) + { + object key = keys.Current; + object value = values.Current; + + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); + } } } } diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index dcab3c1ffc..2d8c63fe7f 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -44,9 +44,9 @@ namespace Godot return (real_t)Math.Atan(s); } - public static real_t Atan2(real_t x, real_t y) + public static real_t Atan2(real_t y, real_t x) { - return (real_t)Math.Atan2(x, y); + return (real_t)Math.Atan2(y, x); } public static Vector2 Cartesian2Polar(real_t x, real_t y) @@ -79,14 +79,27 @@ namespace Godot return (real_t)Math.Cosh(s); } - public static int Decimals(real_t step) - { - return Decimals((decimal)step); - } - - public static int Decimals(decimal step) - { - return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; + public static int StepDecimals(real_t step) + { + double[] sd = new double[] { + 0.9999, + 0.09999, + 0.009999, + 0.0009999, + 0.00009999, + 0.000009999, + 0.0000009999, + 0.00000009999, + 0.000000009999, + }; + double abs = Mathf.Abs(step); + double decs = abs - (int)abs; // Strip away integer part + for (int i = 0; i < sd.Length; i++) { + if (decs >= sd[i]) { + return i; + } + } + return 0; } public static real_t Deg2Rad(real_t deg) @@ -143,6 +156,15 @@ namespace Godot return (weight - from) / (to - from); } + public static bool IsEqualApprox(real_t a, real_t b) + { + real_t tolerance = Epsilon * Abs(a); + if (tolerance < Epsilon) { + tolerance = Epsilon; + } + return Abs(a - b) < tolerance; + } + public static bool IsInf(real_t s) { return real_t.IsInfinity(s); @@ -153,6 +175,11 @@ namespace Godot return real_t.IsNaN(s); } + public static bool IsZeroApprox(real_t s) + { + return Abs(s) < Epsilon; + } + public static real_t Lerp(real_t from, real_t to, real_t weight) { return from + (to - from) * weight; @@ -183,6 +210,11 @@ namespace Godot return a < b ? a : b; } + public static real_t MoveToward(real_t from, real_t to, real_t delta) + { + return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta; + } + public static int NearestPo2(int value) { value--; @@ -261,6 +293,16 @@ namespace Godot return (real_t)Math.Sinh(s); } + public static real_t SmoothStep(real_t from, real_t to, real_t weight) + { + if (IsEqualApprox(from, to)) + { + return from; + } + real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + return x * x * (3 - 2 * x); + } + public static real_t Sqrt(real_t s) { return (real_t)Math.Sqrt(s); @@ -289,13 +331,13 @@ namespace Godot public static int Wrap(int value, int min, int max) { int rng = max - min; - return min + ((value - min) % rng + rng) % rng; + return rng != 0 ? min + ((value - min) % rng + rng) % rng : min; } public static real_t Wrap(real_t value, real_t min, real_t max) { real_t rng = max - min; - return min + ((value - min) % rng + rng) % rng; + return !IsEqualApprox(rng, default(real_t)) ? min + ((value - min) % rng + rng) % rng : min; } } } diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 2ef02cc288..b96f01bc2e 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -21,6 +21,16 @@ namespace Godot public const real_t Epsilon = 1e-06f; #endif + public static int DecimalCount(real_t s) + { + return DecimalCount((decimal)s); + } + + public static int DecimalCount(decimal s) + { + return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; + } + public static int CeilToInt(real_t s) { return (int)Math.Ceiling(s); @@ -35,5 +45,10 @@ namespace Godot { return (int)Math.Round(s); } + + public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) + { + return Abs(a - b) < tolerance; + } } }
\ No newline at end of file diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs index 2c89bec87f..94a4ed1de9 100644 --- a/modules/mono/glue/Managed/Files/NodePath.cs +++ b/modules/mono/glue/Managed/Files/NodePath.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; namespace Godot { - public partial class NodePath : IDisposable + public sealed partial class NodePath : IDisposable { private bool disposed = false; @@ -11,7 +11,13 @@ namespace Godot internal static IntPtr GetPtr(NodePath instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~NodePath() @@ -25,7 +31,7 @@ namespace Godot GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposed) return; @@ -49,7 +55,7 @@ namespace Godot get { return ptr; } } - public NodePath() : this(string.Empty) {} + public NodePath() : this(string.Empty) { } public NodePath(string path) { diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs index 30490a715f..de80f7fddc 100644 --- a/modules/mono/glue/Managed/Files/Object.base.cs +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -30,7 +30,13 @@ namespace Godot internal static IntPtr GetPtr(Object instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~Object() @@ -67,11 +73,44 @@ namespace Godot disposed = true; } + public override string ToString() + { + return godot_icall_Object_ToString(GetPtr(this)); + } + + /// <summary> + /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance + /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter. + /// </summary> + /// <param name="source"> + /// The instance the awaiter will be listening to. + /// </param> + /// <param name="signal"> + /// The signal the awaiter will be waiting for. + /// </param> + /// <example> + /// This sample prints a message once every frame up to 100 times. + /// <code> + /// public override void _Ready() + /// { + /// for (int i = 0; i < 100; i++) + /// { + /// await ToSignal(GetTree(), "idle_frame"); + /// GD.Print($"Frame {i}"); + /// } + /// } + /// </code> + /// </example> public SignalAwaiter ToSignal(Object source, string signal) { return new SignalAwaiter(source, signal, this); } + /// <summary> + /// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance. + /// </summary> + public dynamic DynamicObject => new DynamicGodotObject(this); + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Object_Ctor(Object obj); @@ -81,6 +120,9 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Object_ToString(IntPtr ptr); + // Used by the generated API [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index f11cd490a9..e16d4315be 100644 --- a/modules/mono/glue/Managed/Files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -200,7 +200,7 @@ namespace Godot public bool Equals(Plane other) { - return _normal == other._normal && D == other.D; + return _normal == other._normal && Mathf.IsEqualApprox(D, other.D); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index fd1ac01083..0d4349084a 100644 --- a/modules/mono/glue/Managed/Files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -123,22 +123,23 @@ namespace Godot // Calculate cosine real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; - var to1 = new real_t[4]; + var to1 = new Quat(); // Adjust signs if necessary if (cosom < 0.0) { - cosom = -cosom; to1[0] = -b.x; - to1[1] = -b.y; - to1[2] = -b.z; - to1[3] = -b.w; + cosom = -cosom; + to1.x = -b.x; + to1.y = -b.y; + to1.z = -b.z; + to1.w = -b.w; } else { - to1[0] = b.x; - to1[1] = b.y; - to1[2] = b.z; - to1[3] = b.w; + to1.x = b.x; + to1.y = b.y; + to1.z = b.z; + to1.w = b.w; } real_t sinom, scale0, scale1; @@ -162,10 +163,10 @@ namespace Godot // Calculate final values return new Quat ( - scale0 * x + scale1 * to1[0], - scale0 * y + scale1 * to1[1], - scale0 * z + scale1 * to1[2], - scale0 * w + scale1 * to1[3] + scale0 * x + scale1 * to1.x, + scale0 * y + scale1 * to1.y, + scale0 * z + scale1 * to1.z, + scale0 * w + scale1 * to1.w ); } @@ -347,9 +348,9 @@ namespace Godot public override bool Equals(object obj) { - if (obj is Vector2) + if (obj is Quat) { - return Equals((Vector2)obj); + return Equals((Quat)obj); } return false; @@ -357,7 +358,7 @@ namespace Godot public bool Equals(Quat other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs index b862b8cac0..12064beca2 100644 --- a/modules/mono/glue/Managed/Files/RID.cs +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; namespace Godot { - public partial class RID : IDisposable + public sealed partial class RID : IDisposable { private bool disposed = false; @@ -11,7 +11,13 @@ namespace Godot internal static IntPtr GetPtr(RID instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~RID() @@ -25,7 +31,7 @@ namespace Godot GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposed) return; @@ -64,6 +70,8 @@ namespace Godot return godot_icall_RID_get_id(RID.GetPtr(this)); } + public override string ToString() => "[RID]"; + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); diff --git a/modules/mono/glue/Managed/Files/StringExtensions.cs b/modules/mono/glue/Managed/Files/StringExtensions.cs index c194facd0b..b43034fbb5 100644 --- a/modules/mono/glue/Managed/Files/StringExtensions.cs +++ b/modules/mono/glue/Managed/Files/StringExtensions.cs @@ -299,14 +299,14 @@ namespace Godot if (basepos != -1) { var end = basepos + 3; - rs = instance.Substring(end, instance.Length); + rs = instance.Substring(end); @base = instance.Substring(0, end); } else { if (instance.BeginsWith("/")) { - rs = instance.Substring(1, instance.Length); + rs = instance.Substring(1); @base = "/"; } else @@ -333,7 +333,7 @@ namespace Godot if (sep == -1) return instance; - return instance.Substring(sep + 1, instance.Length); + return instance.Substring(sep + 1); } // <summary> @@ -911,7 +911,8 @@ namespace Godot // </summary> public static string Substr(this string instance, int from, int len) { - return instance.Substring(from, len); + int max = instance.Length - from; + return instance.Substring(from, len > max ? max : len); } // <summary> diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index fa85855edd..bd79144873 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -71,21 +71,21 @@ namespace Godot { // Make rotation matrix // Z vector - Vector3 zAxis = eye - target; + Vector3 column2 = eye - target; - zAxis.Normalize(); + column2.Normalize(); - Vector3 yAxis = up; + Vector3 column1 = up; - Vector3 xAxis = yAxis.Cross(zAxis); + Vector3 column0 = column1.Cross(column2); // Recompute Y = Z cross X - yAxis = zAxis.Cross(xAxis); + column1 = column2.Cross(column0); - xAxis.Normalize(); - yAxis.Normalize(); + column0.Normalize(); + column1.Normalize(); - basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = new Basis(column0, column1, column2); origin = eye; } @@ -94,9 +94,9 @@ namespace Godot { return new Transform(basis, new Vector3 ( - origin[0] += basis[0].Dot(ofs), - origin[1] += basis[1].Dot(ofs), - origin[2] += basis[2].Dot(ofs) + origin[0] += basis.Row0.Dot(ofs), + origin[1] += basis.Row1.Dot(ofs), + origin[2] += basis.Row2.Dot(ofs) )); } @@ -104,9 +104,9 @@ namespace Godot { return new Vector3 ( - basis[0].Dot(v) + origin.x, - basis[1].Dot(v) + origin.y, - basis[2].Dot(v) + origin.z + basis.Row0.Dot(v) + origin.x, + basis.Row1.Dot(v) + origin.y, + basis.Row2.Dot(v) + origin.z ); } @@ -116,9 +116,9 @@ namespace Godot return new Vector3 ( - basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z, - basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z, - basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z + basis.Row0[0] * vInv.x + basis.Row1[0] * vInv.y + basis.Row2[0] * vInv.z, + basis.Row0[1] * vInv.x + basis.Row1[1] * vInv.y + basis.Row2[1] * vInv.z, + basis.Row0[2] * vInv.x + basis.Row1[2] * vInv.y + basis.Row2[2] * vInv.z ); } @@ -134,9 +134,9 @@ namespace Godot public static Transform FlipZ { get { return _flipZ; } } // Constructors - public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) + public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin) { - basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = new Basis(column0, column1, column2); this.origin = origin; } diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs index c9e5b560b2..33ff286769 100644 --- a/modules/mono/glue/Managed/Files/Transform2D.cs +++ b/modules/mono/glue/Managed/Files/Transform2D.cs @@ -13,42 +13,65 @@ namespace Godot { public Vector2 x; public Vector2 y; - public Vector2 o; - - public Vector2 Origin - { - get { return o; } - } + public Vector2 origin; public real_t Rotation { - get { return Mathf.Atan2(y.x, o.y); } + get + { + real_t det = BasisDeterminant(); + Transform2D t = Orthonormalized(); + if (det < 0) + { + t.ScaleBasis(new Vector2(1, -1)); + } + return Mathf.Atan2(t.x.y, t.x.x); + } + set + { + Vector2 scale = Scale; + x.x = y.y = Mathf.Cos(value); + x.y = y.x = Mathf.Sin(value); + y.x *= -1; + Scale = scale; + } } public Vector2 Scale { - get { return new Vector2(x.Length(), y.Length()); } + get + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return new Vector2(x.Length(), detSign * y.Length()); + } + set + { + x = x.Normalized(); + y = y.Normalized(); + x *= value.x; + y *= value.y; + } } - public Vector2 this[int index] + public Vector2 this[int rowIndex] { get { - switch (index) + switch (rowIndex) { case 0: return x; case 1: return y; case 2: - return o; + return origin; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (rowIndex) { case 0: x = value; @@ -57,7 +80,7 @@ namespace Godot y = value; return; case 2: - o = value; + origin = value; return; default: throw new IndexOutOfRangeException(); @@ -65,30 +88,29 @@ namespace Godot } } - - public real_t this[int index, int axis] + public real_t this[int rowIndex, int columnIndex] { get { - switch (index) + switch (rowIndex) { case 0: - return x[axis]; + return x[columnIndex]; case 1: - return y[axis]; + return y[columnIndex]; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (rowIndex) { case 0: - x[axis] = value; + x[columnIndex] = value; return; case 1: - y[axis] = value; + y[columnIndex] = value; return; default: throw new IndexOutOfRangeException(); @@ -98,34 +120,32 @@ namespace Godot public Transform2D AffineInverse() { - var inv = this; - - real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; + real_t det = BasisDeterminant(); if (det == 0) - { - return new Transform2D - ( - float.NaN, float.NaN, - float.NaN, float.NaN, - float.NaN, float.NaN - ); - } + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); - real_t idet = 1.0f / det; + var inv = this; + + real_t temp = inv[0, 0]; + inv[0, 0] = inv[1, 1]; + inv[1, 1] = temp; - real_t temp = this[0, 0]; - this[0, 0] = this[1, 1]; - this[1, 1] = temp; + real_t detInv = 1.0f / det; - this[0] *= new Vector2(idet, -idet); - this[1] *= new Vector2(-idet, idet); + inv[0] *= new Vector2(detInv, -detInv); + inv[1] *= new Vector2(-detInv, detInv); - this[2] = BasisXform(-this[2]); + inv[2] = BasisXform(-inv[2]); return inv; } + private real_t BasisDeterminant() + { + return x.x * y.y - x.y * y.x; + } + public Vector2 BasisXform(Vector2 v) { return new Vector2(Tdotx(v), Tdoty(v)); @@ -168,8 +188,8 @@ namespace Godot } // Extract parameters - Vector2 p1 = Origin; - Vector2 p2 = m.Origin; + Vector2 p1 = origin; + Vector2 p2 = m.origin; // Construct matrix var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); @@ -189,7 +209,7 @@ namespace Godot inv.x.y = inv.y.x; inv.y.x = temp; - inv.o = inv.BasisXform(-inv.o); + inv.origin = inv.BasisXform(-inv.origin); return inv; } @@ -221,10 +241,18 @@ namespace Godot var copy = this; copy.x *= scale; copy.y *= scale; - copy.o *= scale; + copy.origin *= scale; return copy; } + private void ScaleBasis(Vector2 scale) + { + x.x *= scale.x; + x.y *= scale.y; + y.x *= scale.x; + y.y *= scale.y; + } + private real_t Tdotx(Vector2 with) { return this[0, 0] * with[0] + this[1, 0] * with[1]; @@ -238,66 +266,62 @@ namespace Godot public Transform2D Translated(Vector2 offset) { var copy = this; - copy.o += copy.BasisXform(offset); + copy.origin += copy.BasisXform(offset); return copy; } public Vector2 Xform(Vector2 v) { - return new Vector2(Tdotx(v), Tdoty(v)) + o; + return new Vector2(Tdotx(v), Tdoty(v)) + origin; } public Vector2 XformInv(Vector2 v) { - Vector2 vInv = v - o; + Vector2 vInv = v - origin; return new Vector2(x.Dot(vInv), y.Dot(vInv)); } // Constants - private static readonly Transform2D _identity = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, 1f), Vector2.Zero); - private static readonly Transform2D _flipX = new Transform2D(new Vector2(-1f, 0f), new Vector2(0f, 1f), Vector2.Zero); - private static readonly Transform2D _flipY = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, -1f), Vector2.Zero); + private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0); + private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0); + private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0); - public static Transform2D Identity { get { return _identity; } } - public static Transform2D FlipX { get { return _flipX; } } - public static Transform2D FlipY { get { return _flipY; } } + public static Transform2D Identity => _identity; + public static Transform2D FlipX => _flipX; + public static Transform2D FlipY => _flipY; // Constructors - public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) + public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos) { x = xAxis; y = yAxis; - o = origin; + origin = originPos; } + // Arguments are named such that xy is equal to calling x.y public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { x = new Vector2(xx, xy); y = new Vector2(yx, yy); - o = new Vector2(ox, oy); + origin = new Vector2(ox, oy); } public Transform2D(real_t rot, Vector2 pos) { - real_t cr = Mathf.Cos(rot); - real_t sr = Mathf.Sin(rot); - x.x = cr; - y.y = cr; - x.y = -sr; - y.x = sr; - o = pos; + x.x = y.y = Mathf.Cos(rot); + x.y = y.x = Mathf.Sin(rot); + y.x *= -1; + origin = pos; } public static Transform2D operator *(Transform2D left, Transform2D right) { - left.o = left.Xform(right.o); + left.origin = left.Xform(right.origin); - real_t x0, x1, y0, y1; - - x0 = left.Tdotx(right.x); - x1 = left.Tdoty(right.x); - y0 = left.Tdotx(right.y); - y1 = left.Tdoty(right.y); + real_t x0 = left.Tdotx(right.x); + real_t x1 = left.Tdoty(right.x); + real_t y0 = left.Tdotx(right.y); + real_t y1 = left.Tdoty(right.y); left.x.x = x0; left.x.y = x1; @@ -319,22 +343,17 @@ namespace Godot public override bool Equals(object obj) { - if (obj is Transform2D) - { - return Equals((Transform2D)obj); - } - - return false; + return obj is Transform2D transform2D && Equals(transform2D); } public bool Equals(Transform2D other) { - return x.Equals(other.x) && y.Equals(other.y) && o.Equals(other.o); + return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); } public override int GetHashCode() { - return x.GetHashCode() ^ y.GetHashCode() ^ o.GetHashCode(); + return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode(); } public override string ToString() @@ -343,7 +362,7 @@ namespace Godot { x.ToString(), y.ToString(), - o.ToString() + origin.ToString() }); } @@ -353,7 +372,7 @@ namespace Godot { x.ToString(format), y.ToString(format), - o.ToString(format) + origin.ToString(format) }); } } diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index ce41886bfc..a7f26283a7 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -52,11 +52,15 @@ namespace Godot internal void Normalize() { - real_t length = x * x + y * y; + real_t lengthsq = LengthSquared(); - if (length != 0f) + if (lengthsq == 0) { - length = Mathf.Sqrt(length); + x = y = 0f; + } + else + { + real_t length = Mathf.Sqrt(lengthsq); x /= length; y /= length; } @@ -84,7 +88,7 @@ namespace Godot public real_t AngleToPoint(Vector2 to) { - return Mathf.Atan2(x - to.x, y - to.y); + return Mathf.Atan2(y - to.y, x - to.x); } public real_t Aspect() @@ -132,6 +136,11 @@ namespace Godot (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } + public Vector2 DirectionTo(Vector2 b) + { + return new Vector2(b.x - x, b.y - y).Normalized(); + } + public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); @@ -177,11 +186,19 @@ namespace Godot return res; } + public Vector2 MoveToward(Vector2 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + public Vector2 Normalized() { - var result = this; - result.Normalize(); - return result; + var v = this; + v.Normalize(); + return v; } public Vector2 Project(Vector2 onNormal) @@ -338,7 +355,7 @@ namespace Godot public static bool operator <(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y < right.y; } @@ -348,7 +365,7 @@ namespace Godot public static bool operator >(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y > right.y; } @@ -358,7 +375,7 @@ namespace Godot public static bool operator <=(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y <= right.y; } @@ -368,7 +385,7 @@ namespace Godot public static bool operator >=(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y >= right.y; } @@ -388,7 +405,7 @@ namespace Godot public bool Equals(Vector2 other) { - return x == other.x && y == other.y; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index f6ff27989d..16803ae55c 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -65,14 +65,15 @@ namespace Godot internal void Normalize() { - real_t length = Length(); + real_t lengthsq = LengthSquared(); - if (length == 0f) + if (lengthsq == 0) { x = y = z = 0f; } else { + real_t length = Mathf.Sqrt(lengthsq); x /= length; y /= length; z /= length; @@ -126,6 +127,11 @@ namespace Godot ); } + public Vector3 DirectionTo(Vector3 b) + { + return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); + } + public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); @@ -184,6 +190,14 @@ namespace Godot ); } + public Vector3 MoveToward(Vector3 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + public Axis MaxAxis() { return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); @@ -392,9 +406,9 @@ namespace Godot public static bool operator <(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z < right.z; return left.y < right.y; } @@ -404,9 +418,9 @@ namespace Godot public static bool operator >(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z > right.z; return left.y > right.y; } @@ -416,9 +430,9 @@ namespace Godot public static bool operator <=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z <= right.z; return left.y < right.y; } @@ -428,9 +442,9 @@ namespace Godot public static bool operator >=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z >= right.z; return left.y > right.y; } @@ -450,7 +464,7 @@ namespace Godot public bool Equals(Vector3 other) { - return x == other.x && y == other.y && z == other.z; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/IgnoredFiles/Node.cs b/modules/mono/glue/Managed/IgnoredFiles/Node.cs index 99ba0f827a..cff61b1e0b 100644 --- a/modules/mono/glue/Managed/IgnoredFiles/Node.cs +++ b/modules/mono/glue/Managed/IgnoredFiles/Node.cs @@ -15,9 +15,10 @@ namespace Godot throw new NotImplementedException(); } - public Node GetOwner() + public Node Owner { - throw new NotImplementedException(); + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); } public Node GetParent() diff --git a/modules/mono/glue/Managed/IgnoredFiles/Variant.cs b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs new file mode 100644 index 0000000000..802140b062 --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/Variant.cs @@ -0,0 +1,11 @@ + +namespace Godot +{ + public static class Variant + { + public enum Type + { + + } + } +} diff --git a/modules/mono/glue/Managed/Managed.csproj b/modules/mono/glue/Managed/Managed.csproj index 1f82dde5e7..61f738922b 100644 --- a/modules/mono/glue/Managed/Managed.csproj +++ b/modules/mono/glue/Managed/Managed.csproj @@ -11,7 +11,7 @@ </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> + <DebugType>portable</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> diff --git a/modules/mono/editor/dotnet_solution.h b/modules/mono/glue/arguments_vector.h index 629605ad18..8c0f308c15 100644 --- a/modules/mono/editor/dotnet_solution.h +++ b/modules/mono/glue/arguments_vector.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* dotnet_solution.h */ +/* arguments_vector.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,36 +28,41 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NET_SOLUTION_H -#define NET_SOLUTION_H +#ifndef ARGUMENTS_VECTOR_H +#define ARGUMENTS_VECTOR_H -#include "core/map.h" -#include "core/ustring.h" +#include "core/os/memory.h" -struct DotNetSolution { - String name; +template <typename T, int POOL_SIZE = 5> +struct ArgumentsVector { - struct ProjectInfo { - String guid; - String relpath; // Must be relative to the solution directory - Vector<String> configs; - }; - - void add_new_project(const String &p_name, const ProjectInfo &p_project_info); - bool has_project(const String &p_name) const; - const ProjectInfo &get_project_info(const String &p_name) const; - bool remove_project(const String &p_name); +private: + T pool[POOL_SIZE]; + T *_ptr; + int size; - Error save(); + ArgumentsVector(); + ArgumentsVector(const ArgumentsVector &); - bool set_path(const String &p_existing_path); - String get_path(); +public: + T *ptr() { return _ptr; } + T &get(int p_idx) { return _ptr[p_idx]; } + void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; } - DotNetSolution(const String &p_name); + explicit ArgumentsVector(int p_size) : + size(p_size) { + if (p_size <= POOL_SIZE) { + _ptr = pool; + } else { + _ptr = memnew_arr(T, p_size); + } + } -private: - String path; - Map<String, ProjectInfo> projects; + ~ArgumentsVector() { + if (size > POOL_SIZE) { + memdelete_arr(_ptr); + } + } }; -#endif // NET_SOLUTION_H +#endif // ARGUMENTS_VECTOR_H diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 58916c5283..6d85f55b97 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,12 +33,14 @@ #ifdef MONO_GLUE_ENABLED #include "core/reference.h" -#include "core/string_db.h" +#include "core/string_name.h" #include "../csharp_script.h" +#include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_internals.h" #include "../mono_gd/gd_mono_utils.h" #include "../signal_awaiter_utils.h" +#include "arguments_vector.h" Object *godot_icall_Object_Ctor(MonoObject *p_obj) { Object *instance = memnew(Object); @@ -65,14 +67,17 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } -void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer) { +void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) { #ifdef DEBUG_ENABLED CRASH_COND(p_ptr == NULL); // This is only called with Reference derived classes @@ -85,11 +90,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance()); if (cs_instance) { if (!cs_instance->is_destructing_script_instance()) { - bool r_owner_deleted; - cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); - if (!r_owner_deleted && !p_is_finalizer) { - // If the native instance is still alive and Dispose() was called - // (instead of the finalizer), then we remove the script instance. + bool delete_owner; + bool remove_script_instance; + + cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance); + + if (delete_owner) { + memdelete(ref); + } else if (remove_script_instance) { ref->set_script_instance(NULL); } } @@ -105,9 +113,12 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; - if (gchandle.is_valid()) { - CSharpLanguage::release_script_gchandle(p_obj, gchandle); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get(); + if (script_binding.inited) { + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } } } } @@ -138,7 +149,7 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) { wref->set_obj(p_obj); } - return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); + return GDMonoUtils::unmanaged_get_managed(wref.ptr()); } Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { @@ -146,13 +157,94 @@ Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter); } +MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { + List<PropertyInfo> property_list; + p_ptr->get_property_list(&property_list); + + MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size()); + + int i = 0; + for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { + MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name); + mono_array_setref(result, i, boxed); + i++; + } + + return result; +} + +MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) { + String name = GDMonoMarshal::mono_string_to_godot(p_name); + + int argc = mono_array_length(p_args); + + ArgumentsVector<Variant> arg_store(argc); + ArgumentsVector<const Variant *> args(argc); + + for (int i = 0; i < argc; i++) { + MonoObject *elem = mono_array_get(p_args, MonoObject *, i); + arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem)); + args.set(i, &arg_store.get(i)); + } + + Variant::CallError error; + Variant result = p_ptr->call(StringName(name), args.ptr(), argc, error); + + *r_result = GDMonoMarshal::variant_to_mono_object(result); + + return error.error == Variant::CallError::CALL_OK; +} + +MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) { + String name = GDMonoMarshal::mono_string_to_godot(p_name); + + bool valid; + Variant value = p_ptr->get(StringName(name), &valid); + + if (valid) { + *r_result = GDMonoMarshal::variant_to_mono_object(value); + } + + return valid; +} + +MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) { + String name = GDMonoMarshal::mono_string_to_godot(p_name); + Variant value = GDMonoMarshal::mono_object_to_variant(p_value); + + bool valid; + p_ptr->set(StringName(name), value, &valid); + + return valid; +} + +MonoString *godot_icall_Object_ToString(Object *p_ptr) { +#ifdef DEBUG_ENABLED + // Cannot happen in C#; would get an ObjectDisposedException instead. + CRASH_COND(p_ptr == NULL); + + if (ScriptDebugger::get_singleton() && !Object::cast_to<Reference>(p_ptr)) { // Only if debugging! + // Cannot happen either in C#; the handle is nullified when the object is destroyed + CRASH_COND(!ObjectDB::instance_validate(p_ptr)); + } +#endif + + String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]"; + return GDMonoMarshal::mono_string_from_godot(result); +} + void godot_register_object_icalls() { mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor); mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method); + mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString); mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref); mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect); + mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList); + mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", (void *)godot_icall_DynamicGodotObject_InvokeMember); + mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", (void *)godot_icall_DynamicGodotObject_GetMember); + mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", (void *)godot_icall_DynamicGodotObject_SetMember); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h index 2d4d66ebb8..e4ac9fb99d 100644 --- a/modules/mono/glue/base_object_glue.h +++ b/modules/mono/glue/base_object_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ Object *godot_icall_Object_Ctor(MonoObject *p_obj); void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr); -void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer); +void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer); MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method); @@ -50,6 +50,18 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj); Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter); +// DynamicGodotObject + +MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr); + +MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result); + +MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result); + +MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value); + +MonoString *godot_icall_Object_ToString(Object *p_ptr); + // Register internal calls void godot_register_object_icalls(); diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 059e2ff6de..e67c8b9ad9 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,7 +46,7 @@ void godot_icall_Array_Dtor(Array *ptr) { } MonoObject *godot_icall_Array_At(Array *ptr, int index) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return NULL; } @@ -54,7 +54,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) { } MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return NULL; } @@ -62,7 +62,7 @@ MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_en } void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; } @@ -73,20 +73,21 @@ int godot_icall_Array_Count(Array *ptr) { return ptr->size(); } -void godot_icall_Array_Add(Array *ptr, MonoObject *item) { +int godot_icall_Array_Add(Array *ptr, MonoObject *item) { ptr->append(GDMonoMarshal::mono_object_to_variant(item)); + return ptr->size(); } void godot_icall_Array_Clear(Array *ptr) { ptr->clear(); } -bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) { +MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) { return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; } void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { - int count = ptr->size(); + unsigned int count = ptr->size(); if (mono_array_length(array) < (array_index + count)) { MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); @@ -94,7 +95,7 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { return; } - for (int i = 0; i < count; i++) { + for (unsigned int i = 0; i < count; i++) { MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); mono_array_setref(array, array_index, boxed); array_index++; @@ -113,7 +114,7 @@ void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) { ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); } -bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) { +MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) { int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); if (idx >= 0) { ptr->remove(idx); @@ -123,13 +124,17 @@ bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) { } void godot_icall_Array_RemoveAt(Array *ptr, int index) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; } ptr->remove(index); } +Error godot_icall_Array_Resize(Array *ptr, int new_size) { + return ptr->resize(new_size); +} + void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { MonoType *elem_type = mono_reflection_type_get_type(refltype); @@ -138,6 +143,10 @@ void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Array_ToString(Array *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + Dictionary *godot_icall_Dictionary_Ctor() { return memnew(Dictionary); } @@ -153,7 +162,7 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { #ifdef DEBUG_ENABLED CRASH_COND(!exc); #endif - GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); GDMonoUtils::set_pending_exception((MonoException *)exc); return NULL; } @@ -167,7 +176,7 @@ MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject #ifdef DEBUG_ENABLED CRASH_COND(!exc); #endif - GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException)); GDMonoUtils::set_pending_exception((MonoException *)exc); return NULL; } @@ -204,21 +213,21 @@ void godot_icall_Dictionary_Clear(Dictionary *ptr) { ptr->clear(); } -bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { +MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { // no dupes Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value); } -bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { +MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); } -bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { +MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { return ptr->erase(GDMonoMarshal::mono_object_to_variant(key)); } -bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { +MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { Variant varKey = GDMonoMarshal::mono_object_to_variant(key); // no dupes @@ -231,7 +240,7 @@ bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject return false; } -bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { +MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); if (ret == NULL) { *value = NULL; @@ -241,7 +250,7 @@ bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoOb return true; } -bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { +MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); if (ret == NULL) { *value = NULL; @@ -259,6 +268,10 @@ void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltyp *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); @@ -274,7 +287,9 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); @@ -293,6 +308,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index b9b1338510..54ed42c3b6 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,11 +51,11 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); int godot_icall_Array_Count(Array *ptr); -void godot_icall_Array_Add(Array *ptr, MonoObject *item); +int godot_icall_Array_Add(Array *ptr, MonoObject *item); void godot_icall_Array_Clear(Array *ptr); -bool godot_icall_Array_Contains(Array *ptr, MonoObject *item); +MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item); void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index); @@ -63,12 +63,16 @@ int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item); void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item); -bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); +MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item); void godot_icall_Array_RemoveAt(Array *ptr, int index); +Error godot_icall_Array_Resize(Array *ptr, int new_size); + void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Array_ToString(Array *ptr); + // Dictionary Dictionary *godot_icall_Dictionary_Ctor(); @@ -91,20 +95,22 @@ void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *va void godot_icall_Dictionary_Clear(Dictionary *ptr); -bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value); +MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value); -bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key); +MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key); -bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key); +MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key); -bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value); +MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value); -bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); +MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); -bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class); +MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class); void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr); + // Register internal calls void godot_register_collections_icalls(); diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index 9f5bcecdd9..7c30092855 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,18 +41,18 @@ #include "../mono_gd/gd_mono_utils.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes) { +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) { Variant ret; PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes); PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, p_allow_objects); if (err != OK) { ret = RTR("Not enough bytes for decoding bytes, or invalid format."); } return GDMonoMarshal::variant_to_mono_object(ret); } -MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type) { +MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) { Variant what = GDMonoMarshal::mono_object_to_variant(p_what); const Variant *args[1] = { &what }; Variant::CallError ce; @@ -65,7 +65,7 @@ int godot_icall_GD_hash(MonoObject *p_var) { return GDMonoMarshal::mono_object_to_variant(p_var).hash(); } -MonoObject *godot_icall_GD_instance_from_id(int p_instance_id) { +MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) { return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id)); } @@ -115,7 +115,29 @@ void godot_icall_GD_printt(MonoArray *p_what) { print_line(str); } -void godot_icall_GD_seed(int p_seed) { +float godot_icall_GD_randf() { + return Math::randf(); +} + +uint32_t godot_icall_GD_randi() { + return Math::rand(); +} + +void godot_icall_GD_randomize() { + Math::randomize(); +} + +double godot_icall_GD_rand_range(double from, double to) { + return Math::random(from, to); +} + +uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) { + int ret = Math::rand_from_seed(&seed); + *newSeed = seed; + return ret; +} + +void godot_icall_GD_seed(uint64_t p_seed) { Math::seed(p_seed); } @@ -153,7 +175,7 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) { return GDMonoMarshal::variant_to_mono_object(ret); } -bool godot_icall_GD_type_exists(MonoString *p_type) { +MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) { return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type)); } @@ -165,19 +187,19 @@ void godot_icall_GD_pushwarning(MonoString *p_str) { WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str)); } -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) { +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) { Variant var = GDMonoMarshal::mono_object_to_variant(p_var); PoolByteArray barr; int len; - Error err = encode_variant(var, NULL, len); + Error err = encode_variant(var, NULL, len, p_full_objects); ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); ERR_FAIL_COND_V(err != OK, NULL); barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(var, w.ptr(), len); + encode_variant(var, w.ptr(), len, p_full_objects); } return GDMonoMarshal::PoolByteArray_to_mono_array(barr); @@ -201,6 +223,11 @@ void godot_register_gd_icalls() { mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw); mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints); mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt); + mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf); + mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi); + mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize); + mono_add_internal_call("Godot.GD::godot_icall_GD_rand_range", (void *)godot_icall_GD_rand_range); + mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed); mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed); mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str); mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var); diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h index 6f846f221d..d4e20e2887 100644 --- a/modules/mono/glue/gd_glue.h +++ b/modules/mono/glue/gd_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,13 +35,13 @@ #include "../mono_gd/gd_mono_marshal.h" -MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes); +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects); -MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type); +MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type); int godot_icall_GD_hash(MonoObject *p_var); -MonoObject *godot_icall_GD_instance_from_id(int p_instance_id); +MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id); void godot_icall_GD_print(MonoArray *p_what); @@ -53,15 +53,25 @@ void godot_icall_GD_prints(MonoArray *p_what); void godot_icall_GD_printt(MonoArray *p_what); -void godot_icall_GD_seed(int p_seed); +float godot_icall_GD_randf(); + +uint32_t godot_icall_GD_randi(); + +void godot_icall_GD_randomize(); + +double godot_icall_GD_rand_range(double from, double to); + +uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed); + +void godot_icall_GD_seed(uint64_t p_seed); MonoString *godot_icall_GD_str(MonoArray *p_what); MonoObject *godot_icall_GD_str2var(MonoString *p_str); -bool godot_icall_GD_type_exists(MonoString *p_type); +MonoBoolean godot_icall_GD_type_exists(MonoString *p_type); -MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var); +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects); MonoString *godot_icall_GD_var2str(MonoObject *p_var); diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index 69c5c6dcdb..1836130b76 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -74,4 +74,6 @@ void godot_register_glue_header_icalls() { } \ Object *m_instance = ci->creation_func(); +#include "arguments_vector.h" + #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp index 422d73104d..2edc43af8d 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/nodepath_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/nodepath_glue.h b/modules/mono/glue/nodepath_glue.h index 92579399a6..2192444336 100644 --- a/modules/mono/glue/nodepath_glue.h +++ b/modules/mono/glue/nodepath_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp index 6c002b5b9d..6a9810d0d4 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/rid_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/rid_glue.h b/modules/mono/glue/rid_glue.h index c725a9b5de..4b593e8a95 100644 --- a/modules/mono/glue/rid_glue.h +++ b/modules/mono/glue/rid_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index e1a2f1affd..e9373fb486 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,12 +68,12 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) { } void godot_register_string_icalls() { - mono_add_internal_call("Godot.String::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer); - mono_add_internal_call("Godot.String::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text); - mono_add_internal_call("Godot.String::godot_icall_String_rfind", (void *)godot_icall_String_rfind); - mono_add_internal_call("Godot.String::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn); - mono_add_internal_call("Godot.String::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer); - mono_add_internal_call("Godot.String::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer); + mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/string_glue.h b/modules/mono/glue/string_glue.h index 8b1553e28b..c9052edd40 100644 --- a/modules/mono/glue/string_glue.h +++ b/modules/mono/glue/string_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 5a6a2d1742..4ad4088514 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,8 @@ #define API_SOLUTION_NAME "GodotSharp" #define CORE_API_ASSEMBLY_NAME "GodotSharp" #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" -#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools" +#define TOOLS_ASSEMBLY_NAME "GodotTools" +#define TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME "GodotTools.ProjectEditor" #define BINDINGS_CLASS_NATIVECALLS "NativeCalls" #define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls" diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index d3fb2cb640..4b2525c692 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,10 @@ #include "editor/editor_settings.h" #endif +#ifdef __ANDROID__ +#include "utils/android_utils.h" +#endif + namespace GodotSharpDirs { String _get_expected_build_config() { @@ -55,6 +59,20 @@ String _get_expected_build_config() { #endif } +String _get_expected_api_build_config() { +#ifdef TOOLS_ENABLED + return "Debug"; +#else + +#ifdef DEBUG_ENABLED + return "Debug"; +#else + return "Release"; +#endif + +#endif +} + String _get_mono_user_dir() { #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { @@ -84,6 +102,7 @@ class _GodotSharpDirs { public: String res_data_dir; String res_metadata_dir; + String res_assemblies_base_dir; String res_assemblies_dir; String res_config_dir; String res_temp_dir; @@ -99,7 +118,6 @@ public: String sln_filepath; String csproj_filepath; - String data_mono_bin_dir; String data_editor_tools_dir; String data_editor_prebuilt_api_dir; #endif @@ -107,11 +125,16 @@ public: String data_mono_etc_dir; String data_mono_lib_dir; +#ifdef WINDOWS_ENABLED + String data_mono_bin_dir; +#endif + private: _GodotSharpDirs() { res_data_dir = "res://.mono"; res_metadata_dir = res_data_dir.plus_file("metadata"); - res_assemblies_dir = res_data_dir.plus_file("assemblies"); + res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); + res_assemblies_dir = res_assemblies_base_dir.plus_file(_get_expected_api_build_config()); res_config_dir = res_data_dir.plus_file("etc").plus_file("mono"); // TODO use paths from csproj @@ -126,15 +149,16 @@ private: mono_solutions_dir = mono_user_dir.plus_file("solutions"); build_logs_dir = mono_user_dir.plus_file("build_logs"); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } String base_path = ProjectSettings::get_singleton()->globalize_path("res://"); - sln_filepath = base_path.plus_file(name + ".sln"); - csproj_filepath = base_path.plus_file(name + ".csproj"); + sln_filepath = base_path.plus_file(appname_safe + ".sln"); + csproj_filepath = base_path.plus_file(appname_safe + ".csproj"); #endif String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); @@ -146,9 +170,17 @@ private: data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api"); String data_mono_root_dir = data_dir_root.plus_file("Mono"); - data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + +#if __ANDROID__ + data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); +#endif + +#ifdef WINDOWS_ENABLED + data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); +#endif #ifdef OSX_ENABLED if (!DirAccess::exists(data_editor_tools_dir)) { @@ -160,7 +192,6 @@ private: } if (!DirAccess::exists(data_mono_root_dir)) { - data_mono_bin_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/bin"); data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc"); data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib"); } @@ -168,15 +199,25 @@ private: #else - String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); - String data_dir_root = exe_dir.plus_file("data_" + appname); + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + String data_dir_root = exe_dir.plus_file("data_" + appname_safe); if (!DirAccess::exists(data_dir_root)) { data_dir_root = exe_dir.plus_file("data_Godot"); } String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + +#if __ANDROID__ + data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); +#endif + +#ifdef WINDOWS_ENABLED + data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); +#endif #ifdef OSX_ENABLED if (!DirAccess::exists(data_mono_root_dir)) { @@ -206,6 +247,10 @@ String get_res_metadata_dir() { return _GodotSharpDirs::get_singleton().res_metadata_dir; } +String get_res_assemblies_base_dir() { + return _GodotSharpDirs::get_singleton().res_assemblies_base_dir; +} + String get_res_assemblies_dir() { return _GodotSharpDirs::get_singleton().res_assemblies_dir; } @@ -251,10 +296,6 @@ String get_project_csproj_path() { return _GodotSharpDirs::get_singleton().csproj_filepath; } -String get_data_mono_bin_dir() { - return _GodotSharpDirs::get_singleton().data_mono_bin_dir; -} - String get_data_editor_tools_dir() { return _GodotSharpDirs::get_singleton().data_editor_tools_dir; } @@ -272,4 +313,10 @@ String get_data_mono_lib_dir() { return _GodotSharpDirs::get_singleton().data_mono_lib_dir; } +#ifdef WINDOWS_ENABLED +String get_data_mono_bin_dir() { + return _GodotSharpDirs::get_singleton().data_mono_bin_dir; +} +#endif + } // namespace GodotSharpDirs diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index 35b564be30..ff51888d1c 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ namespace GodotSharpDirs { String get_res_data_dir(); String get_res_metadata_dir(); +String get_res_assemblies_base_dir(); String get_res_assemblies_dir(); String get_res_config_dir(); String get_res_temp_dir(); @@ -53,7 +54,6 @@ String get_build_logs_dir(); String get_project_sln_path(); String get_project_csproj_path(); -String get_data_mono_bin_dir(); String get_data_editor_tools_dir(); String get_data_editor_prebuilt_api_dir(); #endif @@ -61,6 +61,10 @@ String get_data_editor_prebuilt_api_dir(); String get_data_mono_etc_dir(); String get_data_mono_lib_dir(); +#ifdef WINDOWS_ENABLED +String get_data_mono_bin_dir(); +#endif + } // namespace GodotSharpDirs #endif // GODOTSHARP_DIRS_H diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index f695bddfeb..a9e2136a19 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { void MonoGCHandle::release() { #ifdef DEBUG_ENABLED - CRASH_COND(GDMono::get_singleton() == NULL); + CRASH_COND(!released && GDMono::get_singleton() == NULL); #endif if (!released && GDMono::get_singleton()->is_runtime_initialized()) { diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index e5aa04e2b8..60a1eed212 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,7 @@ class MonoGCHandle : public Reference { - GDCLASS(MonoGCHandle, Reference) + GDCLASS(MonoGCHandle, Reference); bool released; bool weak; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index a80155bd89..096ad0f5e3 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,12 @@ #include "gd_mono.h" +#include <mono/metadata/environment.h> #include <mono/metadata/exception.h> #include <mono/metadata/mono-config.h> #include <mono/metadata/mono-debug.h> #include <mono/metadata/mono-gc.h> +#include <mono/metadata/profiler.h> #include "core/os/dir_access.h" #include "core/os/file_access.h" @@ -50,19 +52,11 @@ #include "gd_mono_utils.h" #ifdef TOOLS_ENABLED -#include "../editor/godotsharp_editor.h" #include "main/main.h" #endif -#ifdef MONO_PRINT_HANDLER_ENABLED -void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { - - if (is_stdout) { - OS::get_singleton()->print(string); - } else { - OS::get_singleton()->printerr(string); - } -} +#ifdef ANDROID_ENABLED +#include "android_mono_config.gen.h" #endif GDMono *GDMono::singleton = NULL; @@ -90,9 +84,17 @@ void setup_runtime_main_args() { mono_runtime_set_main_args(main_args.size(), main_args.ptrw()); } +void gdmono_profiler_init() { + String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd"); + bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false); + if (profiler_enabled) { + mono_profiler_load(profiler_args.utf8()); + } +} + #ifdef DEBUG_ENABLED -static bool _wait_for_debugger_msecs(uint32_t p_msecs) { +bool _wait_for_debugger_msecs(uint32_t p_msecs) { do { if (mono_is_debugger_attached()) @@ -102,7 +104,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) { OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000); - int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick; + uint32_t tdiff = OS::get_singleton()->get_ticks_msec() - last_tick; if (tdiff > p_msecs) { p_msecs = 0; @@ -122,17 +124,23 @@ void gdmono_debug_init() { bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false); int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); + CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); + #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() || ProjectSettings::get_singleton()->get_resource_path().empty() || Main::is_project_manager()) { - return; + if (da_args.size() == 0) + return; } #endif - CharString da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + - ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) - .utf8(); + if (da_args.length() == 0) { + da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) + .utf8(); + } + // --debugger-agent=help const char *options[] = { "--soft-breakpoints", @@ -145,28 +153,71 @@ void gdmono_debug_init() { } // namespace +void GDMono::add_mono_shared_libs_dir_to_path() { + // By default Mono seems to search shared libraries in the following directories: + // Current working directory, @executable_path@ and PATH + // The parent directory of the image file (assembly where the dllimport method is declared) + // @executable_path@/../lib + // @executable_path@/../Libraries (__MACH__ only) + + // This does not work when embedding Mono unless we use the same directory structure. + // To fix this we append the directory containing our shared libraries to PATH. + +#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED) + String path_var("PATH"); + String path_value = OS::get_singleton()->get_environment(path_var); + +#ifdef WINDOWS_ENABLED + path_value += ';'; + + String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir(); +#ifdef TOOLS_ENABLED + if (DirAccess::exists(bundled_bin_dir)) { + path_value += bundled_bin_dir; + } else { + path_value += mono_reg_info.bin_dir; + } +#else + if (DirAccess::exists(bundled_bin_dir)) + path_value += bundled_bin_dir; +#endif // TOOLS_ENABLED + +#else + path_value += ':'; + + String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir(); + if (DirAccess::exists(bundled_lib_dir)) { + path_value += bundled_lib_dir; + } else { + // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms? + } +#endif // WINDOWS_ENABLED + + OS::get_singleton()->set_environment(path_var, path_value); +#endif // WINDOWS_ENABLED || UNIX_ENABLED +} + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); print_verbose("Mono: Initializing module..."); + char *runtime_build_info = mono_get_runtime_build_info(); + print_verbose("Mono JIT compiler version " + String(runtime_build_info)); + mono_free(runtime_build_info); + #ifdef DEBUG_METHODS_ENABLED _initialize_and_check_api_hashes(); #endif GDMonoLog::get_singleton()->initialize(); -#ifdef MONO_PRINT_HANDLER_ENABLED - mono_trace_set_print_handler(gdmono_MonoPrintCallback); - mono_trace_set_printerr_handler(gdmono_MonoPrintCallback); -#endif - String assembly_rootdir; String config_dir; #ifdef TOOLS_ENABLED -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) mono_reg_info = MonoRegUtils::find_mono(); if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { @@ -176,7 +227,7 @@ void GDMono::initialize() { if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { config_dir = mono_reg_info.config_dir; } -#elif OSX_ENABLED +#elif defined(OSX_ENABLED) const char *c_assembly_rootdir = mono_assembly_getrootdir(); const char *c_config_dir = mono_get_config_dir(); @@ -186,9 +237,9 @@ void GDMono::initialize() { locations.push_back("/usr/local/var/homebrew/linked/mono/"); for (int i = 0; i < locations.size(); i++) { - String hint_assembly_rootdir = path_join(locations[i], "lib"); - String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll"); - String hint_config_dir = path_join(locations[i], "etc"); + String hint_assembly_rootdir = path::join(locations[i], "lib"); + String hint_mscorlib_path = path::join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll"); + String hint_config_dir = path::join(locations[i], "etc"); if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) { assembly_rootdir = hint_assembly_rootdir; @@ -204,30 +255,56 @@ void GDMono::initialize() { String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir(); #ifdef TOOLS_ENABLED - if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) { + if (DirAccess::exists(bundled_assembly_rootdir)) { assembly_rootdir = bundled_assembly_rootdir; + } + + if (DirAccess::exists(bundled_config_dir)) { config_dir = bundled_config_dir; } + +#ifdef WINDOWS_ENABLED + if (assembly_rootdir.empty() || config_dir.empty()) { + ERR_PRINT("Cannot find Mono in the registry"); + // Assertion: if they are not set, then they weren't found in the registry + CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0); + } +#endif // WINDOWS_ENABLED + #else // These are always the directories in export templates assembly_rootdir = bundled_assembly_rootdir; config_dir = bundled_config_dir; -#endif +#endif // TOOLS_ENABLED // Leak if we call mono_set_dirs more than once mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL, config_dir.length() ? config_dir.utf8().get_data() : NULL); + add_mono_shared_libs_dir_to_path(); + GDMonoAssembly::initialize(); + gdmono_profiler_init(); + #ifdef DEBUG_ENABLED gdmono_debug_init(); #endif +#ifdef ANDROID_ENABLED + mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data()); +#else mono_config_parse(NULL); +#endif mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); +#ifndef TOOLS_ENABLED + // Export templates only load the Mono runtime if the project uses it + if (!DirAccess::exists("res://.mono")) + return; +#endif + root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); ERR_EXPLAIN("Mono: Failed to initialize runtime"); @@ -245,18 +322,6 @@ void GDMono::initialize() { ERR_EXPLAIN("Mono: Failed to load mscorlib assembly"); ERR_FAIL_COND(!_load_corlib_assembly()); -#ifdef TOOLS_ENABLED - // The tools domain must be loaded here, before the scripts domain. - // Otherwise domain unload on the scripts domain will hang indefinitely. - - ERR_EXPLAIN("Mono: Failed to load tools domain"); - ERR_FAIL_COND(_load_tools_domain() != OK); - - // TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation) - ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly"); - ERR_FAIL_COND(!_load_editor_tools_assembly()); -#endif - ERR_EXPLAIN("Mono: Failed to load scripts domain"); ERR_FAIL_COND(_load_scripts_domain() != OK); @@ -268,100 +333,99 @@ void GDMono::initialize() { _register_internal_calls(); - // The following assemblies are not required at initialization -#ifdef MONO_GLUE_ENABLED - if (_load_api_assemblies()) { - // Everything is fine with the api assemblies, load the project assembly - _load_project_assembly(); - } else { - if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) -#ifdef TOOLS_ENABLED - || (editor_api_assembly && editor_api_assembly_out_of_sync) + print_verbose("Mono: INITIALIZED"); +} + +void GDMono::initialize_load_assemblies() { + +#ifndef MONO_GLUE_ENABLED + ERR_EXPLAIN("Mono: This binary was built with `mono_glue=no`; cannot load assemblies"); + CRASH_NOW(); #endif - ) { -#ifdef TOOLS_ENABLED - // The assembly was successfully loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the - // metadata, so we invalidate the version in the metadata and unload the script domain. - - if (core_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Core API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } - if (editor_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Editor API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); - } + // Load assemblies. The API and tools assemblies are required, + // the application is aborted if these assemblies cannot be loaded. - print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies."); + _load_api_assemblies(); - Error err = _unload_scripts_domain(); - if (err != OK) { - WARN_PRINT("Mono: Failed to unload scripts domain"); - } -#else - ERR_PRINT("The loaded API assembly is invalid"); - CRASH_NOW(); -#endif // TOOLS_ENABLED - } +#if defined(TOOLS_ENABLED) + if (!_load_tools_assemblies()) { + ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies"); + CRASH_NOW(); } -#else - print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif // MONO_GLUE_ENABLED +#endif - print_verbose("Mono: INITIALIZED"); + // Load the project's main assembly. This doesn't necessarily need to succeed. + // The game may not be using .NET at all, or if the project does use .NET and + // we're running in the editor, it may just happen to be it wasn't built yet. + if (!_load_project_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + print_error("Mono: Failed to load project assembly"); + } +} + +bool GDMono::_are_api_assemblies_out_of_sync() { + bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated); +#ifdef TOOLS_ENABLED + if (!out_of_sync) + out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync; +#endif + return out_of_sync; } -#ifdef MONO_GLUE_ENABLED namespace GodotSharpBindings { +#ifdef MONO_GLUE_ENABLED uint64_t get_core_api_hash(); #ifdef TOOLS_ENABLED uint64_t get_editor_api_hash(); -#endif // TOOLS_ENABLED +#endif uint32_t get_bindings_version(); void register_generated_icalls(); -} // namespace GodotSharpBindings -#endif -void GDMono::_register_internal_calls() { -#ifdef MONO_GLUE_ENABLED - GodotSharpBindings::register_generated_icalls(); -#endif +#else +uint64_t get_core_api_hash() { + CRASH_NOW(); + GD_UNREACHABLE(); +} #ifdef TOOLS_ENABLED - GodotSharpEditor::register_internal_calls(); +uint64_t get_editor_api_hash() { + CRASH_NOW(); + GD_UNREACHABLE(); +} #endif +uint32_t get_bindings_version() { + CRASH_NOW(); + GD_UNREACHABLE(); } -#ifdef DEBUG_METHODS_ENABLED -void GDMono::_initialize_and_check_api_hashes() { +void register_generated_icalls() { + /* Fine, just do nothing */ +} + +#endif // MONO_GLUE_ENABLED +} // namespace GodotSharpBindings + +void GDMono::_register_internal_calls() { + GodotSharpBindings::register_generated_icalls(); +} - api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); +void GDMono::_initialize_and_check_api_hashes() { #ifdef MONO_GLUE_ENABLED - if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { + if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch!"); } -#endif #ifdef TOOLS_ENABLED - api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); - -#ifdef MONO_GLUE_ENABLED - if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { + if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) { ERR_PRINT("Mono: Editor API hash mismatch!"); } -#endif - #endif // TOOLS_ENABLED +#endif // MONO_GLUE_ENABLED } -#endif // DEBUG_METHODS_ENABLED void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { @@ -484,39 +548,120 @@ bool GDMono::_load_corlib_assembly() { return success; } -bool GDMono::_load_core_api_assembly() { +#ifdef TOOLS_ENABLED +bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type) { - if (core_api_assembly) - return true; + bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ? + GDMono::get_singleton()->core_api_assembly_out_of_sync : + GDMono::get_singleton()->editor_api_assembly_out_of_sync; -#ifdef TOOLS_ENABLED - if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { - print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated"); - return false; + String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + String dst_dir = GodotSharpDirs::get_res_assemblies_dir(); + + String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + + // Create destination directory if needed + if (!DirAccess::exists(dst_dir)) { + DirAccess *da = DirAccess::create_for_path(dst_dir); + Error err = da->make_dir_recursive(dst_dir); + memdelete(da); + + if (err != OK) { + ERR_PRINTS("Failed to create destination directory for the API assemblies. Error: " + itos(err)); + return false; + } } + + String assembly_file = assembly_name + ".dll"; + String assembly_src = src_dir.plus_file(assembly_file); + String assembly_dst = dst_dir.plus_file(assembly_file); + + if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + String xml_file = assembly_name + ".xml"; + if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) + WARN_PRINTS("Failed to copy " + xml_file); + + String pdb_file = assembly_name + ".pdb"; + if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) + WARN_PRINTS("Failed to copy " + pdb_file); + + Error err = da->copy(assembly_src, assembly_dst); + + if (err != OK) { + ERR_PRINTS("Failed to copy " + assembly_file); + return false; + } + + api_assembly_out_of_sync = false; + } + + return true; +} + +String GDMono::update_api_assemblies_from_prebuilt() { + +#define FAIL_REASON(m_out_of_sync, m_prebuilt_exist) \ + ( \ + (m_out_of_sync ? \ + String("The assembly is invalidated") : \ + String("The assembly was not found")) + \ + (m_prebuilt_exist ? \ + String(" and the prebuilt assemblies are missing") : \ + String(" and we failed to copy the prebuilt assemblies"))) + + bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync; + + String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) + return String(); // No update needed + + String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) + return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ false); + + // Copy the prebuilt Api + if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE) || !copy_prebuilt_api_assembly(APIAssembly::API_EDITOR)) + return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ true); + + return String(); // Updated successfully + +#undef FAIL_REASON +} #endif - String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); +bool GDMono::_load_core_api_assembly() { - if (!FileAccess::exists(assembly_path)) - return false; + if (core_api_assembly) + return true; - bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME, - assembly_path, - &core_api_assembly); +#ifdef TOOLS_ENABLED + // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + bool success = FileAccess::exists(assembly_path) && + load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly); +#else + bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly); +#endif if (success) { -#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; if (!core_api_assembly_out_of_sync) { GDMonoUtils::update_godot_api_cache(); + + _install_trace_listener(); } -#else - GDMonoUtils::update_godot_api_cache(); -#endif + } else { + core_api_assembly_out_of_sync = false; } return success; @@ -528,70 +673,25 @@ bool GDMono::_load_editor_api_assembly() { if (editor_api_assembly) return true; - if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { - print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated"); - return false; - } - + // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - - if (!FileAccess::exists(assembly_path)) - return false; - - bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME, - assembly_path, - &editor_api_assembly); + bool success = FileAccess::exists(assembly_path) && + load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly); if (success) { -#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; -#endif - } - - return success; -} -#endif - -#ifdef TOOLS_ENABLED -bool GDMono::_load_editor_tools_assembly() { - - if (editor_tools_assembly) - return true; - - _GDMONO_SCOPE_DOMAIN_(tools_domain) - - return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); -} -#endif - -bool GDMono::_load_project_assembly() { - - if (project_assembly) - return true; - - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; - } - - bool success = load_assembly(name, &project_assembly); - - if (success) { - mono_assembly_set_main(project_assembly->get_assembly()); - - CSharpLanguage::get_singleton()->project_assembly_loaded(); } else { - if (OS::get_singleton()->is_stdout_verbose()) - print_error("Mono: Failed to load project assembly"); + editor_api_assembly_out_of_sync = false; } return success; } +#endif -bool GDMono::_load_api_assemblies() { +bool GDMono::_try_load_api_assemblies() { if (!_load_core_api_assembly()) { if (OS::get_singleton()->is_stdout_verbose()) @@ -616,69 +716,106 @@ bool GDMono::_load_api_assemblies() { return true; } -#ifdef TOOLS_ENABLED -String GDMono::_get_api_assembly_metadata_path() { +void GDMono::_load_api_assemblies() { - return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg"); -} + if (!_try_load_api_assemblies()) { + // The API assemblies are out of sync. Fine, try one more time, but this time + // update them from the prebuilt assemblies directory before trying to load them. -void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) { + // 1. Unload the scripts domain + if (_unload_scripts_domain() != OK) { + ERR_EXPLAIN("Mono: Failed to unload scripts domain"); + CRASH_NOW(); + } - String section = APIAssembly::to_string(p_api_type); - String path = _get_api_assembly_metadata_path(); + // 2. Update the API assemblies + String update_error = update_api_assemblies_from_prebuilt(); + if (!update_error.empty()) { + ERR_EXPLAIN(update_error); + CRASH_NOW(); + } - Ref<ConfigFile> metadata; - metadata.instance(); - metadata->load(path); + // 3. Load the scripts domain again + if (_load_scripts_domain() != OK) { + ERR_EXPLAIN("Mono: Failed to load scripts domain"); + CRASH_NOW(); + } - metadata->set_value(section, "invalidated", p_invalidated); + // 4. Try loading the updated assemblies + if (!_try_load_api_assemblies()) { + // welp... too bad - String assembly_path = GodotSharpDirs::get_res_assemblies_dir() - .plus_file(p_api_type == APIAssembly::API_CORE ? - CORE_API_ASSEMBLY_NAME ".dll" : - EDITOR_API_ASSEMBLY_NAME ".dll"); + if (_are_api_assemblies_out_of_sync()) { + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync"); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed"); + } - ERR_FAIL_COND(!FileAccess::exists(assembly_path)); +#ifdef TOOLS_ENABLED + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync"); + } +#endif - uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + CRASH_NOW(); + } else { + ERR_EXPLAIN("Failed to load one of the API assemblies"); + CRASH_NOW(); + } + } + } +} - metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time)); +#ifdef TOOLS_ENABLED +bool GDMono::_load_tools_assemblies() { - String dir = path.get_base_dir(); - if (!DirAccess::exists(dir)) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND(!da); - Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir)); - ERR_FAIL_COND(err != OK); - } + if (tools_assembly && tools_project_editor_assembly) + return true; + + bool success = load_assembly(TOOLS_ASSEMBLY_NAME, &tools_assembly) && + load_assembly(TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME, &tools_project_editor_assembly); - Error save_err = metadata->save(path); - ERR_FAIL_COND(save_err != OK); + return success; } +#endif -bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) { +bool GDMono::_load_project_assembly() { - String section = APIAssembly::to_string(p_api_type); + if (project_assembly) + return true; - Ref<ConfigFile> metadata; - metadata.instance(); - metadata->load(_get_api_assembly_metadata_path()); + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; + } - String assembly_path = GodotSharpDirs::get_res_assemblies_dir() - .plus_file(p_api_type == APIAssembly::API_CORE ? - CORE_API_ASSEMBLY_NAME ".dll" : - EDITOR_API_ASSEMBLY_NAME ".dll"); + bool success = load_assembly(appname_safe, &project_assembly); - if (!FileAccess::exists(assembly_path)) - return false; + if (success) { + mono_assembly_set_main(project_assembly->get_assembly()); + } - uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + return success; +} - uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0); +void GDMono::_install_trace_listener() { - return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time; -} +#ifdef DEBUG_ENABLED + // Install the trace listener now before the project assembly is loaded + typedef void (*DebuggingUtils_InstallTraceListener)(MonoObject **); + MonoException *exc = NULL; + GDMonoClass *debug_utils = core_api_assembly->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); + DebuggingUtils_InstallTraceListener install_func = + (DebuggingUtils_InstallTraceListener)debug_utils->get_method_thunk("InstallTraceListener"); + install_func((MonoObject **)&exc); + if (exc) { + ERR_PRINT("Failed to install System.Diagnostics.Trace listener"); + GDMonoUtils::debug_print_unhandled_exception(exc); + } #endif +} Error GDMono::_load_scripts_domain() { @@ -705,8 +842,6 @@ Error GDMono::_unload_scripts_domain() { if (mono_domain_get() != root_domain) mono_domain_set(root_domain, true); - mono_gc_collect(mono_gc_max_generation()); - finalizing_scripts_domain = true; if (!mono_domain_finalize(scripts_domain, 2000)) { @@ -717,17 +852,18 @@ Error GDMono::_unload_scripts_domain() { mono_gc_collect(mono_gc_max_generation()); + GDMonoUtils::clear_godot_api_cache(); + _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; + tools_assembly = NULL; + tools_project_editor_assembly = NULL; #endif - core_api_assembly_out_of_sync = false; - editor_api_assembly_out_of_sync = false; - MonoDomain *domain = scripts_domain; scripts_domain = NULL; @@ -743,23 +879,7 @@ Error GDMono::_unload_scripts_domain() { return OK; } -#ifdef TOOLS_ENABLED -Error GDMono::_load_tools_domain() { - - ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG); - - print_verbose("Mono: Loading tools domain..."); - - tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain"); - - ERR_EXPLAIN("Mono: Could not create tools app domain"); - ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE); - - return OK; -} -#endif - -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD Error GDMono::reload_scripts_domain() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); @@ -772,50 +892,33 @@ Error GDMono::reload_scripts_domain() { } } + CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded(); + Error err = _load_scripts_domain(); if (err != OK) { ERR_PRINT("Mono: Failed to load scripts domain"); return err; } -#ifdef MONO_GLUE_ENABLED - if (!_load_api_assemblies()) { - if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) || - (editor_api_assembly && editor_api_assembly_out_of_sync)) { - // The assembly was successfully loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the - // metadata, so we invalidate the version in the metadata and unload the script domain. - - if (core_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Core API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } + // Load assemblies. The API and tools assemblies are required, + // the application is aborted if these assemblies cannot be loaded. - if (editor_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Editor API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); - } + _load_api_assemblies(); - Error err = _unload_scripts_domain(); - if (err != OK) { - WARN_PRINT("Mono: Failed to unload scripts domain"); - } - - return ERR_CANT_RESOLVE; - } else { - return ERR_CANT_OPEN; - } +#if defined(TOOLS_ENABLED) + if (!_load_tools_assemblies()) { + ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies"); + CRASH_NOW(); } +#endif + // Load the project's main assembly. Here, during hot-reloading, we do + // consider failing to load the project's main assembly to be an error. + // However, unlike the API and tools assemblies, the application can continue working. if (!_load_project_assembly()) { + print_error("Mono: Failed to load project assembly"); return ERR_CANT_OPEN; } -#else - print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif // MONO_GLUE_ENABLED return OK; } @@ -824,6 +927,7 @@ Error GDMono::reload_scripts_domain() { Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { CRASH_COND(p_domain == NULL); + CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead String domain_name = mono_domain_get_friendly_name(p_domain); @@ -832,10 +936,10 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { if (mono_domain_get() == p_domain) mono_domain_set(root_domain, true); - mono_gc_collect(mono_gc_max_generation()); if (!mono_domain_finalize(p_domain, 2000)) { ERR_PRINT("Mono: Domain finalization timeout"); } + mono_gc_collect(mono_gc_max_generation()); _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); @@ -845,7 +949,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { if (exc) { ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`"); - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); return FAILED; } @@ -876,6 +980,22 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { return NULL; } +GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) { + + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; + + const String *k = NULL; + while ((k = domain_assemblies.next(k))) { + GDMonoAssembly *assembly = domain_assemblies.get(*k); + GDMonoClass *klass = assembly->get_class(p_namespace, p_name); + if (klass) + return klass; + } + + return NULL; +} + void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; @@ -899,8 +1019,10 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { if (ScriptDebugger::get_singleton()) ScriptDebugger::get_singleton()->idle_poll(); #endif - abort(); - _UNREACHABLE_(); + + exit(mono_environment_exitcode_get()); + + GD_UNREACHABLE(); } GDMono::GDMono() { @@ -914,38 +1036,34 @@ GDMono::GDMono() { root_domain = NULL; scripts_domain = NULL; -#ifdef TOOLS_ENABLED - tools_domain = NULL; -#endif core_api_assembly_out_of_sync = false; +#ifdef TOOLS_ENABLED editor_api_assembly_out_of_sync = false; +#endif corlib_assembly = NULL; core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; - editor_tools_assembly = NULL; + tools_assembly = NULL; + tools_project_editor_assembly = NULL; #endif -#ifdef DEBUG_METHODS_ENABLED api_core_hash = 0; #ifdef TOOLS_ENABLED api_editor_hash = 0; #endif -#endif } GDMono::~GDMono() { if (is_runtime_initialized()) { - if (scripts_domain) { - Error err = _unload_scripts_domain(); if (err != OK) { - WARN_PRINT("Mono: Failed to unload scripts domain"); + ERR_PRINT("Mono: Failed to unload scripts domain"); } } @@ -960,12 +1078,12 @@ GDMono::~GDMono() { } assemblies.clear(); - GDMonoUtils::clear_cache(); - print_verbose("Mono: Runtime cleanup..."); mono_jit_cleanup(root_domain); + print_verbose("Mono: Finalized"); + runtime_initialized = false; } @@ -996,14 +1114,14 @@ int32_t _GodotSharp::get_domain_id() { int32_t _GodotSharp::get_scripts_domain_id() { - MonoDomain *domain = SCRIPTS_DOMAIN; + MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain(); CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method return mono_domain_get_id(domain); } bool _GodotSharp::is_scripts_domain_loaded() { - return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL; + return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL; } bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { @@ -1025,7 +1143,7 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { if (!p_domain) return true; - if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain()) + if (p_domain == GDMono::get_singleton()->get_scripts_domain() && GDMono::get_singleton()->is_finalizing_scripts_domain()) return true; return mono_domain_is_unloading(p_domain); } @@ -1040,6 +1158,12 @@ bool _GodotSharp::is_runtime_initialized() { return GDMono::get_singleton()->is_runtime_initialized(); } +void _GodotSharp::_reload_assemblies(bool p_soft_reload) { +#ifdef GD_MONO_HOT_RELOAD + CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); +#endif +} + void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); @@ -1052,22 +1176,15 @@ void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down); ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized); + ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies); } _GodotSharp::_GodotSharp() { singleton = this; - queue_empty = true; -#ifndef NO_THREADS - queue_mutex = Mutex::create(); -#endif } _GodotSharp::~_GodotSharp() { singleton = NULL; - - if (queue_mutex) { - memdelete(queue_mutex); - } } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index fd9551b6de..deebe5fd50 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -78,11 +78,6 @@ struct Version { String to_string(Type p_type); } // namespace APIAssembly -#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain() -#ifdef TOOLS_ENABLED -#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain() -#endif - class GDMono { bool runtime_initialized; @@ -90,55 +85,50 @@ class GDMono { MonoDomain *root_domain; MonoDomain *scripts_domain; -#ifdef TOOLS_ENABLED - MonoDomain *tools_domain; -#endif bool core_api_assembly_out_of_sync; +#ifdef TOOLS_ENABLED bool editor_api_assembly_out_of_sync; +#endif GDMonoAssembly *corlib_assembly; GDMonoAssembly *core_api_assembly; GDMonoAssembly *project_assembly; #ifdef TOOLS_ENABLED GDMonoAssembly *editor_api_assembly; - GDMonoAssembly *editor_tools_assembly; + GDMonoAssembly *tools_assembly; + GDMonoAssembly *tools_project_editor_assembly; #endif HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; void _domain_assemblies_cleanup(uint32_t p_domain_id); + bool _are_api_assemblies_out_of_sync(); + bool _load_corlib_assembly(); bool _load_core_api_assembly(); #ifdef TOOLS_ENABLED bool _load_editor_api_assembly(); - bool _load_editor_tools_assembly(); + bool _load_tools_assemblies(); #endif bool _load_project_assembly(); - bool _load_api_assemblies(); + bool _try_load_api_assemblies(); + void _load_api_assemblies(); -#ifdef TOOLS_ENABLED - String _get_api_assembly_metadata_path(); -#endif + void _install_trace_listener(); void _register_internal_calls(); Error _load_scripts_domain(); Error _unload_scripts_domain(); -#ifdef TOOLS_ENABLED - Error _load_tools_domain(); -#endif - -#ifdef DEBUG_METHODS_ENABLED uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; #endif void _initialize_and_check_api_hashes(); -#endif GDMonoLog *gdmono_log; @@ -146,20 +136,28 @@ class GDMono { MonoRegInfo mono_reg_info; #endif + void add_mono_shared_libs_dir_to_path(); + protected: static GDMono *singleton; public: -#ifdef DEBUG_METHODS_ENABLED - uint64_t get_api_core_hash() { return api_core_hash; } + uint64_t get_api_core_hash() { + if (api_core_hash == 0) + api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); + return api_core_hash; + } #ifdef TOOLS_ENABLED - uint64_t get_api_editor_hash() { return api_editor_hash; } -#endif + uint64_t get_api_editor_hash() { + if (api_editor_hash == 0) + api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); + return api_editor_hash; + } #endif #ifdef TOOLS_ENABLED - void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated); - bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type); + bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type); + String update_api_assemblies_from_prebuilt(); #endif static GDMono *get_singleton() { return singleton; } @@ -175,16 +173,14 @@ public: _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; } _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } -#ifdef TOOLS_ENABLED - _FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; } -#endif _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } #ifdef TOOLS_ENABLED _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; } #endif #if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) @@ -192,8 +188,9 @@ public: #endif GDMonoClass *get_class(MonoClass *p_raw_class); + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); -#ifdef TOOLS_ENABLED +#ifdef GD_MONO_HOT_RELOAD Error reload_scripts_domain(); #endif @@ -204,6 +201,7 @@ public: Error finalize_and_unload_domain(MonoDomain *p_domain); void initialize(); + void initialize_load_assemblies(); GDMono(); ~GDMono(); @@ -257,7 +255,7 @@ public: (void)__gdmono__scope__exit__domain__unload__; class _GodotSharp : public Object { - GDCLASS(_GodotSharp, Object) + GDCLASS(_GodotSharp, Object); friend class GDMono; @@ -266,11 +264,7 @@ class _GodotSharp : public Object { List<NodePath *> np_delete_queue; List<RID *> rid_delete_queue; - bool queue_empty; - -#ifndef NO_THREADS - Mutex *queue_mutex; -#endif + void _reload_assemblies(bool p_soft_reload); protected: static _GodotSharp *singleton; diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 72b0204672..761c7f6fcb 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,11 +46,17 @@ bool GDMonoAssembly::in_preload = false; Vector<String> GDMonoAssembly::search_dirs; -void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) { +void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) { - const char *rootdir = mono_assembly_getrootdir(); - if (rootdir) { - String framework_dir = String(rootdir).plus_file("mono").plus_file("4.5"); + String framework_dir; + + if (!p_custom_bcl_dir.empty()) { + framework_dir = p_custom_bcl_dir; + } else if (mono_assembly_getrootdir()) { + framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5"); + } + + if (!framework_dir.empty()) { r_search_dirs.push_back(framework_dir); r_search_dirs.push_back(framework_dir.plus_file("Facades")); } @@ -61,11 +67,22 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); } - r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); + if (p_custom_config.empty()) { + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); + } else { + String api_config = p_custom_config == "Release" ? "Release" : "Debug"; + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config)); + } + + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir()); r_search_dirs.push_back(OS::get_singleton()->get_resource_dir()); r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); + #ifdef TOOLS_ENABLED r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir()); + + // For GodotTools to find the api assemblies + r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug")); #endif } @@ -264,7 +281,18 @@ Error GDMonoAssembly::load(bool p_refonly) { Vector<uint8_t> data = FileAccess::get_file_as_array(path); ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ); - String image_filename = ProjectSettings::get_singleton()->globalize_path(path); + String image_filename; + +#ifdef ANDROID_ENABLED + if (path.begins_with("res://")) { + image_filename = path.substr(6, path.length()); + } else { + image_filename = ProjectSettings::get_singleton()->globalize_path(path); + } +#else + // FIXME: globalize_path does not work on exported games + image_filename = ProjectSettings::get_singleton()->globalize_path(path); +#endif MonoImageOpenStatus status = MONO_IMAGE_OK; @@ -277,6 +305,7 @@ Error GDMonoAssembly::load(bool p_refonly) { ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN); #ifdef DEBUG_ENABLED + Vector<uint8_t> pdb_data; String pdb_path(path + ".pdb"); if (!FileAccess::exists(pdb_path)) { @@ -286,8 +315,9 @@ Error GDMonoAssembly::load(bool p_refonly) { goto no_pdb; } - pdb_data.clear(); pdb_data = FileAccess::get_file_as_array(pdb_path); + + // mono_debug_close_image doesn't seem to be needed mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size()); no_pdb: @@ -306,6 +336,9 @@ no_pdb: ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); + // Decrement refcount which was previously incremented by mono_image_open_from_data_with_name + mono_image_close(image); + loaded = true; modified_time = last_modified_time; @@ -321,8 +354,6 @@ Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) { image = p_image; - mono_image_addref(image); - loaded = true; return OK; @@ -332,13 +363,6 @@ void GDMonoAssembly::unload() { ERR_FAIL_COND(!loaded); -#ifdef DEBUG_ENABLED - if (pdb_data.size()) { - mono_debug_close_image(image); - pdb_data.clear(); - } -#endif - for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) { memdelete(E->value()); } @@ -346,8 +370,6 @@ void GDMonoAssembly::unload() { cached_classes.clear(); cached_raw.clear(); - mono_image_close(image); - assembly = NULL; image = NULL; loaded = false; diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 2af40ec901..39749dfc1d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -84,10 +84,6 @@ class GDMonoAssembly { bool gdobject_class_cache_updated; Map<StringName, GDMonoClass *> gdobject_class_cache; -#ifdef DEBUG_ENABLED - Vector<uint8_t> pdb_data; -#endif - static bool no_search; static bool in_preload; static Vector<String> search_dirs; @@ -126,7 +122,7 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_class); - static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String()); + static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String()); static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 4e515cde28..1c10d3c8eb 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) { MonoException *exc = NULL; MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return GDMonoMarshal::mono_string_to_godot(str); } @@ -55,26 +55,32 @@ String GDMonoClass::get_full_name() const { } MonoType *GDMonoClass::get_mono_type() { - // Care, you cannot compare MonoType pointers + // Careful, you cannot compare two MonoType*. + // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*? return get_mono_type(mono_class); } -bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { +uint32_t GDMonoClass::get_flags() const { + return mono_class_get_flags(mono_class); +} + +bool GDMonoClass::is_static() const { + uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED; + return (get_flags() & static_class_flags) == static_class_flags; +} +bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { return mono_class_is_assignable_from(mono_class, p_from->mono_class); } GDMonoClass *GDMonoClass::get_parent_class() { + MonoClass *parent_mono_class = mono_class_get_parent(mono_class); + return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : NULL; +} - if (assembly) { - MonoClass *parent_mono_class = mono_class_get_parent(mono_class); - - if (parent_mono_class) { - return GDMono::get_singleton()->get_class(parent_mono_class); - } - } - - return NULL; +GDMonoClass *GDMonoClass::get_nesting_class() { + MonoClass *nesting_type = mono_class_get_nesting_type(mono_class); + return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : NULL; } #ifdef TOOLS_ENABLED @@ -151,6 +157,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { StringName name = mono_method_get_name(raw_method); + // get_method implicitly fetches methods and adds them to this->methods GDMonoMethod *method = get_method(raw_method, name); ERR_CONTINUE(!method); @@ -251,6 +258,11 @@ bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) { return get_fetched_method_unknown_params(p_name) != NULL; } +bool GDMonoClass::implements_interface(GDMonoClass *p_interface) { + + return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr()); +} + GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { MethodKey key = MethodKey(p_name, p_params_count); @@ -449,6 +461,21 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() { return delegates_list; } +const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() { + + if (!method_list_fetched) { + void *iter = NULL; + MonoMethod *raw_method = NULL; + while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { + method_list.push_back(memnew(GDMonoMethod(mono_method_get_name(raw_method), raw_method))); + } + + method_list_fetched = true; + } + + return method_list; +} + GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { namespace_name = p_namespace; @@ -460,6 +487,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name attributes = NULL; methods_fetched = false; + method_list_fetched = false; fields_fetched = false; properties_fetched = false; delegates_fetched = false; @@ -512,4 +540,8 @@ GDMonoClass::~GDMonoClass() { methods.clear(); } + + for (int i = 0; i < method_list.size(); ++i) { + memdelete(method_list[i]); + } } diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 477305d503..40e1574927 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,9 +79,14 @@ class GDMonoClass { bool attrs_fetched; MonoCustomAttrInfo *attributes; + // This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions. + // Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming. bool methods_fetched; HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods; + bool method_list_fetched; + Vector<GDMonoMethod *> method_list; + bool fields_fetched; Map<StringName, GDMonoField *> fields; Vector<GDMonoField *> fields_list; @@ -104,6 +109,9 @@ public: String get_full_name() const; MonoType *get_mono_type(); + uint32_t get_flags() const; + bool is_static() const; + bool is_assignable_from(GDMonoClass *p_from) const; _FORCE_INLINE_ StringName get_namespace() const { return namespace_name; } @@ -113,6 +121,7 @@ public: _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } GDMonoClass *get_parent_class(); + GDMonoClass *get_nesting_class(); #ifdef TOOLS_ENABLED Vector<MonoClassField *> get_enum_fields(); @@ -127,6 +136,8 @@ public: void fetch_attributes(); void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); + bool implements_interface(GDMonoClass *p_interface); + GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); @@ -143,6 +154,8 @@ public: const Vector<GDMonoClass *> &get_all_delegates(); + const Vector<GDMonoMethod *> &get_all_methods(); + ~GDMonoClass(); }; diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index f09e93e662..3999658f93 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ #define SET_FROM_ARRAY(m_type) \ { \ MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \ - mono_field_set_value(p_object, mono_field, &managed); \ + mono_field_set_value(p_object, mono_field, managed); \ } switch (type.type_encoding) { @@ -313,6 +313,44 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + if (GDMonoUtils::tools_godot_api_check()) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } else { + MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + } + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); ERR_FAIL(); } break; @@ -418,31 +456,55 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); - - MonoException *exc = NULL; + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } - exc = NULL; + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + // The order in which we check the following interfaces is very important (dictionaries and generics first) - if (is_array) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); mono_field_set_value(p_object, mono_field, managed); break; } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + if (GDMonoUtils::tools_godot_api_check()) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } else { + MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); + mono_field_set_value(p_object, mono_field, managed); + break; + } + } } break; default: { @@ -505,20 +567,20 @@ bool GDMonoField::is_static() { return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoField::get_visibility() { +IMonoClassMember::Visibility GDMonoField::get_visibility() { switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) { case MONO_FIELD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_FIELD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_FIELD_ATTR_ASSEMBLY: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_FIELD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_FIELD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index a6b368c4d6..a7727ddf34 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,10 +32,10 @@ #define GDMONOFIELD_H #include "gd_mono.h" -#include "gd_mono_class_member.h" #include "gd_mono_header.h" +#include "i_mono_class_member.h" -class GDMonoField : public GDMonoClassMember { +class GDMonoField : public IMonoClassMember { GDMonoClass *owner; MonoClassField *mono_field; @@ -47,15 +47,17 @@ class GDMonoField : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_FIELD; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } - virtual StringName get_name() { return name; } + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual StringName get_name() const GD_FINAL { return name; } - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; + + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); _FORCE_INLINE_ ManagedType get_type() const { return type; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 51d0c9b356..d7962eac8b 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,24 +35,12 @@ class GDMonoAssembly; class GDMonoClass; -class GDMonoClassMember; class GDMonoField; -class GDMonoProperty; class GDMonoMethod; +class GDMonoProperty; -struct ManagedType { - int type_encoding; - GDMonoClass *type_class; - - ManagedType() : - type_encoding(0), - type_class(NULL) { - } +class IMonoClassMember; - ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : - type_encoding(p_type_encoding), - type_class(p_type_class) { - } -}; +#include "managed_type.h" #endif // GD_MONO_HEADER_H diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index 505c030ca1..a84332d4cd 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,8 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" #include "../utils/thread_local.h" +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #include <mono/metadata/exception.h> @@ -55,24 +57,60 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { CRASH_COND(!klass); - Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : - MonoGCHandle::create_strong(managed); + GDMonoClass *native = GDMonoUtils::get_class_native_base(klass); - Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass); + CRASH_COND(native == NULL); + + if (native == klass) { + // If it's just a wrapper Godot class and not a custom inheriting class, then attach a + // script binding instead. One of the advantages of this is that if a script is attached + // later and it's not a C# script, then the managed object won't have to be disposed. + // Another reason for doing this is that this instance could outlive CSharpLanguage, which would + // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621 + + CSharpScriptBinding script_binding; + + script_binding.inited = true; + script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass); + script_binding.wrapper_class = klass; + script_binding.gchandle = MonoGCHandle::create_strong(managed); + script_binding.owner = unmanaged; + + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + ref->reference(); + } + + // The object was just created, no script instance binding should have been attached + CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())); + + void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding); + + // Should be thread safe because the object was just created and nothing else should be referencing it + unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data); + + return; + } + + Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed); + + Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native); CRASH_COND(script.is_null()); ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); unmanaged->set_script_and_instance(script.get_ref_ptr(), si); - - return; } void unhandled_exception(MonoException *p_exc) { mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well - abort(); - _UNREACHABLE_(); + // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders + GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL); + GD_UNREACHABLE(); } } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index 50cadcedf6..2d77bde27c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,7 +45,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); * Do not call this function directly. * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. */ -_NO_RETURN_ void unhandled_exception(MonoException *p_exc); +GD_NORETURN void unhandled_exception(MonoException *p_exc); } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index b7bb2cb2d6..a6e04e561d 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "core/os/os.h" #include "../godotsharp_dirs.h" +#include "../utils/string_utils.h" static int log_level_get_id(const char *p_log_level) { @@ -52,7 +53,7 @@ static int log_level_get_id(const char *p_log_level) { return -1; } -void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { +static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { FileAccess *f = GDMonoLog::get_singleton()->get_log_file(); @@ -72,8 +73,11 @@ void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const if (fatal) { ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n"); - // If we were to abort without flushing, the log wouldn't get written. + // Make sure to flush before aborting f->flush(); + f->close(); + memdelete(f); + abort(); } } @@ -93,17 +97,9 @@ bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { return true; } -void GDMonoLog::_open_log_file(const String &p_file_path) { - - log_file = FileAccess::open(p_file_path, FileAccess::WRITE); - - ERR_EXPLAIN("Failed to create log file"); - ERR_FAIL_COND(!log_file); -} - void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { - static const uint64_t MAX_SECS = 5 * 86400; + static const uint64_t MAX_SECS = 5 * 86400; // 5 days DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND(!da); @@ -120,10 +116,9 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { if (!current.ends_with(".txt")) continue; - String name = current.get_basename(); - uint64_t unixtime = (uint64_t)name.to_int64(); + uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current)); - if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) { + if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) { da->remove(current); } } @@ -133,27 +128,48 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { void GDMonoLog::initialize() { + CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); + + if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) { + ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): " + log_level.get_data()); + log_level = CharString(); + } + + if (log_level.length() == 0) { #ifdef DEBUG_ENABLED - const char *log_level = "debug"; + log_level = String("info").utf8(); #else - const char *log_level = "warning"; + log_level = String("warning").utf8(); #endif + } String logs_dir = GodotSharpDirs::get_mono_logs_dir(); if (_try_create_logs_dir(logs_dir)) { _delete_old_log_files(logs_dir); - log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt"); - _open_log_file(log_file_path); + OS::Date date_now = OS::get_singleton()->get_date(); + OS::Time time_now = OS::get_singleton()->get_time(); + int pid = OS::get_singleton()->get_process_id(); + + String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt", + date_now.year, date_now.month, date_now.day, + time_now.hour, time_now.min, time_now.sec, pid); + + log_file_path = logs_dir.plus_file(log_file_name); + + log_file = FileAccess::open(log_file_path, FileAccess::WRITE); + if (!log_file) { + ERR_PRINT("Mono: Cannot create log file"); + } } - mono_trace_set_level_string(log_level); - log_level_id = log_level_get_id(log_level); + mono_trace_set_level_string(log_level.get_data()); + log_level_id = log_level_get_id(log_level.get_data()); if (log_file) { - print_verbose("Mono: Logfile is " + log_file_path); - mono_trace_set_log_handler(gdmono_MonoLogCallback, this); + OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data()); + mono_trace_set_log_handler(mono_log_callback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); } diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index 3b4ff07b7b..91d0557aa3 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,6 @@ class GDMonoLog { String log_file_path; bool _try_create_logs_dir(const String &p_logs_dir); - void _open_log_file(const String &p_file_path); void _delete_old_log_files(const String &p_logs_dir); static GDMonoLog *singleton; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 3f0a5d6e50..42102ed835 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,39 +68,39 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { } break; case MONO_TYPE_VALUETYPE: { - GDMonoClass *tclass = p_type.type_class; + GDMonoClass *vtclass = p_type.type_class; - if (tclass == CACHED_CLASS(Vector2)) + if (vtclass == CACHED_CLASS(Vector2)) return Variant::VECTOR2; - if (tclass == CACHED_CLASS(Rect2)) + if (vtclass == CACHED_CLASS(Rect2)) return Variant::RECT2; - if (tclass == CACHED_CLASS(Transform2D)) + if (vtclass == CACHED_CLASS(Transform2D)) return Variant::TRANSFORM2D; - if (tclass == CACHED_CLASS(Vector3)) + if (vtclass == CACHED_CLASS(Vector3)) return Variant::VECTOR3; - if (tclass == CACHED_CLASS(Basis)) + if (vtclass == CACHED_CLASS(Basis)) return Variant::BASIS; - if (tclass == CACHED_CLASS(Quat)) + if (vtclass == CACHED_CLASS(Quat)) return Variant::QUAT; - if (tclass == CACHED_CLASS(Transform)) + if (vtclass == CACHED_CLASS(Transform)) return Variant::TRANSFORM; - if (tclass == CACHED_CLASS(AABB)) + if (vtclass == CACHED_CLASS(AABB)) return Variant::AABB; - if (tclass == CACHED_CLASS(Color)) + if (vtclass == CACHED_CLASS(Color)) return Variant::COLOR; - if (tclass == CACHED_CLASS(Plane)) + if (vtclass == CACHED_CLASS(Plane)) return Variant::PLANE; - if (mono_class_is_enum(tclass->get_mono_ptr())) + if (mono_class_is_enum(vtclass->get_mono_ptr())) return Variant::INT; } break; @@ -156,26 +156,52 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (CACHED_CLASS(Array) == type_class) { return Variant::ARRAY; } + + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); + + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { + return Variant::DICTIONARY; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return Variant::DICTIONARY; + } + + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { + return Variant::ARRAY; + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return Variant::ARRAY; + } } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { + return Variant::DICTIONARY; + } - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + return Variant::ARRAY; + } + + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) + return Variant::DICTIONARY; + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { return Variant::DICTIONARY; } - exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) + return Variant::ARRAY; - if (is_array) { + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return Variant::ARRAY; } } break; @@ -188,6 +214,63 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { return Variant::NIL; } +bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { + switch (p_array_type.type_encoding) { + case MONO_TYPE_GENERICINST: { + MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); + + if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { + MonoReflectionType *elem_reftype; + + GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); + + r_elem_type = ManagedType::from_reftype(elem_reftype); + return true; + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) { + r_elem_type = ManagedType::from_reftype(elem_reftype); + return true; + } + } break; + default: { + } break; + } + + return false; +} + +bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) { + switch (p_dictionary_type.type_encoding) { + case MONO_TYPE_GENERICINST: { + MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type()); + + if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + + GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype); + + r_key_type = ManagedType::from_reftype(key_reftype); + r_value_type = ManagedType::from_reftype(value_reftype); + return true; + } + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) { + r_key_type = ManagedType::from_reftype(key_reftype); + r_value_type = ManagedType::from_reftype(value_reftype); + return true; + } + } break; + default: { + } break; + } + + return false; +} + String mono_to_utf8_string(MonoString *p_mono_string) { MonoError error; char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); @@ -216,7 +299,7 @@ String mono_to_utf16_string(MonoString *p_mono_string) { ret.set(len, 0); CharType *src = (CharType *)mono_string_chars(p_mono_string); - CharType *dst = &(ret.operator[](0)); + CharType *dst = ret.ptrw(); for (int i = 0; i < len; i++) { dst[i] = src[i]; @@ -294,60 +377,60 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_VALUETYPE: { - GDMonoClass *tclass = p_type.type_class; + GDMonoClass *vtclass = p_type.type_class; - if (tclass == CACHED_CLASS(Vector2)) { + if (vtclass == CACHED_CLASS(Vector2)) { GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); } - if (tclass == CACHED_CLASS(Rect2)) { + if (vtclass == CACHED_CLASS(Rect2)) { GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); } - if (tclass == CACHED_CLASS(Transform2D)) { + if (vtclass == CACHED_CLASS(Transform2D)) { GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); } - if (tclass == CACHED_CLASS(Vector3)) { + if (vtclass == CACHED_CLASS(Vector3)) { GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); } - if (tclass == CACHED_CLASS(Basis)) { + if (vtclass == CACHED_CLASS(Basis)) { GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); } - if (tclass == CACHED_CLASS(Quat)) { + if (vtclass == CACHED_CLASS(Quat)) { GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); } - if (tclass == CACHED_CLASS(Transform)) { + if (vtclass == CACHED_CLASS(Transform)) { GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); } - if (tclass == CACHED_CLASS(AABB)) { + if (vtclass == CACHED_CLASS(AABB)) { GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); } - if (tclass == CACHED_CLASS(Color)) { + if (vtclass == CACHED_CLASS(Color)) { GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); } - if (tclass == CACHED_CLASS(Plane)) { + if (vtclass == CACHED_CLASS(Plane)) { GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane()); return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); } - if (mono_class_is_enum(tclass->get_mono_ptr())) { - MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); + if (mono_class_is_enum(vtclass->get_mono_ptr())) { + MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr()); MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype); switch (mono_type_get_type(enum_basetype)) { case MONO_TYPE_BOOLEAN: { @@ -453,6 +536,34 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (CACHED_CLASS(Array) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } + + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + if (GDMonoUtils::tools_godot_api_check()) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } else { + return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); + } + } } break; case MONO_TYPE_OBJECT: { // Variant @@ -545,25 +656,41 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); - MonoException *exc = NULL; - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); } - exc = NULL; - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_array) { + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } + + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + } + + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + if (GDMonoUtils::tools_godot_api_check()) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } else { + return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); + } + } } break; } break; } @@ -577,15 +704,9 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (!p_obj) return Variant(); - GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj)); - ERR_FAIL_COND_V(!tclass, Variant()); - - MonoType *raw_type = tclass->get_mono_type(); - - ManagedType type; + ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj)); - type.type_encoding = mono_type_get_type(raw_type); - type.type_class = tclass; + ERR_FAIL_COND_V(!type.type_class, Variant()); switch (type.type_encoding) { case MONO_TYPE_BOOLEAN: @@ -624,39 +745,39 @@ Variant mono_object_to_variant(MonoObject *p_obj) { } break; case MONO_TYPE_VALUETYPE: { - GDMonoClass *tclass = type.type_class; + GDMonoClass *vtclass = type.type_class; - if (tclass == CACHED_CLASS(Vector2)) + if (vtclass == CACHED_CLASS(Vector2)) return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Rect2)) + if (vtclass == CACHED_CLASS(Rect2)) return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Transform2D)) + if (vtclass == CACHED_CLASS(Transform2D)) return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Vector3)) + if (vtclass == CACHED_CLASS(Vector3)) return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Basis)) + if (vtclass == CACHED_CLASS(Basis)) return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Quat)) + if (vtclass == CACHED_CLASS(Quat)) return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Transform)) + if (vtclass == CACHED_CLASS(Transform)) return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(AABB)) + if (vtclass == CACHED_CLASS(AABB)) return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Color)) + if (vtclass == CACHED_CLASS(Color)) return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj)); - if (tclass == CACHED_CLASS(Plane)) + if (vtclass == CACHED_CLASS(Plane)) return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj)); - if (mono_class_is_enum(tclass->get_mono_ptr())) + if (mono_class_is_enum(vtclass->get_mono_ptr())) return unbox<int32_t>(p_obj); } break; @@ -698,7 +819,11 @@ Variant mono_object_to_variant(MonoObject *p_obj) { // GodotObject if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); - return ptr ? Variant(ptr) : Variant(); + if (ptr != NULL) { + Reference *ref = Object::cast_to<Reference>(ptr); + return ref ? Variant(Ref<Reference>(ref)) : Variant(ptr); + } + return Variant(); } if (CACHED_CLASS(NodePath) == type_class) { @@ -713,47 +838,73 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (CACHED_CLASS(Array) == type_class) { MonoException *exc = NULL; - Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc); + UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = NULL; - Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc); + UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } - } break; - case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - MonoException *exc = NULL; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { + return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + } - GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + } + + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + } + } break; + + case MONO_TYPE_GENERICINST: { + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return *unbox<Dictionary *>(ret); } - exc = NULL; - - GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_array) { + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return *unbox<Array *>(ret); } + + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { + return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); + } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + } + + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + } + + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + } } break; } @@ -876,7 +1027,7 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { for (int i = 0; i < p_array.size(); i++) { MonoString *boxed = mono_string_from_godot(r[i]); - mono_array_set(ret, MonoString *, i, boxed); + mono_array_setref(ret, i, boxed); } return ret; @@ -981,4 +1132,5 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { return ret; } + } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 4002f2a225..3fa958ac32 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #define GDMONOMARSHAL_H #include "core/variant.h" + #include "gd_mono.h" #include "gd_mono_utils.h" @@ -58,6 +59,9 @@ T unbox(MonoObject *p_obj) { Variant::Type managed_to_variant_type(const ManagedType &p_type); +bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); +bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type); + // String String mono_to_utf8_string(MonoString *p_mono_string); @@ -206,9 +210,10 @@ enum { // In the future we may force this if we want to ref return these structs #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY -// Sometimes clang-format can be an ass -GD_STATIC_ASSERT(MATCHES_Vector2 &&MATCHES_Rect2 &&MATCHES_Transform2D &&MATCHES_Vector3 && - MATCHES_Basis &&MATCHES_Quat &&MATCHES_Transform &&MATCHES_AABB &&MATCHES_Color &&MATCHES_Plane); +/* clang-format off */ +GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && + MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane); +/* clang-format on */ #endif } // namespace InteropLayout diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 630bda8b4e..968b316a3e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,26 +68,34 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { param_types.push_back(param_type); } + + // clear the cache + method_info_fetched = false; + method_info = MethodInfo(); +} + +GDMonoClass *GDMonoMethod::get_enclosing_class() const { + return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method)); } bool GDMonoMethod::is_static() { return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoMethod::get_visibility() { +IMonoClassMember::Visibility GDMonoMethod::get_visibility() { switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } @@ -101,7 +109,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, for (int i = 0; i < params_count; i++) { MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]); - mono_array_set(params, MonoObject *, i, boxed_param); + mono_array_setref(params, i, boxed_param); } MonoException *exc = NULL; @@ -246,11 +254,34 @@ void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { } } +const MethodInfo &GDMonoMethod::get_method_info() { + + if (!method_info_fetched) { + method_info.name = name; + method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), ""); + + Vector<StringName> names; + get_parameter_names(names); + + for (int i = 0; i < params_count; ++i) { + method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i])); + } + + // TODO: default arguments + + method_info_fetched = true; + } + + return method_info; +} + GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) { name = p_name; mono_method = p_method; + method_info_fetched = false; + attrs_fetched = false; attributes = NULL; diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index 444ec2a67d..2fc8628f27 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,10 +32,10 @@ #define GD_MONO_METHOD_H #include "gd_mono.h" -#include "gd_mono_class_member.h" #include "gd_mono_header.h" +#include "i_mono_class_member.h" -class GDMonoMethod : public GDMonoClassMember { +class GDMonoMethod : public IMonoClassMember { StringName name; @@ -43,6 +43,9 @@ class GDMonoMethod : public GDMonoClassMember { ManagedType return_type; Vector<ManagedType> param_types; + bool method_info_fetched; + MethodInfo method_info; + bool attrs_fetched; MonoCustomAttrInfo *attributes; @@ -54,17 +57,19 @@ class GDMonoMethod : public GDMonoClassMember { MonoMethod *mono_method; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_METHOD; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL; + + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; } - virtual StringName get_name() { return name; } + virtual StringName get_name() const GD_FINAL { return name; } - virtual bool is_static(); + virtual bool is_static() GD_FINAL; - virtual Visibility get_visibility(); + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); - virtual void fetch_attributes(); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; + void fetch_attributes(); _FORCE_INLINE_ int get_parameters_count() { return params_count; } _FORCE_INLINE_ ManagedType get_return_type() { return return_type; } @@ -83,6 +88,8 @@ public: void get_parameter_names(Vector<StringName> &names) const; void get_parameter_types(Vector<ManagedType> &types) const; + const MethodInfo &get_method_info(); + GDMonoMethod(StringName p_name, MonoMethod *p_method); ~GDMonoMethod(); }; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index ce66e0c8db..f1da00638f 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "gd_mono_property.h" #include "gd_mono_class.h" @@ -79,24 +80,24 @@ bool GDMonoProperty::is_static() { return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC; } -GDMonoClassMember::Visibility GDMonoProperty::get_visibility() { +IMonoClassMember::Visibility GDMonoProperty::get_visibility() { MonoMethod *prop_method = mono_property_get_get_method(mono_property); if (prop_method == NULL) prop_method = mono_property_get_set_method(mono_property); switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: - return GDMonoClassMember::PRIVATE; + return IMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: - return GDMonoClassMember::PROTECTED_AND_INTERNAL; + return IMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: - return GDMonoClassMember::INTERNAL; + return IMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: - return GDMonoClassMember::PROTECTED; + return IMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: - return GDMonoClassMember::PUBLIC; + return IMonoClassMember::PUBLIC; default: - ERR_FAIL_V(GDMonoClassMember::PRIVATE); + ERR_FAIL_V(IMonoClassMember::PRIVATE); } } @@ -141,7 +142,7 @@ bool GDMonoProperty::has_setter() { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); - mono_array_set(params, MonoObject *, 0, p_value); + mono_array_setref(params, 0, p_value); MonoException *exc = NULL; GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); if (exc) { diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index b3f1e2114a..d6efa60412 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,14 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef GD_MONO_PROPERTY_H #define GD_MONO_PROPERTY_H #include "gd_mono.h" -#include "gd_mono_class_member.h" #include "gd_mono_header.h" +#include "i_mono_class_member.h" -class GDMonoProperty : public GDMonoClassMember { +class GDMonoProperty : public IMonoClassMember { GDMonoClass *owner; MonoProperty *mono_property; @@ -46,15 +47,17 @@ class GDMonoProperty : public GDMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() { return MEMBER_TYPE_PROPERTY; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } + + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; } - virtual StringName get_name() { return name; } + virtual StringName get_name() const GD_FINAL { return name; } - virtual bool is_static(); - virtual Visibility get_visibility(); + virtual bool is_static() GD_FINAL; + virtual Visibility get_visibility() GD_FINAL; - virtual bool has_attribute(GDMonoClass *p_attr_class); - virtual MonoObject *get_attribute(GDMonoClass *p_attr_class); + virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL; + virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL; void fetch_attributes(); bool has_getter(); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 211987d242..5987fa8ebb 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,7 @@ #include "../csharp_script.h" #include "../utils/macros.h" +#include "../utils/mutex_utils.h" #include "gd_mono.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" @@ -49,6 +50,7 @@ MonoCache mono_cache; #define CACHE_AND_CHECK(m_var, m_val) \ { \ + CRASH_COND(m_var != NULL); \ m_var = m_val; \ if (!m_var) { \ ERR_EXPLAIN("Mono Cache: Member " #m_var " is null"); \ @@ -62,8 +64,11 @@ MonoCache mono_cache; #define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val) #define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val) #define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val) +#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val) -void MonoCache::clear_members() { +void MonoCache::clear_corlib_cache() { + + corlib_cache_updated = false; class_MonoObject = NULL; class_bool = NULL; @@ -80,6 +85,9 @@ void MonoCache::clear_members() { class_String = NULL; class_IntPtr = NULL; + class_System_Collections_IEnumerable = NULL; + class_System_Collections_IDictionary = NULL; + #ifdef DEBUG_ENABLED class_System_Diagnostics_StackTrace = NULL; methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL; @@ -88,6 +96,11 @@ void MonoCache::clear_members() { #endif class_KeyNotFoundException = NULL; +} + +void MonoCache::clear_godot_api_cache() { + + godot_api_cache_updated = false; rawclass_Dictionary = NULL; @@ -104,7 +117,7 @@ void MonoCache::clear_members() { class_NodePath = NULL; class_RID = NULL; class_GodotObject = NULL; - class_GodotReference = NULL; + class_GodotResource = NULL; class_Node = NULL; class_Control = NULL; class_Spatial = NULL; @@ -112,6 +125,7 @@ void MonoCache::clear_members() { class_Array = NULL; class_Dictionary = NULL; class_MarshalUtils = NULL; + class_ISerializationListener = NULL; #ifdef DEBUG_ENABLED class_DebuggingUtils = NULL; @@ -142,19 +156,33 @@ void MonoCache::clear_members() { methodthunk_GodotObject_Dispose = NULL; methodthunk_Array_GetPtr = NULL; methodthunk_Dictionary_GetPtr = NULL; - methodthunk_MarshalUtils_IsArrayGenericType = NULL; - methodthunk_MarshalUtils_IsDictionaryGenericType = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; - task_scheduler_handle = Ref<MonoGCHandle>(); -} + // Start of MarshalUtils methods -void MonoCache::cleanup() { + methodthunk_MarshalUtils_TypeIsGenericArray = NULL; + methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL; - corlib_cache_updated = false; - godot_api_cache_updated = false; + methodthunk_MarshalUtils_ArrayGetElementType = NULL; + methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL; + + methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL; + methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL; + methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info = NULL; + methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info = NULL; + + methodthunk_MarshalUtils_MakeGenericArrayType = NULL; + methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL; + + methodthunk_MarshalUtils_EnumerableToArray = NULL; + methodthunk_MarshalUtils_IDictionaryToDictionary = NULL; + methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL; + + // End of MarshalUtils methods + + task_scheduler_handle = Ref<MonoGCHandle>(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) @@ -177,6 +205,9 @@ void update_corlib_cache() { CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class())); CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); + CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); + CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); + #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames")); @@ -204,7 +235,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); - CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference)); + CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); @@ -212,6 +243,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); + CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener)); #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils)); @@ -241,85 +273,110 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0)); CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0)); CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); + // Start of MarshalUtils methods + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2)); + + // End of MarshalUtils methods + #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); #endif // TODO Move to CSharpLanguage::init() and do handle disposal - MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - GDMonoUtils::runtime_object_init(task_scheduler); + MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); + GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); mono_cache.godot_api_cache_updated = true; } -void clear_cache() { - mono_cache.cleanup(); - mono_cache.clear_members(); -} - MonoObject *unmanaged_get_managed(Object *unmanaged) { - if (unmanaged) { - if (unmanaged->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); - if (cs_instance) { - return cs_instance->get_mono_object(); - } + if (!unmanaged) + return NULL; + + if (unmanaged->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance()); + + if (cs_instance) { + return cs_instance->get_mono_object(); } + } - // If the owner does not have a CSharpInstance... + // If the owner does not have a CSharpInstance... - void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); - if (data) { - CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); + ERR_FAIL_NULL_V(data, NULL); - Ref<MonoGCHandle> &gchandle = script_binding.gchandle; - ERR_FAIL_COND_V(gchandle.is_null(), NULL); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); - MonoObject *target = gchandle->get_target(); + if (!script_binding.inited) { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex()); - if (target) - return target; + if (!script_binding.inited) { // Other thread may have set it up + // Already had a binding that needs to be setup + CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged); - CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + ERR_FAIL_COND_V(!script_binding.inited, NULL); + } + } - // Create a new one + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + ERR_FAIL_COND_V(gchandle.is_null(), NULL); -#ifdef DEBUG_ENABLED - CRASH_COND(script_binding.type_name == StringName()); - CRASH_COND(script_binding.wrapper_class == NULL); -#endif + MonoObject *target = gchandle->get_target(); - MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); - ERR_FAIL_NULL_V(mono_object, NULL); + if (target) + return target; - gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); - // Tie managed to unmanaged - Reference *ref = Object::cast_to<Reference>(unmanaged); + // Create a new one - if (ref) { - // Unsafe refcount increment. The managed instance also counts as a reference. - // This way if the unmanaged world has no references to our owner - // but the managed instance is alive, the refcount will be 1 instead of 0. - // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) +#ifdef DEBUG_ENABLED + CRASH_COND(script_binding.type_name == StringName()); + CRASH_COND(script_binding.wrapper_class == NULL); +#endif - ref->reference(); - } + MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); + ERR_FAIL_NULL_V(mono_object, NULL); - return mono_object; - } + gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + + // Tie managed to unmanaged + Reference *ref = Object::cast_to<Reference>(unmanaged); + + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + ref->reference(); } - return NULL; + return mono_object; } void set_main_thread(MonoThread *p_thread) { @@ -328,7 +385,7 @@ void set_main_thread(MonoThread *p_thread) { void attach_current_thread() { ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + MonoThread *mono_thread = mono_thread_attach(mono_domain_get()); ERR_FAIL_NULL(mono_thread); } @@ -343,11 +400,10 @@ MonoThread *get_current_thread() { return mono_thread_current(); } -void runtime_object_init(MonoObject *p_this_obj) { - GD_MONO_BEGIN_RUNTIME_INVOKE; - // FIXME: Do not use mono_runtime_object_init, it aborts if an exception is thrown - mono_runtime_object_init(p_this_obj); - GD_MONO_END_RUNTIME_INVOKE; +void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) { + GDMonoMethod *ctor = p_class->get_method(".ctor", 0); + ERR_FAIL_NULL(ctor); + ctor->invoke_raw(p_this_obj, NULL, r_exc); } GDMonoClass *get_object_class(MonoObject *p_object) { @@ -362,6 +418,11 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) { GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); + if (klass && klass->is_static()) { + // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object. + return mono_cache.class_GodotObject; + } + #ifdef TOOLS_ENABLED if (!klass) { return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); @@ -388,33 +449,28 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) { } MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) { - String object_type = p_object->get_class_name(); - - if (object_type[0] == '_') - object_type = object_type.substr(1, object_type.length()); - - if (!ClassDB::is_parent_class(object_type, p_native)) { + if (!ClassDB::is_parent_class(p_object->get_class_name(), p_native)) { ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'"); ERR_FAIL_V(NULL); } - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); // Construct - GDMonoUtils::runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object, p_class); return mono_object; } MonoObject *create_managed_from(const NodePath &p_from) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath)); + MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath)); ERR_FAIL_NULL_V(mono_object, NULL); // Construct - GDMonoUtils::runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath)); CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); @@ -422,11 +478,11 @@ MonoObject *create_managed_from(const NodePath &p_from) { } MonoObject *create_managed_from(const RID &p_from) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID)); + MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID)); ERR_FAIL_NULL_V(mono_object, NULL); // Construct - GDMonoUtils::runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID)); CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); @@ -434,7 +490,7 @@ MonoObject *create_managed_from(const RID &p_from) { } MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); // Search constructor that takes a pointer as parameter @@ -458,13 +514,13 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { MonoException *exc = NULL; GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return mono_object; } MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); // Search constructor that takes a pointer as parameter @@ -488,7 +544,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) MonoException *exc = NULL; GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return mono_object; } @@ -565,7 +621,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { if (unexpected_exc) { GDMonoInternals::unhandled_exception(unexpected_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } Vector<ScriptLanguage::StackInfo> _si; @@ -598,13 +654,8 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { } void debug_unhandled_exception(MonoException *p_exc) { -#ifdef DEBUG_ENABLED - GDMonoUtils::debug_send_unhandled_exception_error(p_exc); - if (ScriptDebugger::get_singleton()) - ScriptDebugger::get_singleton()->idle_poll(); -#endif GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well - _UNREACHABLE_(); + GD_UNREACHABLE(); } void print_unhandled_exception(MonoException *p_exc) { @@ -612,19 +663,19 @@ void print_unhandled_exception(MonoException *p_exc) { } void set_pending_exception(MonoException *p_exc) { -#ifdef HAS_PENDING_EXCEPTIONS +#ifdef NO_PENDING_EXCEPTIONS + debug_unhandled_exception(p_exc); + GD_UNREACHABLE(); +#else if (get_runtime_invoke_count() == 0) { debug_unhandled_exception(p_exc); - _UNREACHABLE_(); + GD_UNREACHABLE(); } if (!mono_runtime_set_pending_exception(p_exc, false)) { ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:"); GDMonoUtils::debug_print_unhandled_exception(p_exc); } -#else - debug_unhandled_exception(p_exc); - _UNREACHABLE_(); #endif } @@ -695,7 +746,142 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool & } void dispose(MonoObject *p_mono_object, MonoException **r_exc) { - invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, (MonoObject **)r_exc); + invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc); +} + +namespace Marshal { + +#ifdef MONO_GLUE_ENABLED +#ifdef TOOLS_ENABLED +#define NO_GLUE_RET(m_ret) \ + { \ + if (!mono_cache.godot_api_cache_updated) return m_ret; \ + } +#else +#define NO_GLUE_RET(m_ret) \ + {} +#endif +#else +#define NO_GLUE_RET(m_ret) \ + { return m_ret; } +#endif + +bool type_is_generic_array(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); + TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); + TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { + ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc); + UNHANDLED_EXCEPTION(exc); +} + +void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { + DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc); + UNHANDLED_EXCEPTION(exc); +} + +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); + GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); + GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { + NO_GLUE_RET(false); + GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { + NO_GLUE_RET(false); + GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return (bool)res; +} + +Array enumerable_to_array(MonoObject *p_enumerable) { + NO_GLUE_RET(Array()); + Array result; + EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_enumerable, &result, &exc); + UNHANDLED_EXCEPTION(exc); + return result; } +Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { + NO_GLUE_RET(Dictionary()); + Dictionary result; + IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_idictionary, &result, &exc); + UNHANDLED_EXCEPTION(exc); + return result; +} + +Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { + NO_GLUE_RET(Dictionary()); + Dictionary result; + GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc); + UNHANDLED_EXCEPTION(exc); + return result; +} + +GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { + NO_GLUE_RET(NULL); + MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType); + MonoException *exc = NULL; + MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); +} + +GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { + NO_GLUE_RET(NULL); + MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType); + MonoException *exc = NULL; + MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc); + UNHANDLED_EXCEPTION(exc); + return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); +} + +} // namespace Marshal + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 170df32991..f535fbb6d0 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,26 +41,64 @@ #include "core/object.h" #include "core/reference.h" -#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ +#define UNHANDLED_EXCEPTION(m_exc) \ if (unlikely(m_exc != NULL)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ - _UNREACHABLE_(); \ + GD_UNREACHABLE(); \ } namespace GDMonoUtils { -typedef void (*GodotObject_Dispose)(MonoObject *, MonoObject **); -typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); -typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); -typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); -typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); -typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); -typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); +typedef void (*GodotObject_Dispose)(MonoObject *, MonoException **); +typedef Array *(*Array_GetPtr)(MonoObject *, MonoException **); +typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoException **); +typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoException **); +typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoException **); +typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoException **); +typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoException **); +typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoException **); + +typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **); +typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **); + +typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **); +typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); + +typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoException **); +typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoException **); +typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoException **); +typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); + +typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **); +typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **); + +typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **); +typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); +typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); + +namespace Marshal { + +bool type_is_generic_array(MonoReflectionType *p_reftype); +bool type_is_generic_dictionary(MonoReflectionType *p_reftype); + +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); +void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); + +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype); +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype); +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); + +GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); +GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); + +Array enumerable_to_array(MonoObject *p_enumerable); +Dictionary idictionary_to_dictionary(MonoObject *p_idictionary); +Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary); + +} // namespace Marshal + +// End of MarshalUtils methods struct MonoCache { @@ -83,6 +121,9 @@ struct MonoCache { GDMonoClass *class_String; GDMonoClass *class_IntPtr; + GDMonoClass *class_System_Collections_IEnumerable; + GDMonoClass *class_System_Collections_IDictionary; + #ifdef DEBUG_ENABLED GDMonoClass *class_System_Diagnostics_StackTrace; StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames; @@ -108,7 +149,7 @@ struct MonoCache { GDMonoClass *class_NodePath; GDMonoClass *class_RID; GDMonoClass *class_GodotObject; - GDMonoClass *class_GodotReference; + GDMonoClass *class_GodotResource; GDMonoClass *class_Node; GDMonoClass *class_Control; GDMonoClass *class_Spatial; @@ -116,6 +157,7 @@ struct MonoCache { GDMonoClass *class_Array; GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; + GDMonoClass *class_ISerializationListener; #ifdef DEBUG_ENABLED GDMonoClass *class_DebuggingUtils; @@ -146,25 +188,43 @@ struct MonoCache { GodotObject_Dispose methodthunk_GodotObject_Dispose; Array_GetPtr methodthunk_Array_GetPtr; Dictionary_GetPtr methodthunk_Dictionary_GetPtr; - IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; - IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; + // Start of MarshalUtils methods + + TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray; + TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary; + + ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType; + DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; + + GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; + GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; + GenericIEnumerableIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info; + GenericIDictionaryIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info; + + MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType; + MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType; + + EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray; + IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary; + GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary; + + // End of MarshalUtils methods + Ref<MonoGCHandle> task_scheduler_handle; bool corlib_cache_updated; bool godot_api_cache_updated; - void clear_members(); - void cleanup(); + void clear_corlib_cache(); + void clear_godot_api_cache(); MonoCache() { - corlib_cache_updated = false; - godot_api_cache_updated = false; - - clear_members(); + clear_corlib_cache(); + clear_godot_api_cache(); } }; @@ -172,7 +232,22 @@ extern MonoCache mono_cache; void update_corlib_cache(); void update_godot_api_cache(); -void clear_cache(); + +inline void clear_corlib_cache() { + mono_cache.clear_corlib_cache(); +} + +inline void clear_godot_api_cache() { + mono_cache.clear_godot_api_cache(); +} + +_FORCE_INLINE_ bool tools_godot_api_check() { +#ifdef TOOLS_ENABLED + return mono_cache.godot_api_cache_updated; +#else + return true; // Assume it's updated if this was called, otherwise it's a bug +#endif +} _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); @@ -194,7 +269,7 @@ _FORCE_INLINE_ bool is_main_thread() { return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current(); } -void runtime_object_init(MonoObject *p_this_obj); +void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL); GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); @@ -214,7 +289,7 @@ void set_exception_message(MonoException *p_exc, String message); void debug_print_unhandled_exception(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc); -_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc); +GD_NORETURN void debug_unhandled_exception(MonoException *p_exc); void print_unhandled_exception(MonoException *p_exc); /** @@ -255,6 +330,7 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc); #define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field) #define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method) #define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method) +#define CACHED_PROPERTY(m_class, m_property) (GDMonoUtils::mono_cache.property_##m_class##_##m_property) #ifdef REAL_T_IS_DOUBLE #define REAL_T_MONOCLASS CACHED_CLASS_RAW(double) diff --git a/modules/mono/mono_gd/gd_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h index 008ea0e416..f4de4e3230 100644 --- a/modules/mono/mono_gd/gd_mono_class_member.h +++ b/modules/mono/mono_gd/i_mono_class_member.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* gd_mono_class_member.h */ +/* i_mono_class_member.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,14 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GD_MONO_CLASS_MEMBER_H -#define GD_MONO_CLASS_MEMBER_H + +#ifndef I_MONO_CLASS_MEMBER_H +#define I_MONO_CLASS_MEMBER_H #include "gd_mono_header.h" #include <mono/metadata/object.h> -class GDMonoClassMember { +class IMonoClassMember { public: enum Visibility { PRIVATE, @@ -50,11 +51,13 @@ public: MEMBER_TYPE_METHOD }; - virtual ~GDMonoClassMember() {} + virtual ~IMonoClassMember() {} + + virtual GDMonoClass *get_enclosing_class() const = 0; - virtual MemberType get_member_type() = 0; + virtual MemberType get_member_type() const = 0; - virtual StringName get_name() = 0; + virtual StringName get_name() const = 0; virtual bool is_static() = 0; @@ -64,4 +67,4 @@ public: virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0; }; -#endif // GD_MONO_CLASS_MEMBER_H +#endif // I_MONO_CLASS_MEMBER_H diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp new file mode 100644 index 0000000000..9f736b71cd --- /dev/null +++ b/modules/mono/mono_gd/managed_type.cpp @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* managed_type.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "managed_type.h" + +#include "gd_mono.h" +#include "gd_mono_class.h" + +ManagedType ManagedType::from_class(GDMonoClass *p_class) { + return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class); +} + +ManagedType ManagedType::from_class(MonoClass *p_mono_class) { + GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class); + ERR_FAIL_COND_V(!tclass, ManagedType()); + + return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass); +} + +ManagedType ManagedType::from_type(MonoType *p_mono_type) { + MonoClass *mono_class = mono_class_from_mono_type(p_mono_type); + GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class); + ERR_FAIL_COND_V(!tclass, ManagedType()); + + return ManagedType(mono_type_get_type(p_mono_type), tclass); +} + +ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) { + MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype); + return from_type(mono_type); +} diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h new file mode 100644 index 0000000000..a537e56aea --- /dev/null +++ b/modules/mono/mono_gd/managed_type.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* managed_type.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MANAGED_TYPE_H +#define MANAGED_TYPE_H + +#include <mono/metadata/object.h> + +#include "gd_mono_header.h" + +struct ManagedType { + int type_encoding; + GDMonoClass *type_class; + + static ManagedType from_class(GDMonoClass *p_class); + static ManagedType from_class(MonoClass *p_mono_class); + static ManagedType from_type(MonoType *p_mono_type); + static ManagedType from_reftype(MonoReflectionType *p_mono_reftype); + + ManagedType() : + type_encoding(0), + type_class(NULL) { + } + + ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : + type_encoding(p_type_encoding), + type_class(p_type_class) { + } +}; + +#endif // MANAGED_TYPE_H diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp index f6cb143e8e..3607b6f8b3 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,8 +35,8 @@ #include "csharp_script.h" CSharpLanguage *script_language_cs = NULL; -ResourceFormatLoaderCSharpScript *resource_loader_cs = NULL; -ResourceFormatSaverCSharpScript *resource_saver_cs = NULL; +Ref<ResourceFormatLoaderCSharpScript> resource_loader_cs; +Ref<ResourceFormatSaverCSharpScript> resource_saver_cs; _GodotSharp *_godotsharp = NULL; @@ -52,9 +52,10 @@ void register_mono_types() { script_language_cs->set_language_index(ScriptServer::get_language_count()); ScriptServer::register_language(script_language_cs); - resource_loader_cs = memnew(ResourceFormatLoaderCSharpScript); + resource_loader_cs.instance(); ResourceLoader::add_resource_format_loader(resource_loader_cs); - resource_saver_cs = memnew(ResourceFormatSaverCSharpScript); + + resource_saver_cs.instance(); ResourceSaver::add_resource_format_saver(resource_saver_cs); } @@ -63,10 +64,12 @@ void unregister_mono_types() { if (script_language_cs) memdelete(script_language_cs); - if (resource_loader_cs) - memdelete(resource_loader_cs); - if (resource_saver_cs) - memdelete(resource_saver_cs); + + ResourceLoader::remove_resource_format_loader(resource_loader_cs); + resource_loader_cs.unref(); + + ResourceSaver::remove_resource_format_saver(resource_saver_cs); + resource_saver_cs.unref(); if (_godotsharp) memdelete(_godotsharp); diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h index ab8a7d6463..0601e9a382 100644 --- a/modules/mono/register_types.h +++ b/modules/mono/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index c6748309f3..54d73c971f 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -91,16 +91,16 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc set_completed(true); int signal_argc = p_argcount - 1; - MonoArray *signal_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), signal_argc); + MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc); for (int i = 0; i < signal_argc; i++) { MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]); - mono_array_set(signal_args, MonoObject *, i, boxed); + mono_array_setref(signal_args, i, boxed); } MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { @@ -132,7 +132,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (awaiter) { MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, &exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 4ec860537b..4fb3cdb56d 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,7 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p class SignalAwaiterHandle : public MonoGCHandle { - GDCLASS(SignalAwaiterHandle, MonoGCHandle) + GDCLASS(SignalAwaiterHandle, MonoGCHandle); bool completed; diff --git a/modules/mono/utils/android_utils.cpp b/modules/mono/utils/android_utils.cpp new file mode 100644 index 0000000000..7dd67e3b8e --- /dev/null +++ b/modules/mono/utils/android_utils.cpp @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* android_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "android_utils.h" + +#ifdef __ANDROID__ + +#include "platform/android/thread_jandroid.h" + +namespace GDMonoUtils { +namespace Android { + +String get_app_native_lib_dir() { + JNIEnv *env = ThreadAndroid::get_env(); + + jclass activityThreadClass = env->FindClass("android/app/ActivityThread"); + jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;"); + jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread); + jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;"); + jobject ctx = env->CallObjectMethod(activityThread, getApplication); + + jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;"); + jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo); + jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;"); + jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField); + + String result; + + const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL); + if (nativeLibraryDir_utf8) { + result.parse_utf8(nativeLibraryDir_utf8); + env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8); + } + + return result; +} + +} // namespace Android +} // namespace GDMonoUtils + +#endif // __ANDROID__ diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/utils/android_utils.h index 29de4a4735..f911c3fdfe 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/utils/android_utils.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* monodevelop_instance.h */ +/* android_utils.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,29 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MONODEVELOP_INSTANCE_H -#define MONODEVELOP_INSTANCE_H +#ifndef ANDROID_UTILS_H +#define ANDROID_UTILS_H -#include "core/reference.h" +#ifdef __ANDROID__ -#include "../mono_gc_handle.h" -#include "../mono_gd/gd_mono_method.h" +#include "core/ustring.h" -class MonoDevelopInstance { +namespace GDMonoUtils { +namespace Android { - Ref<MonoGCHandle> gc_handle; - GDMonoMethod *execute_method; +String get_app_native_lib_dir(); -public: - enum EditorId { - MONODEVELOP = 0, - VISUALSTUDIO_FOR_MAC = 1 - }; +} // namespace Android +} // namespace GDMonoUtils - void execute(const Vector<String> &p_files); - void execute(const String &p_file); +#endif // __ANDROID__ - MonoDevelopInstance(const String &p_solution, EditorId p_editor_id); -}; - -#endif // MONODEVELOP_INSTANCE_H +#endif // ANDROID_UTILS_H diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index c801fb2f33..e44f254e1c 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* util_macros.h */ +/* macros.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,39 +31,53 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H -#define _GD_VARNAME_CONCAT_B(m_ignore, m_name) m_name -#define _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B(hello there, m_a##m_b##m_c) -#define _GD_VARNAME_CONCAT(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) -#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT(m_name, _, __COUNTER__) +#define _GD_VARNAME_CONCAT_B_(m_ignore, m_name) m_name +#define _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B_(hello there, m_a##m_b##m_c) +#define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) +#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__) -// noreturn +// static assert +// TODO: Get rid of this macro once we upgrade to C++11 -#if __cpp_static_assert +#ifdef __cpp_static_assert #define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") #else #define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)] #endif -#undef _NO_RETURN_ +// final +// TODO: Get rid of this macro once we upgrade to C++11 + +#if (__cplusplus >= 201103L) +#define GD_FINAL final +#else +#define GD_FINAL +#endif + +// noreturn +// TODO: Get rid of this macro once we upgrade to C++11 -#ifdef __GNUC__ -#define _NO_RETURN_ __attribute__((noreturn)) -#elif _MSC_VER -#define _NO_RETURN_ __declspec(noreturn) +#if (__cplusplus >= 201103L) +#define GD_NORETURN [[noreturn]] +#elif defined(__GNUC__) +#define GD_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define GD_NORETURN __declspec(noreturn) #else -#error Platform or compiler not supported +#define GD_NORETURN +#pragma message "Macro GD_NORETURN will have no effect" #endif // unreachable #if defined(_MSC_VER) -#define _UNREACHABLE_() __assume(0) +#define GD_UNREACHABLE() __assume(0) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 -#define _UNREACHABLE_() __builtin_unreachable() +#define GD_UNREACHABLE() __builtin_unreachable() #else -#define _UNREACHABLE_() \ - CRASH_NOW(); \ - do { \ +#define GD_UNREACHABLE() \ + CRASH_NOW(); \ + do { \ } while (true); #endif diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 6bb6efa92a..98aeadc8c8 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,6 +29,7 @@ /*************************************************************************/ #include "mono_reg_utils.h" +#include "core/os/dir_access.h" #ifdef WINDOWS_ENABLED @@ -158,8 +159,6 @@ MonoRegInfo find_mono() { if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS) return info; - ERR_PRINT("Cannot find mono in the registry"); - return MonoRegInfo(); } @@ -202,6 +201,13 @@ String find_msbuild_tools_path() { val += "\\"; } + // Since VS2019, the directory is simply named "Current" + String msbuild_dir = val + "MSBuild\\Current\\Bin"; + if (DirAccess::exists(msbuild_dir)) { + return msbuild_dir; + } + + // Directory name "15.0" is used in VS 2017 return val + "MSBuild\\15.0\\Bin"; } } diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h index 26f7e2d3c2..25446a4992 100644 --- a/modules/mono/utils/mono_reg_utils.h +++ b/modules/mono/utils/mono_reg_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/mutex_utils.h b/modules/mono/utils/mutex_utils.h index 07d659b6eb..b8be033cba 100644 --- a/modules/mono/utils/mutex_utils.h +++ b/modules/mono/utils/mutex_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp index f520706a0c..f1362be249 100644 --- a/modules/mono/utils/osx_utils.cpp +++ b/modules/mono/utils/osx_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,10 @@ #include "osx_utils.h" -#include "core/print_string.h" - #ifdef OSX_ENABLED +#include "core/print_string.h" + #include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h> diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/osx_utils.h index 68479b4f44..cc72233058 100644 --- a/modules/mono/utils/osx_utils.h +++ b/modules/mono/utils/osx_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #include "core/ustring.h" #ifndef OSX_UTILS_H +#define OSX_UTILS_H #ifdef OSX_ENABLED diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index e663ee3c4a..20863b1afe 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,16 +36,21 @@ #include "core/project_settings.h" #ifdef WINDOWS_ENABLED +#include <windows.h> + #define ENV_PATH_SEP ";" #else -#define ENV_PATH_SEP ":" #include <limits.h> +#include <unistd.h> + +#define ENV_PATH_SEP ":" #endif #include <stdlib.h> -String path_which(const String &p_name) { +namespace path { +String find_executable(const String &p_name) { #ifdef WINDOWS_ENABLED Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false); #endif @@ -55,11 +60,11 @@ String path_which(const String &p_name) { return String(); for (int i = 0; i < env_path.size(); i++) { - String p = path_join(env_path[i], p_name); + String p = path::join(env_path[i], p_name); #ifdef WINDOWS_ENABLED for (int j = 0; j < exts.size(); j++) { - String p2 = p + exts[j]; + String p2 = p + exts[j].to_lower(); // lowercase to reduce risk of case mismatch warning if (FileAccess::exists(p2)) return p2; @@ -73,42 +78,96 @@ String path_which(const String &p_name) { return String(); } -void fix_path(const String &p_path, String &r_out) { - r_out = p_path.replace("\\", "/"); +String cwd() { +#ifdef WINDOWS_ENABLED + const DWORD expected_size = ::GetCurrentDirectoryW(0, NULL); + + String buffer; + buffer.resize((int)expected_size); + if (::GetCurrentDirectoryW(expected_size, buffer.ptrw()) == 0) + return "."; + + return buffer.simplify_path(); +#else + char buffer[PATH_MAX]; + if (::getcwd(buffer, sizeof(buffer)) == NULL) + return "."; + + String result; + if (result.parse_utf8(buffer)) + return "."; - while (true) { // in case of using 2 or more slash - String compare = r_out.replace("//", "/"); - if (r_out == compare) - break; - else - r_out = compare; + return result.simplify_path(); +#endif +} + +String abspath(const String &p_path) { + if (p_path.is_abs_path()) { + return p_path.simplify_path(); + } else { + return path::join(path::cwd(), p_path).simplify_path(); } } -bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) { +String realpath(const String &p_path) { #ifdef WINDOWS_ENABLED - CharType ret[_MAX_PATH]; - if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) { - String abspath = String(ret).replace("\\", "/"); - int pos = abspath.find(":/"); - if (pos != -1) { - r_abs_path = abspath.substr(pos - 1, abspath.length()); - } else { - r_abs_path = abspath; - } - return true; - } -#else - char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL); - if (resolved_path) { - String retstr; - bool success = !retstr.parse_utf8(resolved_path); - ::free(resolved_path); - if (success) { - r_abs_path = retstr; - return true; - } + // Open file without read/write access + HANDLE hFile = ::CreateFileW(p_path.c_str(), 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return p_path; + + const DWORD expected_size = ::GetFinalPathNameByHandleW(hFile, NULL, 0, FILE_NAME_NORMALIZED); + + if (expected_size == 0) { + ::CloseHandle(hFile); + return p_path; } + + String buffer; + buffer.resize((int)expected_size); + ::GetFinalPathNameByHandleW(hFile, buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED); + + ::CloseHandle(hFile); + return buffer.simplify_path(); +#elif UNIX_ENABLED + char *resolved_path = ::realpath(p_path.utf8().get_data(), NULL); + + if (!resolved_path) + return p_path; + + String result; + bool parse_ok = result.parse_utf8(resolved_path); + ::free(resolved_path); + + if (parse_ok) + return p_path; + + return result.simplify_path(); #endif - return false; } + +String join(const String &p_a, const String &p_b) { + if (p_a.empty()) + return p_b; + + const CharType a_last = p_a[p_a.length() - 1]; + if ((a_last == '/' || a_last == '\\') || + (p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) { + return p_a + p_b; + } + + return p_a + "/" + p_b; +} + +String join(const String &p_a, const String &p_b, const String &p_c) { + return path::join(path::join(p_a, p_b), p_c); +} + +String join(const String &p_a, const String &p_b, const String &p_c, const String &p_d) { + return path::join(path::join(path::join(p_a, p_b), p_c), p_d); +} + +} // namespace path diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h index 3c7b36c0d4..ca25bc09f7 100644 --- a/modules/mono/utils/path_utils.h +++ b/modules/mono/utils/path_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,24 +31,32 @@ #ifndef PATH_UTILS_H #define PATH_UTILS_H +#include "core/string_builder.h" #include "core/ustring.h" -_FORCE_INLINE_ String path_join(const String &e1, const String &e2) { - return e1.plus_file(e2); -} +namespace path { -_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3) { - return e1.plus_file(e2).plus_file(e3); -} +String join(const String &p_a, const String &p_b); +String join(const String &p_a, const String &p_b, const String &p_c); +String join(const String &p_a, const String &p_b, const String &p_c, const String &p_d); -_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3, const String &e4) { - return e1.plus_file(e2).plus_file(e3).plus_file(e4); -} +String find_executable(const String &p_name); -String path_which(const String &p_name); +/// Returns a normalized absolute path to the current working directory +String cwd(); -void fix_path(const String &p_path, String &r_out); +/** + * Obtains a normalized absolute path to p_path. Symbolic links are + * not resolved. The path p_path might not exist in the file system. + */ +String abspath(const String &p_path); -bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path); +/** + * Obtains a normalized path to p_path with symbolic links resolved. + * The resulting path might be either a relative or an absolute path. + */ +String realpath(const String &p_path); + +} // namespace path #endif // PATH_UTILS_H diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 6900866725..2b014c2a45 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,9 @@ #include "core/os/file_access.h" +#include <stdio.h> +#include <stdlib.h> + namespace { int sfind(const String &p_text, int p_from) { @@ -41,7 +44,7 @@ int sfind(const String &p_text, int p_from) { int src_len = 2; int len = p_text.length(); - if (src_len == 0 || len == 0) + if (len == 0) return -1; const CharType *src = p_text.c_str(); @@ -63,7 +66,7 @@ int sfind(const String &p_text, int p_from) { break; case 1: { CharType c = src[read_pos]; - found = src[read_pos] == 's' || (c >= '0' || c <= '4'); + found = src[read_pos] == 's' || (c >= '0' && c <= '4'); break; } default: @@ -184,3 +187,50 @@ Error read_all_file_utf8(const String &p_path, String &r_content) { r_content = source; return OK; } + +// TODO: Move to variadic templates once we upgrade to C++11 + +String str_format(const char *p_format, ...) { + va_list list; + + va_start(list, p_format); + String res = str_format(p_format, list); + va_end(list); + + return res; +} +// va_copy was defined in the C99, but not in C++ standards before C++11. +// When you compile C++ without --std=c++<XX> option, compilers still define +// va_copy, otherwise you have to use the internal version (__va_copy). +#if !defined(va_copy) +#if defined(__GNUC__) +#define va_copy(d, s) __va_copy((d), (s)) +#else +#define va_copy(d, s) ((d) = (s)) +#endif +#endif + +#if defined(MINGW_ENABLED) || defined(_MSC_VER) && _MSC_VER < 1900 +#define vsnprintf(m_buffer, m_count, m_format, m_argptr) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_argptr) +#endif + +String str_format(const char *p_format, va_list p_list) { + va_list list; + + va_copy(list, p_list); + int len = vsnprintf(NULL, 0, p_format, list); + va_end(list); + + len += 1; // for the trailing '/0' + + char *buffer(memnew_arr(char, len)); + + va_copy(list, p_list); + vsnprintf(buffer, len, p_format, list); + va_end(list); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index ee803bd720..565b9bb644 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,8 @@ #include "core/ustring.h" #include "core/variant.h" +#include <stdarg.h> + String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); #ifdef TOOLS_ENABLED @@ -44,4 +46,15 @@ String escape_csharp_keyword(const String &p_name); Error read_all_file_utf8(const String &p_path, String &r_content); +#if defined(__GNUC__) +#define _PRINTF_FORMAT_ATTRIBUTE_1_0 __attribute__((format(printf, 1, 0))) +#define _PRINTF_FORMAT_ATTRIBUTE_1_2 __attribute__((format(printf, 1, 2))) +#else +#define _PRINTF_FORMAT_ATTRIBUTE_1_0 +#define _PRINTF_FORMAT_ATTRIBUTE_1_2 +#endif + +String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2; +String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0; + #endif // STRING_FORMAT_H diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp index a0e28fca5f..13179bf408 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/utils/thread_local.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index d7d98c47e2..e52b6e73ef 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ #error Platform or compiler not supported #endif -#ifdef __GNUC__ +#if defined(__GNUC__) #ifdef HAVE_GCC___THREAD #define _THREAD_LOCAL_(m_t) __thread m_t @@ -47,7 +47,7 @@ #define USE_CUSTOM_THREAD_LOCAL #endif -#elif _MSC_VER +#elif defined(_MSC_VER) #ifdef HAVE_DECLSPEC_THREAD #define _THREAD_LOCAL_(m_t) __declspec(thread) m_t @@ -76,7 +76,7 @@ struct ThreadLocalStorage { void *get_value() const; void set_value(void *p_value) const; - void alloc(void(_CALLBACK_FUNC_ *p_dest_callback)(void *)); + void alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)); void free(); private: diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 765a9fc11a..6a72a519fe 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -14,7 +14,7 @@ if env['builtin_libogg']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_ogg.Append(CPPPATH=[thirdparty_dir]) + env_ogg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_ogg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp index dba5dcc6e2..bea88bd654 100644 --- a/modules/ogg/register_types.cpp +++ b/modules/ogg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/ogg/register_types.h b/modules/ogg/register_types.h index e82c90de0b..84e309455e 100644 --- a/modules/ogg/register_types.h +++ b/modules/ogg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub index 4235f6a0b9..311d33b047 100644 --- a/modules/opensimplex/SCsub +++ b/modules/opensimplex/SCsub @@ -12,7 +12,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_opensimplex.Append(CPPPATH=[thirdparty_dir]) +env_opensimplex.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_opensimplex.Clone() env_thirdparty.disable_warnings() diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index ba54160a90..4b59a380f5 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NoiseTexture" inherits="Texture" category="Core" version="3.1"> +<class name="NoiseTexture" inherits="Texture" category="Core" version="3.2"> <brief_description> [OpenSimplexNoise] filled texture. </brief_description> @@ -9,24 +9,24 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap"> - If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map. + <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap" default="false"> + If [code]true[/code], the resulting texture contains a normal map created from the original noise interpreted as a bump map. </member> - <member name="height" type="int" setter="set_height" getter="get_height"> + <member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength" default="8.0"> + </member> + <member name="height" type="int" setter="set_height" getter="get_height" default="512"> Height of the generated texture. </member> <member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise"> The [OpenSimplexNoise] instance used to generate the noise. </member> - <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless"> + <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false"> Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate. </member> - <member name="width" type="int" setter="set_width" getter="get_width"> + <member name="width" type="int" setter="set_width" getter="get_width" default="512"> Width of the generated texture. </member> </members> diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index 31f13f341c..f3fbbab3da 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenSimplexNoise" inherits="Resource" category="Core" version="3.1"> +<class name="OpenSimplexNoise" inherits="Resource" category="Core" version="3.2"> <brief_description> Noise generator based on Open Simplex. </brief_description> @@ -23,8 +23,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_image"> <return type="Image"> @@ -37,6 +35,16 @@ Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. </description> </method> + <method name="get_noise_1d"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <description> + Returns the 1D noise value [code][-1,1][/code] at the given x-coordinate. + [b]Note:[/b] This method actually returns the 2D noise value [code][-1,1][/code] with fixed y-coordinate value 0.0. + </description> + </method> <method name="get_noise_2d"> <return type="float"> </return> @@ -100,24 +108,24 @@ <argument index="0" name="size" type="int"> </argument> <description> - Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] x [code]size[/code]). + Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]). </description> </method> </methods> <members> - <member name="lacunarity" type="float" setter="set_lacunarity" getter="get_lacunarity"> + <member name="lacunarity" type="float" setter="set_lacunarity" getter="get_lacunarity" default="2.0"> Difference in period between [member octaves]. </member> - <member name="octaves" type="int" setter="set_octaves" getter="get_octaves"> + <member name="octaves" type="int" setter="set_octaves" getter="get_octaves" default="3"> Number of OpenSimplex noise layers that are sampled to get the fractal noise. </member> - <member name="period" type="float" setter="set_period" getter="get_period"> + <member name="period" type="float" setter="set_period" getter="get_period" default="64.0"> Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance). </member> - <member name="persistence" type="float" setter="set_persistence" getter="get_persistence"> + <member name="persistence" type="float" setter="set_persistence" getter="get_persistence" default="0.5"> Contribution factor of the different octaves. A [code]persistence[/code] value of 1 means all the octaves have the same contribution, a value of 0.5 means each octave contributes half as much as the previous one. </member> - <member name="seed" type="int" setter="set_seed" getter="get_seed"> + <member name="seed" type="int" setter="set_seed" getter="get_seed" default="0"> Seed used to generate random values, different seeds will generate different noise maps. </member> </members> diff --git a/modules/opensimplex/icons/icon_noise_texture.svg b/modules/opensimplex/icons/icon_noise_texture.svg new file mode 100644 index 0000000000..5908c2b2d4 --- /dev/null +++ b/modules/opensimplex/icons/icon_noise_texture.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m2 1c-0.55228 0-1 0.44772-1 1v12c0 0.55228 0.44772 1 1 1h12c0.55228 0 1-0.44772 1-1v-12c0-0.55228-0.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/> +</svg> diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp index be522a9ab1..9240183265 100644 --- a/modules/opensimplex/noise_texture.cpp +++ b/modules/opensimplex/noise_texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,6 +41,7 @@ NoiseTexture::NoiseTexture() { size = Vector2i(512, 512); seamless = false; as_normalmap = false; + bump_strength = 8.0; flags = FLAGS_DEFAULT; noise = Ref<OpenSimplexNoise>(); @@ -68,6 +69,9 @@ void NoiseTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_as_normalmap", "as_normalmap"), &NoiseTexture::set_as_normalmap); ClassDB::bind_method(D_METHOD("is_normalmap"), &NoiseTexture::is_normalmap); + ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture::set_bump_strength); + ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength); + ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture::_update_texture); ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture::_generate_texture); ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture::_thread_done); @@ -76,9 +80,19 @@ void NoiseTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise"); } +void NoiseTexture::_validate_property(PropertyInfo &property) const { + + if (property.name == "bump_strength") { + if (!as_normalmap) { + property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + } + } +} + void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) { data = p_image; if (data.is_valid()) { @@ -129,7 +143,7 @@ Ref<Image> NoiseTexture::_generate_texture() { } if (as_normalmap) { - image->bumpmap_to_normalmap(); + image->bumpmap_to_normalmap(bump_strength); } return image; @@ -202,12 +216,26 @@ void NoiseTexture::set_as_normalmap(bool p_as_normalmap) { if (p_as_normalmap == as_normalmap) return; as_normalmap = p_as_normalmap; _queue_update(); + _change_notify(); } bool NoiseTexture::is_normalmap() { return as_normalmap; } +void NoiseTexture::set_bump_strength(float p_bump_strength) { + + if (p_bump_strength == bump_strength) return; + bump_strength = p_bump_strength; + if (as_normalmap) + _queue_update(); +} + +float NoiseTexture::get_bump_strength() { + + return bump_strength; +} + int NoiseTexture::get_width() const { return size.x; diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h index 2a4c32d633..5e4a02fcee 100644 --- a/modules/opensimplex/noise_texture.h +++ b/modules/opensimplex/noise_texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ #include "editor/property_editor.h" class NoiseTexture : public Texture { - GDCLASS(NoiseTexture, Texture) + GDCLASS(NoiseTexture, Texture); private: Ref<Image> data; @@ -58,6 +58,7 @@ private: Vector2i size; bool seamless; bool as_normalmap; + float bump_strength; void _thread_done(const Ref<Image> &p_image); static void _thread_function(void *p_ud); @@ -69,20 +70,24 @@ private: protected: static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; public: void set_noise(Ref<OpenSimplexNoise> p_noise); Ref<OpenSimplexNoise> get_noise(); void set_width(int p_width); - void set_height(int p_hieght); + void set_height(int p_height); void set_seamless(bool p_seamless); bool get_seamless(); - void set_as_normalmap(bool p_seamless); + void set_as_normalmap(bool p_as_normalmap); bool is_normalmap(); + void set_bump_strength(float p_bump_strength); + float get_bump_strength(); + int get_width() const; int get_height() const; diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp index bfc2732ff4..3a3a698e5c 100644 --- a/modules/opensimplex/open_simplex_noise.cpp +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -173,6 +173,7 @@ void OpenSimplexNoise::_bind_methods() { ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image); ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image); + ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &OpenSimplexNoise::get_noise_1d); ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &OpenSimplexNoise::get_noise_2d); ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &OpenSimplexNoise::get_noise_3d); ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &OpenSimplexNoise::get_noise_4d); @@ -187,6 +188,11 @@ void OpenSimplexNoise::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); } +float OpenSimplexNoise::get_noise_1d(float x) { + + return get_noise_2d(x, 1.0); +} + float OpenSimplexNoise::get_noise_2d(float x, float y) { x /= period; diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index a9bee266e8..c31cbf68c3 100644 --- a/modules/opensimplex/open_simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,7 +38,7 @@ #include "thirdparty/misc/open-simplex-noise.h" class OpenSimplexNoise : public Resource { - GDCLASS(OpenSimplexNoise, Resource) + GDCLASS(OpenSimplexNoise, Resource); OBJ_SAVE_TYPE(OpenSimplexNoise); osn_context contexts[6]; @@ -73,6 +73,7 @@ public: Ref<Image> get_image(int p_width, int p_height); Ref<Image> get_seamless_image(int p_size); + float get_noise_1d(float x); float get_noise_2d(float x, float y); float get_noise_3d(float x, float y, float z); float get_noise_4d(float x, float y, float z, float w); diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp index d1c77da257..e110752109 100644 --- a/modules/opensimplex/register_types.cpp +++ b/modules/opensimplex/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opensimplex/register_types.h b/modules/opensimplex/register_types.h index 5e71a30ea6..733a4812e0 100644 --- a/modules/opensimplex/register_types.h +++ b/modules/opensimplex/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 508aec7057..1db5b0987e 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -12,196 +12,203 @@ if env['builtin_opus']: thirdparty_dir = "#thirdparty/opus/" thirdparty_sources = [ - "silk/tables_other.c", - "silk/sum_sqr_shift.c", - "silk/PLC.c", - "silk/dec_API.c", - "silk/decode_pulses.c", - "silk/inner_prod_aligned.c", - "silk/init_encoder.c", - "silk/interpolate.c", - "silk/stereo_encode_pred.c", - "silk/decode_frame.c", - "silk/NLSF_del_dec_quant.c", - "silk/VAD.c", - "silk/resampler_private_AR2.c", - "silk/NLSF_unpack.c", - "silk/resampler_down2.c", - "silk/sort.c", - "silk/resampler_private_IIR_FIR.c", - "silk/resampler_down2_3.c", - "silk/resampler_private_up2_HQ.c", - "silk/tables_gain.c", - "silk/stereo_find_predictor.c", - "silk/stereo_quant_pred.c", - "silk/NLSF_stabilize.c", - "silk/ana_filt_bank_1.c", - "silk/check_control_input.c", - "silk/bwexpander.c", - "silk/A2NLSF.c", - "silk/LPC_inv_pred_gain.c", - "silk/log2lin.c", - "silk/process_NLSFs.c", - "silk/sigm_Q15.c", - "silk/VQ_WMat_EC.c", - "silk/quant_LTP_gains.c", - "silk/resampler_private_down_FIR.c", - "silk/NLSF_decode.c", - "silk/control_codec.c", - "silk/NLSF_VQ_weights_laroia.c", - "silk/decode_pitch.c", - "silk/stereo_decode_pred.c", - "silk/tables_pulses_per_block.c", + + # Sync with opus_sources.mk + "opus.c", + "opus_decoder.c", + "opus_encoder.c", + "opus_multistream.c", + "opus_multistream_encoder.c", + "opus_multistream_decoder.c", + "repacketizer.c", + + "analysis.c", + "mlp.c", + "mlp_data.c", + + # Sync with libopusfile Makefile.am + "info.c", + "internal.c", + "opusfile.c", + "stream.c", + + # Sync with celt_sources.mk + "celt/bands.c", + "celt/celt.c", + "celt/celt_encoder.c", + "celt/celt_decoder.c", + "celt/cwrs.c", + "celt/entcode.c", + "celt/entdec.c", + "celt/entenc.c", + "celt/kiss_fft.c", + "celt/laplace.c", + "celt/mathops.c", + "celt/mdct.c", + "celt/modes.c", + "celt/pitch.c", + "celt/celt_lpc.c", + "celt/quant_bands.c", + "celt/rate.c", + "celt/vq.c", + #"celt/arm/arm_celt_map.c", + #"celt/arm/armcpu.c", + #"celt/arm/celt_ne10_fft.c", + #"celt/arm/celt_ne10_mdct.c", + #"celt/arm/celt_neon_intr.c", + + # Sync with silk_sources.mk + "silk/CNG.c", + "silk/code_signs.c", "silk/init_decoder.c", - "silk/table_LSF_cos.c", "silk/decode_core.c", - "silk/code_signs.c", - "silk/enc_API.c", - "silk/tables_LTP.c", - "silk/pitch_est_tables.c", - "silk/biquad_alt.c", - "silk/encode_indices.c", - "silk/tables_NLSF_CB_WB.c", - "silk/debug.c", + "silk/decode_frame.c", "silk/decode_parameters.c", - "silk/tables_pitch_lag.c", - "silk/NLSF2A.c", - "silk/resampler.c", "silk/decode_indices.c", - "silk/NLSF_VQ.c", - "silk/bwexpander_32.c", - "silk/tables_NLSF_CB_NB_MB.c", + "silk/decode_pulses.c", + "silk/decoder_set_fs.c", + "silk/dec_API.c", + "silk/enc_API.c", + "silk/encode_indices.c", "silk/encode_pulses.c", + "silk/gain_quant.c", + "silk/interpolate.c", + "silk/LP_variable_cutoff.c", + "silk/NLSF_decode.c", + "silk/NSQ.c", "silk/NSQ_del_dec.c", - "silk/control_SNR.c", + "silk/PLC.c", "silk/shell_coder.c", + "silk/tables_gain.c", + "silk/tables_LTP.c", + "silk/tables_NLSF_CB_NB_MB.c", + "silk/tables_NLSF_CB_WB.c", + "silk/tables_other.c", + "silk/tables_pitch_lag.c", + "silk/tables_pulses_per_block.c", + "silk/VAD.c", + "silk/control_audio_bandwidth.c", + "silk/quant_LTP_gains.c", + "silk/VQ_WMat_EC.c", + "silk/HP_variable_cutoff.c", "silk/NLSF_encode.c", - "silk/stereo_MS_to_LR.c", + "silk/NLSF_VQ.c", + "silk/NLSF_unpack.c", + "silk/NLSF_del_dec_quant.c", + "silk/process_NLSFs.c", "silk/stereo_LR_to_MS.c", - "silk/HP_variable_cutoff.c", + "silk/stereo_MS_to_LR.c", + "silk/check_control_input.c", + "silk/control_SNR.c", + "silk/init_encoder.c", + "silk/control_codec.c", + "silk/A2NLSF.c", + "silk/ana_filt_bank_1.c", + "silk/biquad_alt.c", + "silk/bwexpander_32.c", + "silk/bwexpander.c", + "silk/debug.c", + "silk/decode_pitch.c", + "silk/inner_prod_aligned.c", + "silk/lin2log.c", + "silk/log2lin.c", "silk/LPC_analysis_filter.c", - "silk/CNG.c", - "silk/decoder_set_fs.c", + "silk/LPC_inv_pred_gain.c", + "silk/table_LSF_cos.c", + "silk/NLSF2A.c", + "silk/NLSF_stabilize.c", + "silk/NLSF_VQ_weights_laroia.c", + "silk/pitch_est_tables.c", + "silk/resampler.c", + "silk/resampler_down2_3.c", + "silk/resampler_down2.c", + "silk/resampler_private_AR2.c", + "silk/resampler_private_down_FIR.c", + "silk/resampler_private_IIR_FIR.c", + "silk/resampler_private_up2_HQ.c", "silk/resampler_rom.c", - "silk/control_audio_bandwidth.c", - "silk/lin2log.c", - "silk/LP_variable_cutoff.c", - "silk/NSQ.c", - "silk/gain_quant.c", - "celt/laplace.c", - "celt/vq.c", - "celt/quant_bands.c", - "celt/kiss_fft.c", - "celt/entcode.c", - "celt/entenc.c", - "celt/celt_lpc.c", - "celt/pitch.c", - "celt/rate.c", - "celt/mathops.c", - #"celt/arm/armcpu.c", - #"celt/arm/celt_neon_intr.c", - #"celt/arm/celt_ne10_mdct.c", - #"celt/arm/celt_ne10_fft.c", - #"celt/arm/arm_celt_map.c", - "celt/celt_encoder.c", - "celt/celt.c", - "celt/bands.c", - "celt/cwrs.c", - "celt/entdec.c", - "celt/celt_decoder.c", - "celt/mdct.c", - "celt/modes.c", - "repacketizer.c", - "mlp_data.c", - "opus_multistream.c", - "opusfile.c", - "opus_encoder.c", - "analysis.c", - "mlp.c", - "info.c", - "stream.c", - "opus_decoder.c", - "internal.c", - "wincerts.c", - "opus.c", - "opus_multistream_encoder.c", - "http.c", - "opus_multistream_decoder.c" + "silk/sigm_Q15.c", + "silk/sort.c", + "silk/sum_sqr_shift.c", + "silk/stereo_decode_pred.c", + "silk/stereo_encode_pred.c", + "silk/stereo_find_predictor.c", + "silk/stereo_quant_pred.c", ] opus_sources_silk = [] - if("opus_fixed_point" in env and env.opus_fixed_point == "yes"): - env_opus.Append(CFLAGS=["-DFIXED_POINT"]) + if env["platform"] in ["android", "iphone", "javascript"]: + env_opus.Append(CPPDEFINES=["FIXED_POINT"]) opus_sources_silk = [ - "silk/fixed/schur64_FIX.c", - "silk/fixed/residual_energy16_FIX.c", + "silk/fixed/LTP_analysis_filter_FIX.c", + "silk/fixed/LTP_scale_ctrl_FIX.c", + "silk/fixed/corrMatrix_FIX.c", "silk/fixed/encode_frame_FIX.c", - "silk/fixed/regularize_correlations_FIX.c", - "silk/fixed/apply_sine_window_FIX.c", - "silk/fixed/solve_LS_FIX.c", - "silk/fixed/schur_FIX.c", - "silk/fixed/pitch_analysis_core_FIX.c", - "silk/fixed/noise_shape_analysis_FIX.c", + "silk/fixed/find_LPC_FIX.c", "silk/fixed/find_LTP_FIX.c", - "silk/fixed/vector_ops_FIX.c", - "silk/fixed/autocorr_FIX.c", - "silk/fixed/warped_autocorrelation_FIX.c", "silk/fixed/find_pitch_lags_FIX.c", - "silk/fixed/k2a_Q16_FIX.c", - "silk/fixed/LTP_scale_ctrl_FIX.c", - "silk/fixed/corrMatrix_FIX.c", + "silk/fixed/find_pred_coefs_FIX.c", + "silk/fixed/noise_shape_analysis_FIX.c", "silk/fixed/prefilter_FIX.c", - "silk/fixed/find_LPC_FIX.c", - "silk/fixed/residual_energy_FIX.c", "silk/fixed/process_gains_FIX.c", - "silk/fixed/LTP_analysis_filter_FIX.c", - "silk/fixed/k2a_FIX.c", + "silk/fixed/regularize_correlations_FIX.c", + "silk/fixed/residual_energy16_FIX.c", + "silk/fixed/residual_energy_FIX.c", + "silk/fixed/solve_LS_FIX.c", + "silk/fixed/warped_autocorrelation_FIX.c", + "silk/fixed/apply_sine_window_FIX.c", + "silk/fixed/autocorr_FIX.c", "silk/fixed/burg_modified_FIX.c", - "silk/fixed/find_pred_coefs_FIX.c" + "silk/fixed/k2a_FIX.c", + "silk/fixed/k2a_Q16_FIX.c", + "silk/fixed/pitch_analysis_core_FIX.c", + "silk/fixed/vector_ops_FIX.c", + "silk/fixed/schur64_FIX.c", + "silk/fixed/schur_FIX.c", ] else: opus_sources_silk = [ - "silk/float/LTP_scale_ctrl_FLP.c", - "silk/float/regularize_correlations_FLP.c", + "silk/float/apply_sine_window_FLP.c", "silk/float/corrMatrix_FLP.c", + "silk/float/encode_frame_FLP.c", + "silk/float/find_LPC_FLP.c", + "silk/float/find_LTP_FLP.c", + "silk/float/find_pitch_lags_FLP.c", + "silk/float/find_pred_coefs_FLP.c", "silk/float/LPC_analysis_filter_FLP.c", - "silk/float/levinsondurbin_FLP.c", - "silk/float/schur_FLP.c", - "silk/float/scale_vector_FLP.c", - "silk/float/apply_sine_window_FLP.c", - "silk/float/pitch_analysis_core_FLP.c", - "silk/float/wrappers_FLP.c", - "silk/float/bwexpander_FLP.c", - "silk/float/warped_autocorrelation_FLP.c", + "silk/float/LTP_analysis_filter_FLP.c", + "silk/float/LTP_scale_ctrl_FLP.c", + "silk/float/noise_shape_analysis_FLP.c", + "silk/float/prefilter_FLP.c", + "silk/float/process_gains_FLP.c", + "silk/float/regularize_correlations_FLP.c", + "silk/float/residual_energy_FLP.c", "silk/float/solve_LS_FLP.c", - "silk/float/find_LPC_FLP.c", + "silk/float/warped_autocorrelation_FLP.c", + "silk/float/wrappers_FLP.c", "silk/float/autocorrelation_FLP.c", - "silk/float/find_pred_coefs_FLP.c", - "silk/float/find_pitch_lags_FLP.c", "silk/float/burg_modified_FLP.c", - "silk/float/find_LTP_FLP.c", + "silk/float/bwexpander_FLP.c", "silk/float/energy_FLP.c", - "silk/float/sort_FLP.c", - "silk/float/LPC_inv_pred_gain_FLP.c", - "silk/float/k2a_FLP.c", - "silk/float/noise_shape_analysis_FLP.c", "silk/float/inner_product_FLP.c", - "silk/float/process_gains_FLP.c", - "silk/float/encode_frame_FLP.c", + "silk/float/k2a_FLP.c", + "silk/float/levinsondurbin_FLP.c", + "silk/float/LPC_inv_pred_gain_FLP.c", + "silk/float/pitch_analysis_core_FLP.c", "silk/float/scale_copy_vector_FLP.c", - "silk/float/residual_energy_FLP.c", - "silk/float/LTP_analysis_filter_FLP.c", - "silk/float/prefilter_FLP.c" + "silk/float/scale_vector_FLP.c", + "silk/float/schur_FLP.c", + "silk/float/sort_FLP.c", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk] # also requires libogg if env['builtin_libogg']: - env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_opus.Prepend(CPPPATH=["#thirdparty/libogg"]) - env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"]) + env_opus.Append(CPPDEFINES=["HAVE_CONFIG_H"]) thirdparty_include_paths = [ "", @@ -211,7 +218,18 @@ if env['builtin_opus']: "silk/fixed", "silk/float", ] - env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) + env_opus.Prepend(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) + + if env["platform"] == "android": + if ("android_arch" in env and env["android_arch"] == "armv7"): + env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"]) + elif ("android_arch" in env and env["android_arch"] == "arm64v8"): + env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"]) + elif env["platform"] == "iphone": + if ("arch" in env and env["arch"] == "arm"): + env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"]) + elif ("arch" in env and env["arch"] == "arm64"): + env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"]) env_thirdparty = env_opus.Clone() env_thirdparty.disable_warnings() diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp index 3920b3cd6e..615081d818 100644 --- a/modules/opus/audio_stream_opus.cpp +++ b/modules/opus/audio_stream_opus.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -280,7 +280,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) { int todo = p_frames; - if (todo == 0 || todo < MIN_MIX) { + if (todo < MIN_MIX) { break; } @@ -313,7 +313,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) { bool ok = op_pcm_seek(opus_file, (loop_restart_time * osrate) + pre_skip) == 0; if (!ok) { playing = false; - ERR_PRINT("loop restart time rejected") + ERR_PRINT("Loop restart time rejected"); } frames_mixed = (loop_restart_time * osrate) + pre_skip; diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h index c004adeb77..04c2e9d9f0 100644 --- a/modules/opus/audio_stream_opus.h +++ b/modules/opus/audio_stream_opus.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,7 @@ class AudioStreamPlaybackOpus : public AudioStreamPlayback { - GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback) + GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback); enum { MIN_MIX = 1024 @@ -117,7 +117,7 @@ public: class AudioStreamOpus : public AudioStream { - GDCLASS(AudioStreamOpus, AudioStream) + GDCLASS(AudioStreamOpus, AudioStream); String file; diff --git a/modules/opus/register_types.cpp b/modules/opus/register_types.cpp index f34555841e..be266c8591 100644 --- a/modules/opus/register_types.cpp +++ b/modules/opus/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "audio_stream_opus.h" -static ResourceFormatLoaderAudioStreamOpus *opus_stream_loader = NULL; +static Ref<ResourceFormatLoaderAudioStreamOpus> opus_stream_loader; void register_opus_types() { // Sorry guys, do not enable this unless you can figure out a way diff --git a/modules/opus/register_types.h b/modules/opus/register_types.h index 84335adfc9..611bbf4fd0 100644 --- a/modules/opus/register_types.h +++ b/modules/opus/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opus/stub/register_types.cpp b/modules/opus/stub/register_types.cpp index fe2bce63ba..e1f6ce7a27 100644 --- a/modules/opus/stub/register_types.cpp +++ b/modules/opus/stub/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/opus/stub/register_types.h b/modules/opus/stub/register_types.h index 84335adfc9..611bbf4fd0 100644 --- a/modules/opus/stub/register_types.h +++ b/modules/opus/stub/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub index 2e4a792a36..18da38fbbd 100644 --- a/modules/pvr/SCsub +++ b/modules/pvr/SCsub @@ -17,7 +17,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_pvr.Append(CPPPATH=[thirdparty_dir]) +env_pvr.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_pvr.Clone() env_thirdparty.disable_warnings() diff --git a/modules/pvr/register_types.cpp b/modules/pvr/register_types.cpp index 0991828ef2..c87e631a11 100644 --- a/modules/pvr/register_types.cpp +++ b/modules/pvr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,15 +32,16 @@ #include "texture_loader_pvr.h" -static ResourceFormatPVR *resource_loader_pvr = NULL; +static Ref<ResourceFormatPVR> resource_loader_pvr; void register_pvr_types() { - resource_loader_pvr = memnew(ResourceFormatPVR); + resource_loader_pvr.instance(); ResourceLoader::add_resource_format_loader(resource_loader_pvr); } void unregister_pvr_types() { - memdelete(resource_loader_pvr); + ResourceLoader::remove_resource_format_loader(resource_loader_pvr); + resource_loader_pvr.unref(); } diff --git a/modules/pvr/register_types.h b/modules/pvr/register_types.h index d187ab5334..cafa11791e 100644 --- a/modules/pvr/register_types.h +++ b/modules/pvr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index e6718eb4a2..8b1f21d95d 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "RgbaBitmap.h" #include "core/os/file_access.h" #include <string.h> +#include <new> static void _pvrtc_decompress(Image *p_img); @@ -152,7 +153,7 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, ERR_FAIL_V(RES()); } - w = PoolVector<uint8_t>::Write(); + w.release(); int tex_flags = Texture::FLAG_FILTER | Texture::FLAG_REPEAT; @@ -191,9 +192,9 @@ static void _compress_pvrtc4(Image *p_img) { Ref<Image> img = p_img->duplicate(); bool make_mipmaps = false; - if (img->get_width() % 8 || img->get_height() % 8) { + if (!img->is_size_po2() || img->get_width() != img->get_height()) { make_mipmaps = img->has_mipmaps(); - img->resize(img->get_width() + (8 - (img->get_width() % 8)), img->get_height() + (8 - (img->get_height() % 8))); + img->resize_to_po2(true); } img->convert(Image::FORMAT_RGBA8); if (!img->has_mipmaps() && make_mipmaps) @@ -203,7 +204,7 @@ static void _compress_pvrtc4(Image *p_img) { Ref<Image> new_img; new_img.instance(); - new_img->create(img->get_width(), img->get_height(), true, use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); + new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); PoolVector<uint8_t> data = new_img->get_data(); { @@ -215,14 +216,11 @@ static void _compress_pvrtc4(Image *p_img) { int ofs, size, w, h; img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); Javelin::RgbaBitmap bm(w, h); - copymem(bm.GetData(), &r[ofs], size); - { + for (int j = 0; j < size / 4; j++) { Javelin::ColorRgba<unsigned char> *dp = bm.GetData(); - for (int j = 0; j < size / 4; j++) { - SWAP(dp[j].r, dp[j].b); - } + /* red and Green colors are swapped. */ + new (dp) Javelin::ColorRgba<unsigned char>(r[ofs + 4 * j + 2], r[ofs + 4 * j + 1], r[ofs + 4 * j], r[ofs + 4 * j + 3]); } - new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm); } @@ -657,8 +655,8 @@ static void _pvrtc_decompress(Image *p_img) { decompress_pvrtc((PVRTCBlock *)r.ptr(), _2bit, p_img->get_width(), p_img->get_height(), 0, (unsigned char *)w.ptr()); - w = PoolVector<uint8_t>::Write(); - r = PoolVector<uint8_t>::Read(); + w.release(); + r.release(); bool make_mipmaps = p_img->has_mipmaps(); p_img->create(p_img->get_width(), p_img->get_height(), false, Image::FORMAT_RGBA8, newdata); diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h index c859a4cdda..606268e447 100644 --- a/modules/pvr/texture_loader_pvr.h +++ b/modules/pvr/texture_loader_pvr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/recast/SCsub b/modules/recast/SCsub index 4a06653968..94d9968164 100644 --- a/modules/recast/SCsub +++ b/modules/recast/SCsub @@ -23,7 +23,7 @@ if env['builtin_recast']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"]) + env_recast.Prepend(CPPPATH=[thirdparty_dir + "/Include"]) env_thirdparty = env_recast.Clone() env_thirdparty.disable_warnings() diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index d121a82d9e..9f30806925 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,28 +54,26 @@ void NavigationMeshEditor::_notification(int p_option) { } void NavigationMeshEditor::_bake_pressed() { + button_bake->set_pressed(false); ERR_FAIL_COND(!node); const String conf_warning = node->get_configuration_warning(); if (!conf_warning.empty()) { err_dialog->set_text(conf_warning); err_dialog->popup_centered_minsize(); - button_bake->set_pressed(false); return; } - NavigationMeshGenerator::clear(node->get_navigation_mesh()); - NavigationMeshGenerator::bake(node->get_navigation_mesh(), node); + EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); + EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node); - if (node) { - node->update_gizmo(); - } + node->update_gizmo(); } void NavigationMeshEditor::_clear_pressed() { if (node) - NavigationMeshGenerator::clear(node->get_navigation_mesh()); + EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); button_bake->set_pressed(false); bake_info->set_text(""); diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h index 914d9ab8b9..23d35efc15 100644 --- a/modules/recast/navigation_mesh_editor_plugin.h +++ b/modules/recast/navigation_mesh_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index c1ce2592a4..14467dc5c7 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,14 +29,35 @@ /*************************************************************************/ #include "navigation_mesh_generator.h" - -void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) { +#include "core/math/quick_hull.h" +#include "core/os/thread.h" +#include "editor/editor_settings.h" +#include "scene/3d/collision_shape.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/physics_body.h" +#include "scene/resources/box_shape.h" +#include "scene/resources/capsule_shape.h" +#include "scene/resources/concave_polygon_shape.h" +#include "scene/resources/convex_polygon_shape.h" +#include "scene/resources/cylinder_shape.h" +#include "scene/resources/plane_shape.h" +#include "scene/resources/primitive_meshes.h" +#include "scene/resources/shape.h" +#include "scene/resources/sphere_shape.h" + +#ifdef MODULE_CSG_ENABLED +#include "modules/csg/csg_shape.h" +#endif + +EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL; + +void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) { p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); } -void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) { +void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) { int current_vertex_count = 0; for (int i = 0; i < p_mesh->get_surface_count(); i++) { @@ -66,48 +87,171 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform PoolVector<int> mesh_indices = a[Mesh::ARRAY_INDEX]; PoolVector<int>::Read ir = mesh_indices.read(); - for (int i = 0; i < mesh_vertices.size(); i++) { - _add_vertex(p_xform.xform(vr[i]), p_verticies); + for (int j = 0; j < mesh_vertices.size(); j++) { + _add_vertex(p_xform.xform(vr[j]), p_verticies); } - for (int i = 0; i < face_count; i++) { + for (int j = 0; j < face_count; j++) { // CCW - p_indices.push_back(current_vertex_count + (ir[i * 3 + 0])); - p_indices.push_back(current_vertex_count + (ir[i * 3 + 2])); - p_indices.push_back(current_vertex_count + (ir[i * 3 + 1])); + p_indices.push_back(current_vertex_count + (ir[j * 3 + 0])); + p_indices.push_back(current_vertex_count + (ir[j * 3 + 2])); + p_indices.push_back(current_vertex_count + (ir[j * 3 + 1])); } } else { face_count = mesh_vertices.size() / 3; - for (int i = 0; i < face_count; i++) { - _add_vertex(p_xform.xform(vr[i * 3 + 0]), p_verticies); - _add_vertex(p_xform.xform(vr[i * 3 + 2]), p_verticies); - _add_vertex(p_xform.xform(vr[i * 3 + 1]), p_verticies); - - p_indices.push_back(current_vertex_count + (i * 3 + 0)); - p_indices.push_back(current_vertex_count + (i * 3 + 1)); - p_indices.push_back(current_vertex_count + (i * 3 + 2)); + for (int j = 0; j < face_count; j++) { + _add_vertex(p_xform.xform(vr[j * 3 + 0]), p_verticies); + _add_vertex(p_xform.xform(vr[j * 3 + 2]), p_verticies); + _add_vertex(p_xform.xform(vr[j * 3 + 1]), p_verticies); + + p_indices.push_back(current_vertex_count + (j * 3 + 0)); + p_indices.push_back(current_vertex_count + (j * 3 + 1)); + p_indices.push_back(current_vertex_count + (j * 3 + 2)); } } } } -void NavigationMeshGenerator::_parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices) { +void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) { + int face_count = p_faces.size() / 3; + int current_vertex_count = p_verticies.size() / 3; + + for (int j = 0; j < face_count; j++) { + _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_verticies); + _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_verticies); + _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_verticies); - if (Object::cast_to<MeshInstance>(p_node)) { + p_indices.push_back(current_vertex_count + (j * 3 + 0)); + p_indices.push_back(current_vertex_count + (j * 3 + 2)); + p_indices.push_back(current_vertex_count + (j * 3 + 1)); + } +} + +void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask) { + + if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(p_node); Ref<Mesh> mesh = mesh_instance->get_mesh(); if (mesh.is_valid()) { - _add_mesh(mesh, p_base_inverse * mesh_instance->get_global_transform(), p_verticies, p_indices); + _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_verticies, p_indices); } } +#ifdef MODULE_CSG_ENABLED + if (Object::cast_to<CSGShape>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { + + CSGShape *csg_shape = Object::cast_to<CSGShape>(p_node); + Array meshes = csg_shape->get_meshes(); + if (!meshes.empty()) { + Ref<Mesh> mesh = meshes[1]; + if (mesh.is_valid()) { + _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices); + } + } + } +#endif + + if (Object::cast_to<StaticBody>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) { + StaticBody *static_body = Object::cast_to<StaticBody>(p_node); + + if (static_body->get_collision_layer() & p_collision_mask) { + + for (int i = 0; i < p_node->get_child_count(); ++i) { + Node *child = p_node->get_child(i); + if (Object::cast_to<CollisionShape>(child)) { + CollisionShape *col_shape = Object::cast_to<CollisionShape>(child); + + Transform transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform(); + + Ref<Mesh> mesh; + Ref<Shape> s = col_shape->get_shape(); + + BoxShape *box = Object::cast_to<BoxShape>(*s); + if (box) { + Ref<CubeMesh> cube_mesh; + cube_mesh.instance(); + cube_mesh->set_size(box->get_extents() * 2.0); + mesh = cube_mesh; + } + + CapsuleShape *capsule = Object::cast_to<CapsuleShape>(*s); + if (capsule) { + Ref<CapsuleMesh> capsule_mesh; + capsule_mesh.instance(); + capsule_mesh->set_radius(capsule->get_radius()); + capsule_mesh->set_mid_height(capsule->get_height() / 2.0); + mesh = capsule_mesh; + } + + CylinderShape *cylinder = Object::cast_to<CylinderShape>(*s); + if (cylinder) { + Ref<CylinderMesh> cylinder_mesh; + cylinder_mesh.instance(); + cylinder_mesh->set_height(cylinder->get_height()); + cylinder_mesh->set_bottom_radius(cylinder->get_radius()); + cylinder_mesh->set_top_radius(cylinder->get_radius()); + mesh = cylinder_mesh; + } + + SphereShape *sphere = Object::cast_to<SphereShape>(*s); + if (sphere) { + Ref<SphereMesh> sphere_mesh; + sphere_mesh.instance(); + sphere_mesh->set_radius(sphere->get_radius()); + sphere_mesh->set_height(sphere->get_radius() * 2.0); + mesh = sphere_mesh; + } + + ConcavePolygonShape *concave_polygon = Object::cast_to<ConcavePolygonShape>(*s); + if (concave_polygon) { + _add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices); + } + + ConvexPolygonShape *convex_polygon = Object::cast_to<ConvexPolygonShape>(*s); + if (convex_polygon) { + Vector<Vector3> varr = Variant(convex_polygon->get_points()); + Geometry::MeshData md; + + Error err = QuickHull::build(varr, md); + + if (err == OK) { + PoolVector3Array faces; + + for (int j = 0; j < md.faces.size(); ++j) { + Geometry::MeshData::Face face = md.faces[j]; + + for (int k = 2; k < face.indices.size(); ++k) { + faces.push_back(md.vertices[face.indices[0]]); + faces.push_back(md.vertices[face.indices[k - 1]]); + faces.push_back(md.vertices[face.indices[k]]); + } + } + + _add_faces(faces, transform, p_verticies, p_indices); + } + } + + if (mesh.is_valid()) { + _add_mesh(mesh, transform, p_verticies, p_indices); + } + } + } + } + } + + if (Object::cast_to<Spatial>(p_node)) { + + Spatial *spatial = Object::cast_to<Spatial>(p_node); + p_accumulated_transform = p_accumulated_transform * spatial->get_transform(); + } + for (int i = 0; i < p_node->get_child_count(); i++) { - _parse_geometry(p_base_inverse, p_node->get_child(i), p_verticies, p_indices); + _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask); } } -void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) { +void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) { PoolVector<Vector3> nav_vertices; @@ -126,15 +270,16 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con for (unsigned int j = 0; j < ntris; j++) { Vector<int> nav_indices; nav_indices.resize(3); + // Polygon order in recast is opposite than godot's nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0])); - nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 1])); - nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 2])); + nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 2])); + nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 1])); p_nav_mesh->add_polygon(nav_indices); } } } -void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep, +void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep, rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices) { rcContext ctx; @@ -256,7 +401,18 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> detail_mesh = 0; } -void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) { +EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() { + return singleton; +} + +EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() { + singleton = this; +} + +EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() { +} + +void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) { ERR_FAIL_COND(!p_nav_mesh.is_valid()); @@ -266,7 +422,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) Vector<float> vertices; Vector<int> indices; - _parse_geometry(Object::cast_to<Spatial>(p_node)->get_global_transform().affine_inverse(), p_node, vertices, indices); + _parse_geometry(Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(), p_node, vertices, indices, p_nav_mesh->get_parsed_geometry_type(), p_nav_mesh->get_collision_mask()); if (vertices.size() > 0 && indices.size() > 0) { @@ -296,9 +452,14 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) ep.step(TTR("Done!"), 11); } -void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) { +void EditorNavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) { if (p_nav_mesh.is_valid()) { p_nav_mesh->clear_polygons(); p_nav_mesh->set_vertices(PoolVector<Vector3>()); } } + +void EditorNavigationMeshGenerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &EditorNavigationMeshGenerator::bake); + ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &EditorNavigationMeshGenerator::clear); +} diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h index 2f2f57d721..30a6e3c835 100644 --- a/modules/recast/navigation_mesh_generator.h +++ b/modules/recast/navigation_mesh_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,20 +31,23 @@ #ifndef NAVIGATION_MESH_GENERATOR_H #define NAVIGATION_MESH_GENERATOR_H -#include "core/os/thread.h" #include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "scene/3d/mesh_instance.h" #include "scene/3d/navigation_mesh.h" -#include "scene/resources/shape.h" #include <Recast.h> -class NavigationMeshGenerator { +class EditorNavigationMeshGenerator : public Object { + GDCLASS(EditorNavigationMeshGenerator, Object); + + static EditorNavigationMeshGenerator *singleton; + protected: + static void _bind_methods(); + static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies); static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); - static void _parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices); + static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); + static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask); static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh); static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep, @@ -52,8 +55,13 @@ protected: rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices); public: - static void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node); - static void clear(Ref<NavigationMesh> p_nav_mesh); + static EditorNavigationMeshGenerator *get_singleton(); + + EditorNavigationMeshGenerator(); + ~EditorNavigationMeshGenerator(); + + void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node); + void clear(Ref<NavigationMesh> p_nav_mesh); }; #endif // NAVIGATION_MESH_GENERATOR_H diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp index f2f18fc86f..44129fbb61 100644 --- a/modules/recast/register_types.cpp +++ b/modules/recast/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,30 @@ #include "navigation_mesh_editor_plugin.h" +#ifdef TOOLS_ENABLED +EditorNavigationMeshGenerator *_nav_mesh_generator = NULL; +#endif + void register_recast_types() { +#ifdef TOOLS_ENABLED EditorPlugins::add_by_type<NavigationMeshEditorPlugin>(); + _nav_mesh_generator = memnew(EditorNavigationMeshGenerator); + + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + ClassDB::register_class<EditorNavigationMeshGenerator>(); + + ClassDB::set_current_api(prev_api); + + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", EditorNavigationMeshGenerator::get_singleton())); +#endif } -void unregister_recast_types() {} +void unregister_recast_types() { +#ifdef TOOLS_ENABLED + if (_nav_mesh_generator) { + memdelete(_nav_mesh_generator); + } +#endif +} diff --git a/modules/recast/register_types.h b/modules/recast/register_types.h index aaa32e0cdf..0b64143e97 100644 --- a/modules/recast/register_types.h +++ b/modules/recast/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 99c25add45..1be5af02a5 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -9,10 +9,10 @@ if env['builtin_pcre2']: jit_blacklist = ['javascript', 'uwp'] thirdparty_dir = '#thirdparty/pcre2/src/' - thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H'] + thirdparty_flags = ['PCRE2_STATIC', 'HAVE_CONFIG_H'] if 'platform' in env and env['platform'] not in jit_blacklist: - thirdparty_flags.append('-DSUPPORT_JIT') + thirdparty_flags.append('SUPPORT_JIT') thirdparty_sources = [ "pcre2_auto_possess.c", @@ -33,6 +33,7 @@ if env['builtin_pcre2']: "pcre2_newline.c", "pcre2_ord2utf.c", "pcre2_pattern_info.c", + "pcre2_script_run.c", "pcre2_serialize.c", "pcre2_string_utils.c", "pcre2_study.c", @@ -46,18 +47,18 @@ if env['builtin_pcre2']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_regex.Append(CPPPATH=[thirdparty_dir]) - env_regex.Append(CPPFLAGS=thirdparty_flags) + env_regex.Prepend(CPPPATH=[thirdparty_dir]) + env_regex.Append(CPPDEFINES=thirdparty_flags) def pcre2_builtin(width): env_pcre2 = env_regex.Clone() env_pcre2.disable_warnings() env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) - env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width]) + env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)]) pcre2_builtin("16") pcre2_builtin("32") -env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) +env_regex.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", 0)]) env_regex.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 75e8903ff8..74b06039d4 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegEx" inherits="Reference" category="Core" version="3.1"> +<class name="RegEx" inherits="Reference" category="Core" version="3.2"> <brief_description> Class for searching text for patterns using regular expressions. </brief_description> <description> - Regular Expression (or regex) is a compact programming language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. + A regular expression (or regex) is a compact language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used. [codeblock] var regex = RegEx.new() @@ -19,7 +19,7 @@ if result: print(result.get_string()) # Would print n-0123 [/codeblock] - The results of capturing groups [code]()[/code] can be retrieved by passing the group number to the various functions in [RegExMatch]. Group 0 is the default and would always refer to the entire pattern. In the above example, calling [code]result.get_string(1)[/code] would give you [code]0123[/code]. + The results of capturing groups [code]()[/code] can be retrieved by passing the group number to the various functions in [RegExMatch]. Group 0 is the default and will always refer to the entire pattern. In the above example, calling [code]result.get_string(1)[/code] would give you [code]0123[/code]. This version of RegEx also supports named capturing groups, and the names can be used to retrieve the results. If two or more groups have the same name, the name would only refer to the first one with a match. [codeblock] var regex = RegEx.new() @@ -28,7 +28,7 @@ if result: print(result.get_string("digit")) # Would print 2f [/codeblock] - If you need to process multiple results, [method search_all] generates a list of all non-overlapping results. This can be combined with a for-loop for convenience. + If you need to process multiple results, [method search_all] generates a list of all non-overlapping results. This can be combined with a [code]for[/code] loop for convenience. [codeblock] for result in regex.search_all("d01, d03, d0c, x3f and x42"): print(result.get_string("digit")) @@ -38,14 +38,12 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> </return> <description> - This method resets the state of the object, as it was freshly created. Namely, it unassigns the regular expression of this object. + This method resets the state of the object, as if it was freshly created. Namely, it unassigns the regular expression of this object. </description> </method> <method name="compile"> @@ -54,7 +52,7 @@ <argument index="0" name="pattern" type="String"> </argument> <description> - Compiles and assign the search pattern to use. Returns OK if the compilation is successful. If an error is encountered the details are printed to STDOUT and FAILED is returned. + Compiles and assign the search pattern to use. Returns [constant OK] if the compilation is successful. If an error is encountered, details are printed to standard output and an error is returned. </description> </method> <method name="get_group_count" qualifiers="const"> @@ -95,7 +93,7 @@ <argument index="2" name="end" type="int" default="-1"> </argument> <description> - Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise null. The region to search within can be specified without modifying where the start and end anchor would be. + Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise [code]null[/code]. The region to search within can be specified without modifying where the start and end anchor would be. </description> </method> <method name="search_all" qualifiers="const"> @@ -108,7 +106,7 @@ <argument index="2" name="end" type="int" default="-1"> </argument> <description> - Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found an empty array is returned instead. The region to search within can be specified without modifying where the start and end anchor would be. + Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found, an empty array is returned instead. The region to search within can be specified without modifying where the start and end anchor would be. </description> </method> <method name="sub" qualifiers="const"> @@ -125,7 +123,7 @@ <argument index="4" name="end" type="int" default="-1"> </argument> <description> - Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]\1[/code] and [code]\g<name>[/code] expanded and resolved. By default only the first instance is replaced but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be. + Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default, only the first instance is replaced, but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be. </description> </method> </methods> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 3d070d2786..6dec9fc516 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -1,15 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegExMatch" inherits="Reference" category="Core" version="3.1"> +<class name="RegExMatch" inherits="Reference" category="Core" version="3.2"> <brief_description> - Contains the results of a regex search. + Contains the results of a [RegEx] search. </brief_description> <description> - Contains the results of a single regex match returned by [method RegEx.search] and [method RegEx.search_all]. It can be used to find the position and range of the match and its capturing groups, and it can extract its sub-string for you. + Contains the results of a single [RegEx] match returned by [method RegEx.search] and [method RegEx.search_all]. It can be used to find the position and range of the match and its capturing groups, and it can extract its substring for you. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_end" qualifiers="const"> <return type="int"> @@ -50,13 +48,13 @@ </method> </methods> <members> - <member name="names" type="Dictionary" setter="" getter="get_names"> + <member name="names" type="Dictionary" setter="" getter="get_names" default="{}"> A dictionary of named groups and its corresponding group number. Only groups with that were matched are included. If multiple groups have the same name, that name would refer to the first matching one. </member> - <member name="strings" type="Array" setter="" getter="get_strings"> + <member name="strings" type="Array" setter="" getter="get_strings" default="[ ]"> An [Array] of the match and its capturing groups. </member> - <member name="subject" type="String" setter="" getter="get_subject"> + <member name="subject" type="String" setter="" getter="get_subject" default=""""> The source string used with the search pattern to find this matching result. </member> </members> diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 03ef024587..12091caa5f 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/regex.h b/modules/regex/regex.h index 04ebaf6f9e..b0171f74c0 100644 --- a/modules/regex/regex.h +++ b/modules/regex/regex.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp index 73e2c5022d..63687fc0a7 100644 --- a/modules/regex/register_types.cpp +++ b/modules/regex/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h index 872e5eece5..ed32322c01 100644 --- a/modules/regex/register_types.h +++ b/modules/regex/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/register_module_types.h b/modules/register_module_types.h index f4163418e8..559d66c927 100644 --- a/modules/register_module_types.h +++ b/modules/register_module_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/SCsub b/modules/squish/SCsub index 3be85a1efa..15320bcd0c 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -22,7 +22,7 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_squish.Append(CPPPATH=[thirdparty_dir]) + env_squish.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_squish.Clone() env_thirdparty.disable_warnings() diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 4161a0f6ae..64f4c169cb 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -119,7 +119,8 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres case Image::FORMAT_RGBA5551: { dc = Image::DETECTED_RGBA; } break; - default: {} + default: { + } } } @@ -197,8 +198,8 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres h = MAX(h / 2, 1); } - rb = PoolVector<uint8_t>::Read(); - wb = PoolVector<uint8_t>::Write(); + rb.release(); + wb.release(); p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h index dd53f2787a..3047b73b91 100644 --- a/modules/squish/image_compress_squish.h +++ b/modules/squish/image_compress_squish.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp index 9a5bb47f19..be91fba4ef 100644 --- a/modules/squish/register_types.cpp +++ b/modules/squish/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h index 9dbd69c46b..5933146329 100644 --- a/modules/squish/register_types.h +++ b/modules/squish/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 5dbe0b4b00..0922471500 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -188,7 +188,7 @@ void AudioStreamOGGVorbis::set_data(const PoolVector<uint8_t> &p_data) { ogg_stream = stb_vorbis_open_memory((const unsigned char *)src_datar.ptr(), src_data_len, &error, &ogg_alloc); if (!ogg_stream && error == VORBIS_outofmem) { - w = PoolVector<char>::Write(); + w.release(); alloc_try *= 2; } else { @@ -273,6 +273,7 @@ void AudioStreamOGGVorbis::_bind_methods() { AudioStreamOGGVorbis::AudioStreamOGGVorbis() { data = NULL; + data_len = 0; length = 0; sample_rate = 1; channels = 1; diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index 8b42111847..c8c179554a 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ class AudioStreamOGGVorbis; class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { - GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled) + GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled); stb_vorbis *ogg_stream; stb_vorbis_alloc ogg_alloc; @@ -72,7 +72,7 @@ public: class AudioStreamOGGVorbis : public AudioStream { - GDCLASS(AudioStreamOGGVorbis, AudioStream) + GDCLASS(AudioStreamOGGVorbis, AudioStream); OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged RES_BASE_EXTENSION("oggstr"); diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py index d75e41797a..200b8dfd50 100644 --- a/modules/stb_vorbis/config.py +++ b/modules/stb_vorbis/config.py @@ -7,7 +7,6 @@ def configure(env): def get_doc_classes(): return [ "AudioStreamOGGVorbis", - "ResourceImporterOGGVorbis", ] def get_doc_path(): diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index e2281babf7..9403199398 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.1"> +<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.2"> <brief_description> OGG Vorbis audio stream driver. </brief_description> @@ -8,17 +8,15 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="data" type="PoolByteArray" setter="set_data" getter="get_data"> + <member name="data" type="PoolByteArray" setter="set_data" getter="get_data" default="PoolByteArray( )"> Contains the audio data in bytes. </member> - <member name="loop" type="bool" setter="set_loop" getter="has_loop"> + <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> </member> - <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset"> + <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0"> </member> </members> <constants> diff --git a/modules/stb_vorbis/register_types.cpp b/modules/stb_vorbis/register_types.cpp index 514ef4c071..ce64626e4b 100644 --- a/modules/stb_vorbis/register_types.cpp +++ b/modules/stb_vorbis/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,15 +29,22 @@ /*************************************************************************/ #include "register_types.h" + #include "audio_stream_ogg_vorbis.h" + +#ifdef TOOLS_ENABLED +#include "core/engine.h" #include "resource_importer_ogg_vorbis.h" +#endif void register_stb_vorbis_types() { #ifdef TOOLS_ENABLED - Ref<ResourceImporterOGGVorbis> ogg_import; - ogg_import.instance(); - ResourceFormatImporter::get_singleton()->add_importer(ogg_import); + if (Engine::get_singleton()->is_editor_hint()) { + Ref<ResourceImporterOGGVorbis> ogg_import; + ogg_import.instance(); + ResourceFormatImporter::get_singleton()->add_importer(ogg_import); + } #endif ClassDB::register_class<AudioStreamOGGVorbis>(); } diff --git a/modules/stb_vorbis/register_types.h b/modules/stb_vorbis/register_types.h index 142be0efdf..736c15e3d8 100644 --- a/modules/stb_vorbis/register_types.h +++ b/modules/stb_vorbis/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index 74f2e68206..e10f29e310 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,15 +76,14 @@ void ResourceImporterOGGVorbis::get_import_options(List<ImportOption> *r_options r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "loop_offset"), 0)); } -Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { +Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { bool loop = p_options["loop"]; float loop_offset = p_options["loop_offset"]; FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); - if (!f) { - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); - } + + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); size_t len = f->get_len(); diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/stb_vorbis/resource_importer_ogg_vorbis.h index 82a03cd207..8e4fbde916 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,10 +32,11 @@ #define RESOURCEIMPORTEROGGVORBIS_H #include "audio_stream_ogg_vorbis.h" -#include "core/io/resource_import.h" +#include "core/io/resource_importer.h" class ResourceImporterOGGVorbis : public ResourceImporter { - GDCLASS(ResourceImporterOGGVorbis, ResourceImporter) + GDCLASS(ResourceImporterOGGVorbis, ResourceImporter); + public: virtual String get_importer_name() const; virtual String get_visible_name() const; @@ -49,7 +50,7 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); ResourceImporterOGGVorbis(); }; diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 22f0b1e3eb..9324c1634b 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -12,11 +12,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_svg.Append(CPPPATH=[thirdparty_dir]) +env_svg.Prepend(CPPPATH=[thirdparty_dir]) # FIXME: Needed in editor/editor_themes.cpp for now, but ideally there # shouldn't be a dependency on modules/ and its own 3rd party deps. -env.Append(CPPPATH=[thirdparty_dir]) -env.Append(CCFLAGS=["-DSVG_ENABLED"]) +env.Prepend(CPPPATH=[thirdparty_dir]) +env.Append(CPPDEFINES=["SVG_ENABLED"]) env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index ccb7d93885..b0cd648734 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -109,7 +109,12 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t float upscale = upsample ? 2.0 : 1.0; int w = (int)(svg_image->width * p_scale * upscale); + ERR_EXPLAIN(vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max width.", rtos(p_scale))); + ERR_FAIL_COND_V(w > Image::MAX_WIDTH, ERR_PARAMETER_RANGE_ERROR); + int h = (int)(svg_image->height * p_scale * upscale); + ERR_EXPLAIN(vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max height.", rtos(p_scale))); + ERR_FAIL_COND_V(h > Image::MAX_HEIGHT, ERR_PARAMETER_RANGE_ERROR); PoolVector<uint8_t> dst_image; dst_image.resize(w * h * 4); @@ -118,7 +123,7 @@ Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const PoolVector<uint8_t rasterizer.rasterize(svg_image, 0, 0, p_scale * upscale, (unsigned char *)dw.ptr(), w, h, w * 4); - dw = PoolVector<uint8_t>::Write(); + dw.release(); p_image->create(w, h, false, Image::FORMAT_RGBA8, dst_image); if (upsample) p_image->shrink_x2(); diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index ff361ad800..2116079aef 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp index 56426662cd..5707c29f60 100644 --- a/modules/svg/register_types.cpp +++ b/modules/svg/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/svg/register_types.h b/modules/svg/register_types.h index 015c586c6b..13eb12d2d1 100644 --- a/modules/svg/register_types.h +++ b/modules/svg/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 9bc24017fc..6ee408d472 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -148,9 +148,11 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff uint8_t a = 0xff; if (p_header.color_map_depth == 24) { - r = (p_palette[(index * 3) + 0]); + // Due to low-high byte order, the color table must be + // read in the same order as image data (little endian) + r = (p_palette[(index * 3) + 2]); g = (p_palette[(index * 3) + 1]); - b = (p_palette[(index * 3) + 2]); + b = (p_palette[(index * 3) + 0]); } else { return ERR_INVALID_DATA; } @@ -197,7 +199,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } } - image_data_w = PoolVector<uint8_t>::Write(); + image_data_w.release(); p_image->create(width, height, 0, Image::FORMAT_RGBA8, image_data); diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index 0fe83a54a1..d466ac6e38 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp index ac1b56af16..d21c0a51d0 100644 --- a/modules/tga/register_types.cpp +++ b/modules/tga/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tga/register_types.h b/modules/tga/register_types.h index 6483f2576e..445a7f3e31 100644 --- a/modules/tga/register_types.h +++ b/modules/tga/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub deleted file mode 100644 index c47c760d5f..0000000000 --- a/modules/thekla_unwrap/SCsub +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -import platform - -Import('env') -Import('env_modules') - -env_thekla_unwrap = env_modules.Clone() - -# Thirdparty source files -if env['builtin_thekla_atlas']: - thirdparty_dir = "#thirdparty/thekla_atlas/" - thirdparty_sources = [ - "nvcore/Memory.cpp", - "nvcore/Debug.cpp", - "nvcore/StrLib.cpp", - "nvcore/FileSystem.cpp", - "nvcore/RadixSort.cpp", - "nvmath/Basis.cpp", - "nvmath/ConvexHull.cpp", - "nvmath/Fitting.cpp", - "nvmath/Plane.cpp", - "nvmath/ProximityGrid.cpp", - "nvmath/Random.cpp", - "nvmath/Solver.cpp", - "nvmath/Sparse.cpp", - "nvmath/TypeSerialization.cpp", - "poshlib/posh.c", - "nvimage/BitMap.cpp", - "nvimage/Image.cpp", - "nvmesh/BaseMesh.cpp", - "nvmesh/MeshBuilder.cpp", - "nvmesh/TriMesh.cpp", - "nvmesh/QuadTriMesh.cpp", - "nvmesh/MeshTopology.cpp", - "nvmesh/halfedge/Edge.cpp", - "nvmesh/halfedge/Mesh.cpp", - "nvmesh/halfedge/Face.cpp", - "nvmesh/halfedge/Vertex.cpp", - "nvmesh/geometry/Bounds.cpp", - "nvmesh/geometry/Measurements.cpp", - "nvmesh/raster/Raster.cpp", - "nvmesh/param/Atlas.cpp", - "nvmesh/param/AtlasBuilder.cpp", - "nvmesh/param/AtlasPacker.cpp", - "nvmesh/param/LeastSquaresConformalMap.cpp", - "nvmesh/param/OrthogonalProjectionMap.cpp", - "nvmesh/param/ParameterizationQuality.cpp", - "nvmesh/param/SingleFaceMap.cpp", - "nvmesh/param/Util.cpp", - "nvmesh/weld/VertexWeld.cpp", - "nvmesh/weld/Snap.cpp", - "thekla/thekla_atlas.cpp" - ] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - - env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"]) - - # upstream uses c++11 - if (not env.msvc): - env_thekla_unwrap.Append(CXXFLAGS="-std=c++11") - - if env["platform"] == 'x11': - # if not specifically one of the *BSD, then use LINUX as default - if platform.system() == "FreeBSD": - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) - elif platform.system() == "OpenBSD": - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) - else: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'osx': - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'windows': - if env.msvc: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) - else: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) - env.Append(LIBS=["dbghelp"]) - - env_thirdparty = env_thekla_unwrap.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) - -# Godot source files -env_thekla_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py deleted file mode 100644 index fad6095064..0000000000 --- a/modules/thekla_unwrap/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - #return (env['tools'] and platform not in ["android", "ios"]) - return False - -def configure(env): - pass diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp deleted file mode 100644 index 8e733d1ad2..0000000000 --- a/modules/thekla_unwrap/register_types.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "register_types.h" -#include "core/error_macros.h" -#include "thirdparty/thekla_atlas/thekla/thekla_atlas.h" - -#include <stdio.h> -#include <stdlib.h> -extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); - -bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { - - //set up input mesh - Thekla::Atlas_Input_Mesh input_mesh; - input_mesh.face_array = new Thekla::Atlas_Input_Face[p_index_count / 3]; - for (int i = 0; i < p_index_count / 3; i++) { - input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0]; - input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1]; - input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2]; - //printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]); - input_mesh.face_array[i].material_index = p_face_materials[i]; - } - input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count]; - for (int i = 0; i < p_vertex_count; i++) { - input_mesh.vertex_array[i].first_colocal = i; //wtf - for (int j = 0; j < 3; j++) { - input_mesh.vertex_array[i].position[j] = p_vertices[i * 3 + j]; - input_mesh.vertex_array[i].normal[j] = p_normals[i * 3 + j]; - } - input_mesh.vertex_array[i].uv[0] = 0; - input_mesh.vertex_array[i].uv[1] = 0; - //printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]); - //printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]); - } - input_mesh.face_count = p_index_count / 3; - input_mesh.vertex_count = p_vertex_count; - - //set up options - Thekla::Atlas_Options options; - Thekla::atlas_set_default_options(&options); - options.packer_options.witness.packing_quality = 1; - options.packer_options.witness.texel_area = 1.0 / p_texel_size; - options.packer_options.witness.conservative = false; - - //generate - Thekla::Atlas_Error err; - Thekla::Atlas_Output_Mesh *output = atlas_generate(&input_mesh, &options, &err); - - delete[] input_mesh.face_array; - delete[] input_mesh.vertex_array; - - if (output == NULL) { - ERR_PRINT("could not generate atlas output mesh"); - return false; - } - - if (err != Thekla::Atlas_Error_Success) { - printf("error with atlas\n"); - } else { - *r_vertex = (int *)malloc(sizeof(int) * output->vertex_count); - *r_uv = (float *)malloc(sizeof(float) * output->vertex_count * 3); - *r_index = (int *)malloc(sizeof(int) * output->index_count); - - // printf("w: %i, h: %i\n", output->atlas_width, output->atlas_height); - for (int i = 0; i < output->vertex_count; i++) { - (*r_vertex)[i] = output->vertex_array[i].xref; - (*r_uv)[i * 2 + 0] = output->vertex_array[i].uv[0] / output->atlas_width; - (*r_uv)[i * 2 + 1] = output->vertex_array[i].uv[1] / output->atlas_height; - // printf("uv: %f,%f\n", (*r_uv)[i * 2 + 0], (*r_uv)[i * 2 + 1]); - } - *r_vertex_count = output->vertex_count; - - for (int i = 0; i < output->index_count; i++) { - (*r_index)[i] = output->index_array[i]; - } - - *r_index_count = output->index_count; - - *r_size_hint_x = output->atlas_height; - *r_size_hint_y = output->atlas_width; - } - - if (output) { - atlas_free(output); - } - - return err == Thekla::Atlas_Error_Success; -} - -void register_thekla_unwrap_types() { - - array_mesh_lightmap_unwrap_callback = thekla_mesh_lightmap_unwrap_callback; -} - -void unregister_thekla_unwrap_types() { -} diff --git a/modules/theora/SCsub b/modules/theora/SCsub index 98c4274a7e..ff65d2f8ec 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -66,17 +66,17 @@ if env['builtin_libtheora']: thirdparty_sources += thirdparty_sources_x86_vc if (env["x86_libtheora_opt_gcc"] or env["x86_libtheora_opt_vc"]): - env_theora.Append(CCFLAGS=["-DOC_X86_ASM"]) + env_theora.Append(CPPDEFINES=["OC_X86_ASM"]) thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_theora.Append(CPPPATH=[thirdparty_dir]) + env_theora.Prepend(CPPPATH=[thirdparty_dir]) # also requires libogg and libvorbis if env['builtin_libogg']: - env_theora.Append(CPPPATH=["#thirdparty/libogg"]) + env_theora.Prepend(CPPPATH=["#thirdparty/libogg"]) if env['builtin_libvorbis']: - env_theora.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_theora.Prepend(CPPPATH=["#thirdparty/libvorbis"]) env_thirdparty = env_theora.Clone() env_thirdparty.disable_warnings() diff --git a/modules/theora/config.py b/modules/theora/config.py index 7504166237..c7713d7607 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -6,7 +6,6 @@ def configure(env): def get_doc_classes(): return [ - "ResourceImporterTheora", "VideoStreamTheora", ] diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index e7c4727332..696101e252 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.1"> +<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp index 971fe39c44..4b50c3f146 100644 --- a/modules/theora/register_types.cpp +++ b/modules/theora/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,11 @@ #include "video_stream_theora.h" -static ResourceFormatLoaderTheora *resource_loader_theora = NULL; +static Ref<ResourceFormatLoaderTheora> resource_loader_theora; void register_theora_types() { - resource_loader_theora = memnew(ResourceFormatLoaderTheora); + resource_loader_theora.instance(); ResourceLoader::add_resource_format_loader(resource_loader_theora, true); ClassDB::register_class<VideoStreamTheora>(); @@ -44,7 +44,6 @@ void register_theora_types() { void unregister_theora_types() { - if (resource_loader_theora) { - memdelete(resource_loader_theora); - } + ResourceLoader::remove_resource_format_loader(resource_loader_theora); + resource_loader_theora.unref(); } diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h index 4a81d7743b..e1fd39f679 100644 --- a/modules/theora/register_types.h +++ b/modules/theora/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 2a6bb0783b..ae542713ea 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -94,15 +94,15 @@ void VideoStreamPlaybackTheora::video_write(void) { if (px_fmt == TH_PF_444) { - yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); } else if (px_fmt == TH_PF_422) { - yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); } else if (px_fmt == TH_PF_420) { - yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[2].data, (uint8_t *)yuv[1].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); }; format = Image::FORMAT_RGBA8; @@ -296,8 +296,8 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { if (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og); /* demux into the appropriate stream */ } else { - int ret = buffer_data(); /* someone needs more data */ - if (ret == 0) { + int ret2 = buffer_data(); /* someone needs more data */ + if (ret2 == 0) { fprintf(stderr, "End of file while searching for codec headers.\n"); clear(); return; @@ -365,7 +365,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { float VideoStreamPlaybackTheora::get_time() const { - return time - AudioServer::get_singleton()->get_output_delay() - delay_compensation; //-((get_total())/(float)vi.rate); + return time - AudioServer::get_singleton()->get_output_latency() - delay_compensation; //-((get_total())/(float)vi.rate); }; Ref<Texture> VideoStreamPlaybackTheora::get_texture() { diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 4be723f85b..0c37d33358 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub index 3e7bda2bca..97f9797b58 100644 --- a/modules/tinyexr/SCsub +++ b/modules/tinyexr/SCsub @@ -13,7 +13,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_tinyexr.Append(CPPPATH=[thirdparty_dir]) +env_tinyexr.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_tinyexr.Clone() env_thirdparty.disable_warnings() diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 63f0781028..74a584821a 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -107,22 +107,31 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f } } - if (idxR == -1) { - ERR_PRINT("TinyEXR: R channel not found."); - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } + if (exr_header.num_channels == 1) { + // Grayscale channel only. + idxR = 0; + idxG = 0; + idxB = 0; + idxA = 0; + } else { + // Assume RGB(A) + if (idxR == -1) { + ERR_PRINT("TinyEXR: R channel not found."); + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } - if (idxG == -1) { - ERR_PRINT("TinyEXR: G channel not found.") - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } + if (idxG == -1) { + ERR_PRINT("TinyEXR: G channel not found."); + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } - if (idxB == -1) { - ERR_PRINT("TinyEXR: B channel not found.") - // @todo { free exr_image } - return ERR_FILE_CORRUPT; + if (idxB == -1) { + ERR_PRINT("TinyEXR: B channel not found."); + // @todo { free exr_image } + return ERR_FILE_CORRUPT; + } } // EXR image data loaded, now parse it into Godot-friendly image data @@ -131,7 +140,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f Image::Format format; int output_channels = 0; - if (idxA > 0) { + if (idxA != -1) { imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16 format = Image::FORMAT_RGBAH; @@ -187,7 +196,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]); const float *a_channel_start = NULL; - if (idxA > 0) { + if (idxA != -1) { a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]); } @@ -216,7 +225,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f *row_w++ = Math::make_half_float(color.g); *row_w++ = Math::make_half_float(color.b); - if (idxA > 0) { + if (idxA != -1) { *row_w++ = Math::make_half_float(*a_channel++); } } @@ -226,7 +235,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f p_image->create(exr_image.width, exr_image.height, false, format, imgdata); - w = PoolVector<uint8_t>::Write(); + w.release(); FreeEXRHeader(&exr_header); FreeEXRImage(&exr_image); diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index a6ef9000e5..4003fdc802 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp index 61217805cf..5473a55687 100644 --- a/modules/tinyexr/register_types.cpp +++ b/modules/tinyexr/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/tinyexr/register_types.h b/modules/tinyexr/register_types.h index 70cd0bfbf8..4c830674f8 100644 --- a/modules/tinyexr/register_types.h +++ b/modules/tinyexr/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub index 2b15f7aee2..3f56a69594 100644 --- a/modules/upnp/SCsub +++ b/modules/upnp/SCsub @@ -23,10 +23,11 @@ if env['builtin_miniupnpc']: "portlistingparse.c", "upnpreplyparse.c", ] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + thirdparty_sources = [thirdparty_dir + "miniupnpc/" + file for file in thirdparty_sources] - env_upnp.Append(CPPPATH=[thirdparty_dir]) - env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"]) + env_upnp.Prepend(CPPPATH=[thirdparty_dir]) + env_upnp.Append(CPPDEFINES=["MINIUPNP_STATICLIB"]) + env_upnp.Append(CPPDEFINES=["MINIUPNPC_SET_SOCKET_TIMEOUT"]) env_thirdparty = env_upnp.Clone() env_thirdparty.disable_warnings() diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index b98327c60d..aac0932995 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -1,15 +1,24 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="UPNP" inherits="Reference" category="Core" version="3.1"> +<class name="UPNP" inherits="Reference" category="Core" version="3.2"> <brief_description> UPNP network functions. </brief_description> <description> Provides UPNP functionality to discover [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (port forwarding) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread. + To forward a specific port: + [codeblock] + const PORT = 7777 + var upnp = UPNP.new() + upnp.discover(2000, 2, "InternetGatewayDevice") + upnp.add_port_mapping(port) + [/codeblock] + To close a specific port (e.g. after you have finished using it): + [codeblock] + upnp.delete_port_mapping(port) + [/codeblock] </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_device"> <return type="void"> @@ -125,13 +134,13 @@ </method> </methods> <members> - <member name="discover_ipv6" type="bool" setter="set_discover_ipv6" getter="is_discover_ipv6"> + <member name="discover_ipv6" type="bool" setter="set_discover_ipv6" getter="is_discover_ipv6" default="false"> If [code]true[/code], IPv6 is used for [UPNPDevice] discovery. </member> - <member name="discover_local_port" type="int" setter="set_discover_local_port" getter="get_discover_local_port"> + <member name="discover_local_port" type="int" setter="set_discover_local_port" getter="get_discover_local_port" default="0"> If [code]0[/code], the local port to use for discovery is chosen automatically by the system. If [code]1[/code], discovery will be done from the source port 1900 (same as destination port). Otherwise, the value will be used as the port. </member> - <member name="discover_multicast_if" type="String" setter="set_discover_multicast_if" getter="get_discover_multicast_if"> + <member name="discover_multicast_if" type="String" setter="set_discover_multicast_if" getter="get_discover_multicast_if" default=""""> Multicast interface to use for discovery. Uses the default multicast interface if empty. </member> </members> @@ -173,7 +182,7 @@ No port maps are available. May also be returned if port mapping functionality is not available. </constant> <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM" value="12" enum="UPNPResult"> - Conflict with other mechanism. May be returned instead of [code]UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING[/code] if a port mapping conflicts with an existing one. + Conflict with other mechanism. May be returned instead of [constant UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING] if a port mapping conflicts with an existing one. </constant> <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING" value="13" enum="UPNPResult"> Conflict with an existing port mapping. diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index 9de8042daf..4d3a0f4f1d 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="UPNPDevice" inherits="Reference" category="Core" version="3.1"> +<class name="UPNPDevice" inherits="Reference" category="Core" version="3.2"> <brief_description> UPNP device. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_port_mapping" qualifiers="const"> <return type="int"> @@ -55,22 +53,22 @@ </method> </methods> <members> - <member name="description_url" type="String" setter="set_description_url" getter="get_description_url"> + <member name="description_url" type="String" setter="set_description_url" getter="get_description_url" default=""""> URL to the device description. </member> - <member name="igd_control_url" type="String" setter="set_igd_control_url" getter="get_igd_control_url"> + <member name="igd_control_url" type="String" setter="set_igd_control_url" getter="get_igd_control_url" default=""""> IDG control URL. </member> - <member name="igd_our_addr" type="String" setter="set_igd_our_addr" getter="get_igd_our_addr"> + <member name="igd_our_addr" type="String" setter="set_igd_our_addr" getter="get_igd_our_addr" default=""""> Address of the local machine in the network connecting it to this [UPNPDevice]. </member> - <member name="igd_service_type" type="String" setter="set_igd_service_type" getter="get_igd_service_type"> + <member name="igd_service_type" type="String" setter="set_igd_service_type" getter="get_igd_service_type" default=""""> IGD service type. </member> - <member name="igd_status" type="int" setter="set_igd_status" getter="get_igd_status" enum="UPNPDevice.IGDStatus"> + <member name="igd_status" type="int" setter="set_igd_status" getter="get_igd_status" enum="UPNPDevice.IGDStatus" default="9"> IGD status. See [enum IGDStatus]. </member> - <member name="service_type" type="String" setter="set_service_type" getter="get_service_type"> + <member name="service_type" type="String" setter="set_service_type" getter="get_service_type" default=""""> Service type. </member> </members> diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp index 13088c94dd..fbf0ee8b97 100644 --- a/modules/upnp/register_types.cpp +++ b/modules/upnp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #include "core/error_macros.h" #include "upnp.h" -#include "upnpdevice.h" +#include "upnp_device.h" void register_upnp_types() { diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h index 2aeb06abc7..3079c26580 100644 --- a/modules/upnp/register_types.h +++ b/modules/upnp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 69e054f5c5..05b96d8a30 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h index 8f0a972c22..011b6d44b3 100644 --- a/modules/upnp/upnp.h +++ b/modules/upnp/upnp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,7 @@ #include "core/reference.h" -#include "upnpdevice.h" +#include "upnp_device.h" #include <miniupnpc/miniupnpc.h> diff --git a/modules/upnp/upnpdevice.cpp b/modules/upnp/upnp_device.cpp index 8f935fbd82..4d67e3ddc8 100644 --- a/modules/upnp/upnpdevice.cpp +++ b/modules/upnp/upnp_device.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* upnpdevice.cpp */ +/* upnp_device.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "upnpdevice.h" +#include "upnp_device.h" #include "upnp.h" diff --git a/modules/upnp/upnpdevice.h b/modules/upnp/upnp_device.h index e4c5eb691d..09fce6af89 100644 --- a/modules/upnp/upnpdevice.h +++ b/modules/upnp/upnp_device.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* upnpdevice.h */ +/* upnp_device.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GODOT_UPNPDEVICE_H -#define GODOT_UPNPDEVICE_H +#ifndef GODOT_UPNP_DEVICE_H +#define GODOT_UPNP_DEVICE_H #include "core/reference.h" @@ -92,4 +92,4 @@ private: VARIANT_ENUM_CAST(UPNPDevice::IGDStatus) -#endif // GODOT_UPNPDEVICE_H +#endif // GODOT_UPNP_DEVICE_H diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub new file mode 100644 index 0000000000..e581fb7bb2 --- /dev/null +++ b/modules/vhacd/SCsub @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_vhacd = env_modules.Clone() + +# Thirdparty source files + +thirdparty_dir = "#thirdparty/vhacd/" + +thirdparty_sources = [ + "src/vhacdManifoldMesh.cpp", + "src/FloatMath.cpp", + "src/vhacdMesh.cpp", + "src/vhacdICHull.cpp", + "src/vhacdVolume.cpp", + "src/VHACD-ASYNC.cpp", + "src/btAlignedAllocator.cpp", + "src/vhacdRaycastMesh.cpp", + "src/VHACD.cpp", + "src/btConvexHullComputer.cpp" +] + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "/inc"]) + +# upstream uses c++11 +if not env.msvc: + env_vhacd.Append(CXXFLAGS="-std=c++11") + +env_thirdparty = env_vhacd.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +env_vhacd.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/vhacd/config.py b/modules/vhacd/config.py new file mode 100644 index 0000000000..9ced70d2fb --- /dev/null +++ b/modules/vhacd/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp new file mode 100644 index 0000000000..076a1738ab --- /dev/null +++ b/modules/vhacd/register_types.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" +#include "scene/resources/mesh.h" +#include "thirdparty/vhacd/public/VHACD.h" + +static Vector<Vector<Face3> > convex_decompose(const Vector<Face3> &p_faces) { + + Vector<float> vertices; + vertices.resize(p_faces.size() * 9); + Vector<uint32_t> indices; + indices.resize(p_faces.size() * 3); + + for (int i = 0; i < p_faces.size(); i++) { + for (int j = 0; j < 3; j++) { + vertices.write[i * 9 + j * 3 + 0] = p_faces[i].vertex[j].x; + vertices.write[i * 9 + j * 3 + 1] = p_faces[i].vertex[j].y; + vertices.write[i * 9 + j * 3 + 2] = p_faces[i].vertex[j].z; + indices.write[i * 3 + j] = i * 3 + j; + } + } + + VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); + VHACD::IVHACD::Parameters params; + decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params); + + int hull_count = decomposer->GetNConvexHulls(); + + Vector<Vector<Face3> > ret; + + for (int i = 0; i < hull_count; i++) { + Vector<Face3> triangles; + VHACD::IVHACD::ConvexHull hull; + decomposer->GetConvexHull(i, hull); + triangles.resize(hull.m_nTriangles); + for (uint32_t j = 0; j < hull.m_nTriangles; j++) { + Face3 f; + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 3; l++) { + f.vertex[k][l] = hull.m_points[hull.m_triangles[j * 3 + k] * 3 + l]; + } + } + triangles.write[j] = f; + } + ret.push_back(triangles); + } + + decomposer->Clean(); + decomposer->Release(); + + return ret; +} + +void register_vhacd_types() { + Mesh::convex_composition_function = convex_decompose; +} + +void unregister_vhacd_types() { + Mesh::convex_composition_function = NULL; +} diff --git a/modules/vhacd/register_types.h b/modules/vhacd/register_types.h new file mode 100644 index 0000000000..cb948faf44 --- /dev/null +++ b/modules/vhacd/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_vhacd_types(); +void unregister_vhacd_types(); diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index 07a0450734..04e1a40b81 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@VisualScript", "VisualScriptBasicTypeConstant", "VisualScriptBuiltinFunc", "VisualScriptClassConstant", diff --git a/modules/visual_script/doc_classes/@VisualScript.xml b/modules/visual_script/doc_classes/@VisualScript.xml new file mode 100644 index 0000000000..8d9408e6d4 --- /dev/null +++ b/modules/visual_script/doc_classes/@VisualScript.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="@VisualScript" category="Core" version="3.2"> + <brief_description> + Built-in visual script functions. + </brief_description> + <description> + A list of built-in visual script functions, see [VisualScriptBuiltinFunc] and [VisualScript]. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 70849c5a80..0d95075152 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScript" inherits="Script" category="Core" version="3.1"> +<class name="VisualScript" inherits="Script" category="Core" version="3.2"> <brief_description> A script implemented in the Visual Script programming environment. </brief_description> @@ -11,8 +11,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_signal"> <return type="void"> @@ -254,7 +252,7 @@ <argument index="0" name="name" type="String"> </argument> <description> - Returns the info for a given variable as a dictionary. The information includes its name, type, hint and usage. + Returns the information for a given variable as a dictionary. The information includes its name, type, hint and usage. </description> </method> <method name="has_custom_signal" qualifiers="const"> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index 793291eca2..6dc54dbc03 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -1,19 +1,17 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node representing a constant from the base types. </brief_description> <description> - A Visual Script node representing a constant from base types, such as [Vector3.AXIS_X]. + A Visual Script node representing a constant from base types, such as [constant Vector3.AXIS_X]. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type"> + <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type" default="0"> The type to get the constant from. </member> <member name="constant" type="String" setter="set_basic_type_constant" getter="get_basic_type_constant"> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 399ba8ef5d..470a3a5e35 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node used to call built-in functions. </brief_description> @@ -9,12 +9,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="function" type="int" setter="set_func" getter="get_func" enum="VisualScriptBuiltinFunc.BuiltinFunc"> + <member name="function" type="int" setter="set_func" getter="get_func" enum="VisualScriptBuiltinFunc.BuiltinFunc" default="0"> The function to be executed. </member> </members> @@ -104,108 +102,119 @@ </constant> <constant name="MATH_RANGE_LERP" value="28" enum="BuiltinFunc"> </constant> - <constant name="MATH_DECTIME" value="29" enum="BuiltinFunc"> - Return the result of 'value' decreased by 'step' * 'amount'. + <constant name="MATH_MOVE_TOWARD" value="29" enum="BuiltinFunc"> + Moves the number toward a value, based on the third input. </constant> - <constant name="MATH_RANDOMIZE" value="30" enum="BuiltinFunc"> + <constant name="MATH_DECTIME" value="30" enum="BuiltinFunc"> + Return the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code]. + </constant> + <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc"> Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. </constant> - <constant name="MATH_RAND" value="31" enum="BuiltinFunc"> + <constant name="MATH_RAND" value="32" enum="BuiltinFunc"> Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function. </constant> - <constant name="MATH_RANDF" value="32" enum="BuiltinFunc"> + <constant name="MATH_RANDF" value="33" enum="BuiltinFunc"> Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication. </constant> - <constant name="MATH_RANDOM" value="33" enum="BuiltinFunc"> + <constant name="MATH_RANDOM" value="34" enum="BuiltinFunc"> Return a random floating-point value between the two inputs. </constant> - <constant name="MATH_SEED" value="34" enum="BuiltinFunc"> + <constant name="MATH_SEED" value="35" enum="BuiltinFunc"> Set the seed for the random number generator. </constant> - <constant name="MATH_RANDSEED" value="35" enum="BuiltinFunc"> + <constant name="MATH_RANDSEED" value="36" enum="BuiltinFunc"> Return a random value from the given seed, along with the new seed. </constant> - <constant name="MATH_DEG2RAD" value="36" enum="BuiltinFunc"> + <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc"> Convert the input from degrees to radians. </constant> - <constant name="MATH_RAD2DEG" value="37" enum="BuiltinFunc"> + <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc"> Convert the input from radians to degrees. </constant> - <constant name="MATH_LINEAR2DB" value="38" enum="BuiltinFunc"> + <constant name="MATH_LINEAR2DB" value="39" enum="BuiltinFunc"> Convert the input from linear volume to decibel volume. </constant> - <constant name="MATH_DB2LINEAR" value="39" enum="BuiltinFunc"> + <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_POLAR2CARTESIAN" value="40" enum="BuiltinFunc"> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (x and y axis). + <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc"> + Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). </constant> - <constant name="MATH_CARTESIAN2POLAR" value="41" enum="BuiltinFunc"> - Converts a 2D point expressed in the cartesian coordinate system (x and y axis) to the polar coordinate system (a distance from the origin and an angle). + <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc"> + Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). </constant> - <constant name="MATH_WRAP" value="42" enum="BuiltinFunc"> + <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAPF" value="43" enum="BuiltinFunc"> + <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> </constant> - <constant name="LOGIC_MAX" value="44" enum="BuiltinFunc"> + <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="45" enum="BuiltinFunc"> + <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="46" enum="BuiltinFunc"> - Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to `min(max(input, range_low), range_high)` + <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc"> + Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code]. </constant> - <constant name="LOGIC_NEAREST_PO2" value="47" enum="BuiltinFunc"> + <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="48" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="FUNC_FUNCREF" value="49" enum="BuiltinFunc"> + <constant name="FUNC_FUNCREF" value="50" enum="BuiltinFunc"> Create a [FuncRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="51" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="52" enum="BuiltinFunc"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="53" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="55" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="59" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> - Deserialize a [Variant] from a string serialized using [VAR_TO_STR]. + <constant name="STR_TO_VAR" value="60" enum="BuiltinFunc"> + Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="61" enum="BuiltinFunc"> Serialize a [Variant] to a [PoolByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> - Deserialize a [Variant] from a [PoolByteArray] serialized using [VAR_TO_BYTES]. + <constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc"> + Deserialize a [Variant] from a [PoolByteArray] serialized using [constant VAR_TO_BYTES]. + </constant> + <constant name="COLORN" value="63" enum="BuiltinFunc"> + Return the [Color] with the given name and alpha ranging from 0 to 1 + [b]Note:[/b] Names are defined in [code]color_names.inc[/code]. </constant> - <constant name="COLORN" value="62" enum="BuiltinFunc"> - Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc. + <constant name="MATH_SMOOTHSTEP" value="64" enum="BuiltinFunc"> + Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: + [codeblock] + var t = clamp((weight - from) / (to - from), 0.0, 1.0) + return t * t * (3.0 - 2.0 * t) + [/codeblock] </constant> - <constant name="FUNC_MAX" value="63" enum="BuiltinFunc"> - The maximum value the [member function] property can have. + <constant name="FUNC_MAX" value="65" enum="BuiltinFunc"> + Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> </class> diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index 2e3a5b33c0..49ea7850ef 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Gets a constant from a given class. </brief_description> <description> - This node returns a constant from a given class, such as [@GlobalScope.TYPE_INT]. See the given class' documentation for available constants. + This node returns a constant from a given class, such as [constant TYPE_INT]. See the given class' documentation for available constants. [b]Input Ports:[/b] none [b]Output Ports:[/b] @@ -12,15 +12,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> The constant's parent class. </member> - <member name="constant" type="String" setter="set_class_constant" getter="get_class_constant"> + <member name="constant" type="String" setter="set_class_constant" getter="get_class_constant" default=""""> The constant to return. See the given class for its available constants. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index a4a890ea8a..2538bc8356 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node used to annotate the script. </brief_description> @@ -9,18 +9,16 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="description" type="String" setter="set_description" getter="get_description"> + <member name="description" type="String" setter="set_description" getter="get_description" default=""""> The text inside the comment node. </member> - <member name="size" type="Vector2" setter="set_size" getter="get_size"> + <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2( 150, 150 )"> The comment node's size (in pixels). </member> - <member name="title" type="String" setter="set_title" getter="get_title"> + <member name="title" type="String" setter="set_title" getter="get_title" default=""Comment""> The comment node's title. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index 353898c93a..12d85429cf 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node which branches the flow. </brief_description> <description> - A Visual Script node that checks a [bool] input port. If [code]true[/code] it will exit via the “true” sequence port. If [code]false[/code] it will exit via the "false" sequence port. After exiting either, it exits via the “done” port. Sequence ports may be left disconnected. + A Visual Script node that checks a [bool] input port. If [code]true[/code], it will exit via the "true" sequence port. If [code]false[/code], it will exit via the "false" sequence port. After exiting either, it exits via the "done" port. Sequence ports may be left disconnected. [b]Input Ports:[/b] - Sequence: [code]if (cond) is[/code] - Data (boolean): [code]cond[/code] @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index ed633c4135..eb12fc3731 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Gets a contant's value. </brief_description> @@ -12,12 +12,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="type" type="int" setter="set_constant_type" getter="get_constant_type" enum="Variant.Type"> + <member name="type" type="int" setter="set_constant_type" getter="get_constant_type" enum="Variant.Type" default="0"> The constant's type. </member> <member name="value" type="Variant" setter="set_constant_value" getter="get_constant_value"> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index 14c44c6970..8da5055d83 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node which calls a base type constructor. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_constructor" qualifiers="const"> <return type="Dictionary"> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index ff3ed66e81..b079653591 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A scripted Visual Script node. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_get_caption" qualifiers="virtual"> <return type="String"> @@ -47,7 +45,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Return the specified input port's type. See the TYPE_* enum in [@GlobalScope]. + Return the specified input port's type. See the [code]TYPE_*[/code] enum in [@GlobalScope]. </description> </method> <method name="_get_output_sequence_port_count" qualifiers="virtual"> @@ -88,7 +86,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Return the specified output's type. See the TYPE_* enum in [@GlobalScope]. + Return the specified output's type. See the [code]TYPE_*[/code] enum in [@GlobalScope]. </description> </method> <method name="_get_text" qualifiers="virtual"> @@ -127,9 +125,9 @@ Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error. The [code]inputs[/code] array contains the values of the input ports. [code]outputs[/code] is an array whose indices should be set to the respective outputs. - The [code]start_mode[/code] is usually [code]START_MODE_BEGIN_SEQUENCE[/code], unless you have used the STEP_* constants. + The [code]start_mode[/code] is usually [constant START_MODE_BEGIN_SEQUENCE], unless you have used the [code]STEP_*[/code] constants. [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. - When returning, you can mask the returned value with one of the STEP_* constants. + When returning, you can mask the returned value with one of the [code]STEP_*[/code] constants. </description> </method> </methods> @@ -145,7 +143,7 @@ </constant> <constant name="STEP_PUSH_STACK_BIT" value="16777216"> Hint used by [method _step] to tell that control should return to it when there is no other node left to execute. - This is used by [VisualScriptCondition] to redirect the sequence to the "Done" port after the true/false branch has finished execution. + This is used by [VisualScriptCondition] to redirect the sequence to the "Done" port after the [code]true[/code]/[code]false[/code] branch has finished execution. </constant> <constant name="STEP_GO_BACK_BIT" value="33554432"> Hint used by [method _step] to tell that control should return back, either hitting a previous STEP_PUSH_STACK_BIT or exiting the function. diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index d3158df357..c8543287b4 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node which deconstructs a base type instance into its parts. </brief_description> @@ -8,12 +8,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="type" type="int" setter="set_deconstruct_type" getter="get_deconstruct_type" enum="Variant.Type"> + <member name="type" type="int" setter="set_deconstruct_type" getter="get_deconstruct_type" enum="Variant.Type" default="0"> The type to deconstruct. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index fc49cfc07b..add2eb2275 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.1"> +<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_node"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index 4bb05525bd..1a567905a9 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Emits a specified signal. </brief_description> @@ -12,12 +12,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="signal" type="String" setter="set_signal" getter="get_signal"> + <member name="signal" type="String" setter="set_signal" getter="get_signal" default=""""> The signal to emit. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 93d7ce3516..05bd87ec22 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> A Visual Script node returning a singleton from [@GlobalScope] </brief_description> @@ -8,12 +8,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="constant" type="String" setter="set_singleton" getter="get_singleton"> + <member name="constant" type="String" setter="set_singleton" getter="get_singleton" default=""""> The singleton's name. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index 343e83cb55..eb6cdbb302 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index ec8e955cf7..152b48ca89 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index f6116cf539..f7f86e8c80 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -1,35 +1,33 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="base_script" type="String" setter="set_base_script" getter="get_base_script"> </member> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> </member> <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type"> </member> - <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptFunctionCall.CallMode"> + <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptFunctionCall.CallMode" default="0"> </member> - <member name="function" type="String" setter="set_function" getter="get_function"> + <member name="function" type="String" setter="set_function" getter="get_function" default=""""> </member> <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path"> </member> - <member name="rpc_call_mode" type="int" setter="set_rpc_call_mode" getter="get_rpc_call_mode" enum="VisualScriptFunctionCall.RPCCallMode"> + <member name="rpc_call_mode" type="int" setter="set_rpc_call_mode" getter="get_rpc_call_mode" enum="VisualScriptFunctionCall.RPCCallMode" default="0"> </member> <member name="singleton" type="String" setter="set_singleton" getter="get_singleton"> </member> <member name="use_default_args" type="int" setter="set_use_default_args" getter="get_use_default_args"> </member> - <member name="validate" type="bool" setter="set_validate" getter="get_validate"> + <member name="validate" type="bool" setter="set_validate" getter="get_validate" default="true"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index c75dd0cdbc..a8e820e6ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.1"> +<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_signal"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index 9d43204f02..6c70dc7dc4 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="constant" type="int" setter="set_global_constant" getter="get_global_constant"> + <member name="constant" type="int" setter="set_global_constant" getter="get_global_constant" default="0"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 73c1f47e1a..ba0dc1a3d0 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index 652c29a9ac..09a8f2c8ad 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index ab4c23012b..8c942813b2 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -1,19 +1,17 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="action" type="String" setter="set_action_name" getter="get_action_name"> + <member name="action" type="String" setter="set_action_name" getter="get_action_name" default=""""> </member> - <member name="mode" type="int" setter="set_action_mode" getter="get_action_mode" enum="VisualScriptInputAction.Mode"> + <member name="mode" type="int" setter="set_action_mode" getter="get_action_mode" enum="VisualScriptInputAction.Mode" default="0"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index 7090621bd7..782bbfae20 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Steps through items in a given input. </brief_description> @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index 5c8ee6453c..8c21c3f0bc 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Gets a local variable's value. </brief_description> @@ -12,15 +12,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type"> + <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0"> The local variable's type. </member> - <member name="var_name" type="String" setter="set_var_name" getter="get_var_name"> + <member name="var_name" type="String" setter="set_var_name" getter="get_var_name" default=""new_local""> The local variable's name. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index f2e6c48907..a981432cdb 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Changes a local variable's value. </brief_description> @@ -14,15 +14,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type"> + <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0"> The local variable's type. </member> - <member name="var_name" type="String" setter="set_var_name" getter="get_var_name"> + <member name="var_name" type="String" setter="set_var_name" getter="get_var_name" default=""new_local""> The local variable's name. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index d456e880b7..3be392aef5 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Commonly used mathematical constants. </brief_description> @@ -12,12 +12,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="constant" type="int" setter="set_math_constant" getter="get_math_constant" enum="VisualScriptMathConstant.MathConstant"> + <member name="constant" type="int" setter="set_math_constant" getter="get_math_constant" enum="VisualScriptMathConstant.MathConstant" default="0"> The math constant. </member> </members> @@ -47,6 +45,7 @@ Not a number: [code]nan[/code] </constant> <constant name="MATH_CONSTANT_MAX" value="8" enum="MathConstant"> + Represents the size of the [enum MathConstant] enum. </constant> </constants> </class> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index 941a0cd91a..89fb5c81f8 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.1"> +<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.2"> <brief_description> A node which is part of a [VisualScript]. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_default_input_value" qualifiers="const"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index e60d50c977..80449ac521 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> @@ -11,14 +11,12 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="Variant.Operator"> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="Variant.Operator" default="6"> </member> - <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type"> + <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index 5a2886ccac..b3b39691c9 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Creates a new [Resource] or loads one from the filesystem. </brief_description> @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index 60cc8fdd4f..af1e8e68e8 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -1,19 +1,17 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="base_script" type="String" setter="set_base_script" getter="get_base_script"> </member> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> </member> <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type"> </member> @@ -21,9 +19,9 @@ </member> <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path"> </member> - <member name="property" type="String" setter="set_property" getter="get_property"> + <member name="property" type="String" setter="set_property" getter="get_property" default=""""> </member> - <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertyGet.CallMode"> + <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertyGet.CallMode" default="0"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index 8f29e9d152..380c196caf 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -1,21 +1,19 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="assign_op" type="int" setter="set_assign_op" getter="get_assign_op" enum="VisualScriptPropertySet.AssignOp"> + <member name="assign_op" type="int" setter="set_assign_op" getter="get_assign_op" enum="VisualScriptPropertySet.AssignOp" default="0"> </member> <member name="base_script" type="String" setter="set_base_script" getter="get_base_script"> </member> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> </member> <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type"> </member> @@ -23,9 +21,9 @@ </member> <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path"> </member> - <member name="property" type="String" setter="set_property" getter="get_property"> + <member name="property" type="String" setter="set_property" getter="get_property" default=""""> </member> - <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertySet.CallMode"> + <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertySet.CallMode" default="0"> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index f6300e03f0..da37bbbb2c 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="path" type="String" setter="set_resource_path" getter="get_resource_path"> + <member name="path" type="String" setter="set_resource_path" getter="get_resource_path" default=""""> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index 6095520eff..9a81c671d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Exits a function and returns an optional value. </brief_description> @@ -13,15 +13,13 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="return_enabled" type="bool" setter="set_enable_return_value" getter="is_return_value_enabled"> - If [code]true[/code] the [code]return[/code] input port is available. + <member name="return_enabled" type="bool" setter="set_enable_return_value" getter="is_return_value_enabled" default="false"> + If [code]true[/code], the [code]return[/code] input port is available. </member> - <member name="return_type" type="int" setter="set_return_type" getter="get_return_type" enum="Variant.Type"> + <member name="return_type" type="int" setter="set_return_type" getter="get_return_type" enum="Variant.Type" default="0"> The return value's data type. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index 7704eaba04..99f4acbd27 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Node reference. </brief_description> @@ -12,12 +12,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="node_path" type="NodePath" setter="set_node_path" getter="get_node_path"> + <member name="node_path" type="NodePath" setter="set_node_path" getter="get_node_path" default="NodePath(".")"> The node's path in the scene tree. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index 2c2af9262d..84718ba119 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index 0731fc77e1..e6a6cd5a48 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Chooses between two input values. </brief_description> @@ -14,12 +14,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type"> + <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0"> The input variables' type. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index 61a73e104c..231e7d8f0d 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Outputs a reference to the current instance. </brief_description> @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index c71e068045..2ceceb199f 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Executes a series of Sequence ports. </brief_description> @@ -14,12 +14,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="steps" type="int" setter="set_steps" getter="get_steps"> + <member name="steps" type="int" setter="set_steps" getter="get_steps" default="1"> The number of steps in the sequence. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 46aeebab9c..51b0093209 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_subcall" qualifiers="virtual"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index a00811a29b..3e3b88ebe0 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Branches program flow based on a given input's value. </brief_description> <description> - Branches the flow based on an input's value. Use "Case Count" in the Inspector to set the number of branches and each comparison's optional type. + Branches the flow based on an input's value. Use [b]Case Count[/b] in the Inspector to set the number of branches and each comparison's optional type. [b]Input Ports:[/b] - Sequence: [code]'input' is[/code] - Data (variant): [code]=[/code] @@ -17,8 +17,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index 0bdc4ce89d..999a190a9a 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -1,19 +1,17 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="base_script" type="String" setter="set_base_script" getter="get_base_script"> + <member name="base_script" type="String" setter="set_base_script" getter="get_base_script" default=""""> </member> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> </member> </members> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 06178a399d..40e461294b 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Gets a variable's value. </brief_description> @@ -12,12 +12,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="var_name" type="String" setter="set_variable" getter="get_variable"> + <member name="var_name" type="String" setter="set_variable" getter="get_variable" default=""""> The variable's name. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index 5969f25060..6c53ad61b4 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Changes a variable's value. </brief_description> @@ -13,12 +13,10 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="var_name" type="String" setter="set_variable" getter="get_variable"> + <member name="var_name" type="String" setter="set_variable" getter="get_variable" default=""""> The variable's name. </member> </members> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index b9e7f6a553..77211fa088 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> Conditional loop. </brief_description> @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index c4698f746a..c506e6db01 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="mode" type="int" setter="set_yield_mode" getter="get_yield_mode" enum="VisualScriptYield.YieldMode"> + <member name="mode" type="int" setter="set_yield_mode" getter="get_yield_mode" enum="VisualScriptYield.YieldMode" default="1"> </member> <member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time"> </member> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index b67e4ab1b8..b9d7aedaab 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -1,23 +1,21 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.1"> +<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> - <member name="base_type" type="String" setter="set_base_type" getter="get_base_type"> + <member name="base_type" type="String" setter="set_base_type" getter="get_base_type" default=""Object""> </member> - <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptYieldSignal.CallMode"> + <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptYieldSignal.CallMode" default="0"> </member> <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path"> </member> - <member name="signal" type="String" setter="set_signal" getter="get_signal"> + <member name="signal" type="String" setter="set_signal" getter="get_signal" default=""""> </member> </members> <constants> diff --git a/modules/visual_script/icons/icon_visual_script.svg b/modules/visual_script/icons/icon_visual_script.svg new file mode 100644 index 0000000000..f6475d590e --- /dev/null +++ b/modules/visual_script/icons/icon_visual_script.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<ellipse cx="3" cy="1039.4" r="2" fill="#6e6e6e"/> +<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2h5.2715a2 2 0 0 1 -0.27148 -1 2 2 0 0 1 2 -2 2 2 0 0 1 2 2 2 2 0 0 1 -0.26953 1h5.2695v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3 -3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324 -1 2 2 0 0 0 0 -2 2 2 0 0 0 -1.7324 -1h2v-2h-2z" fill="#e0e0e0"/> +</g> +</svg> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 6e081817f1..24b96223d7 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/register_types.h b/modules/visual_script/register_types.h index 27b3bd649a..05b3164b3c 100644 --- a/modules/visual_script/register_types.h +++ b/modules/visual_script/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 5b3b3a6769..b816e37936 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,7 @@ #include "visual_script.h" +#include "core/core_string_names.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/main/node.h" @@ -45,25 +46,7 @@ bool VisualScriptNode::is_breakpoint() const { return breakpoint; } -void VisualScriptNode::_notification(int p_what) { - - if (p_what == NOTIFICATION_POSTINITIALIZE) { - _update_input_ports(); - } -} - -void VisualScriptNode::_update_input_ports() { - default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize - int port_count = get_input_value_port_count(); - for (int i = 0; i < port_count; i++) { - Variant::Type expected = get_input_value_port_info(i).type; - Variant::CallError ce; - set_default_input_value(i, Variant::construct(expected, NULL, 0, ce, false)); - } -} - void VisualScriptNode::ports_changed_notify() { - _update_input_ports(); emit_signal("ports_changed"); } @@ -92,8 +75,7 @@ void VisualScriptNode::_set_default_input_values(Array p_values) { } void VisualScriptNode::validate_input_default_values() { - - default_input_values.resize(get_input_value_port_count()); + default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize //actually validate on save for (int i = 0; i < get_input_value_port_count(); i++) { @@ -119,8 +101,10 @@ void VisualScriptNode::validate_input_default_values() { Array VisualScriptNode::_get_default_input_values() const { //validate on save, since on load there is little info about this + Array values = default_input_values; + values.resize(get_input_value_port_count()); - return default_input_values; + return values; } String VisualScriptNode::get_text() const { @@ -281,11 +265,7 @@ void VisualScript::_node_ports_changed(int p_id) { Function &func = functions[function]; Ref<VisualScriptNode> vsn = func.nodes[p_id].node; - if (OS::get_singleton()->get_main_loop() && - Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()) && - Engine::get_singleton()->is_editor_hint()) { - vsn->validate_input_default_values(); //force validate default values when editing on editor - } + vsn->validate_input_default_values(); //must revalidate all the functions @@ -361,6 +341,7 @@ void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<Visual Ref<VisualScriptNode> vsn = p_node; vsn->connect("ports_changed", this, "_node_ports_changed", varray(p_id)); vsn->scripts_used.insert(this); + vsn->validate_input_default_values(); // Validate when fully loaded func.nodes[p_id] = nd; } @@ -1165,9 +1146,9 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array nodes = func["nodes"]; - for (int i = 0; i < nodes.size(); i += 3) { + for (int j = 0; j < nodes.size(); j += 3) { - add_node(name, nodes[i], nodes[i + 2], nodes[i + 1]); + add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); } Array sequence_connections = func["sequence_connections"]; @@ -1506,7 +1487,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p Variant **output_args = (Variant **)(input_args + max_input_args); int flow_max = f->flow_stack_size; int *flow_stack = flow_max ? (int *)(output_args + max_output_args) : (int *)NULL; - int *pass_stack = flow_stack + flow_max; + int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)NULL; String error_str; @@ -1711,7 +1692,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p if ((ret == output || ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) && node->sequence_output_count) { //if no exit bit was set, and has sequence outputs, guess next node - if (output < 0 || output >= node->sequence_output_count) { + if (output >= node->sequence_output_count) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; error_str = RTR("Node returned an invalid sequence output: ") + itos(output); error = true; @@ -1924,7 +1905,7 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p Variant **output_args = (Variant **)(input_args + max_input_args); int flow_max = f->flow_stack_size; int *flow_stack = flow_max ? (int *)(output_args + max_output_args) : (int *)NULL; - int *pass_stack = flow_stack + flow_max; + int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)NULL; for (int i = 0; i < f->node_count; i++) { sequence_bits[i] = false; //all starts as false @@ -1985,6 +1966,27 @@ void VisualScriptInstance::notification(int p_notification) { call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); //do as call } +String VisualScriptInstance::to_string(bool *r_valid) { + if (has_method(CoreStringNames::get_singleton()->_to_string)) { + Variant::CallError ce; + Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce); + if (ce.error == Variant::CallError::CALL_OK) { + if (ret.get_type() != Variant::STRING) { + if (r_valid) + *r_valid = false; + ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String."); + ERR_FAIL_V(String()); + } + if (r_valid) + *r_valid = true; + return ret.operator String(); + } + } + if (r_valid) + *r_valid = false; + return String(); +} + Ref<Script> VisualScriptInstance::get_script() const { return script; diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index cdc9159a73..098c28370d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ class VisualScriptNodeInstance; class VisualScript; class VisualScriptNode : public Resource { - GDCLASS(VisualScriptNode, Resource) + GDCLASS(VisualScriptNode, Resource); friend class VisualScript; @@ -52,10 +52,8 @@ class VisualScriptNode : public Resource { Array _get_default_input_values() const; void validate_input_default_values(); - void _update_input_ports(); protected: - void _notification(int p_what); void ports_changed_notify(); static void _bind_methods(); @@ -167,7 +165,7 @@ public: class VisualScript : public Script { - GDCLASS(VisualScript, Script) + GDCLASS(VisualScript, Script); RES_BASE_EXTENSION("vs"); @@ -406,6 +404,7 @@ public: virtual bool has_method(const StringName &p_method) const; virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); virtual void notification(int p_notification); + String to_string(bool *r_valid); bool set_variable(const StringName &p_variable, const Variant &p_value) { diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 60bc54afe4..75b79f8929 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,6 +68,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "lerp", "inverse_lerp", "range_lerp", + "move_toward", "dectime", "randomize", "randi", @@ -102,6 +103,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "var2bytes", "bytes2var", "color_named", + "smoothstep", }; VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String &p_string) { @@ -183,10 +185,10 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case TEXT_PRINTRAW: case VAR_TO_STR: case STR_TO_VAR: - case VAR_TO_BYTES: - case BYTES_TO_VAR: case TYPE_EXISTS: return 1; + case VAR_TO_BYTES: + case BYTES_TO_VAR: case MATH_ATAN2: case MATH_FMOD: case MATH_FPOSMOD: @@ -204,6 +206,8 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { return 2; case MATH_LERP: case MATH_INVERSE_LERP: + case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -337,6 +341,22 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const else return PropertyInfo(Variant::REAL, "ostop"); } break; + case MATH_SMOOTHSTEP: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "weight"); + } break; + case MATH_MOVE_TOWARD: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "delta"); + } break; case MATH_DECTIME: { if (p_idx == 0) return PropertyInfo(Variant::REAL, "value"); @@ -491,12 +511,18 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const return PropertyInfo(Variant::STRING, "string"); } break; case VAR_TO_BYTES: { - return PropertyInfo(Variant::NIL, "var"); + if (p_idx == 0) + return PropertyInfo(Variant::NIL, "var"); + else + return PropertyInfo(Variant::BOOL, "full_objects"); } break; case BYTES_TO_VAR: { - return PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"); + if (p_idx == 0) + return PropertyInfo(Variant::POOL_BYTE_ARRAY, "bytes"); + else + return PropertyInfo(Variant::BOOL, "allow_objects"); } break; case COLORN: { @@ -563,6 +589,8 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: + case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: { t = Variant::REAL; @@ -655,11 +683,15 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons } break; case VAR_TO_BYTES: { - t = Variant::POOL_BYTE_ARRAY; + if (p_idx == 0) + t = Variant::POOL_BYTE_ARRAY; + else + t = Variant::BOOL; } break; case BYTES_TO_VAR: { - + if (p_idx == 1) + t = Variant::BOOL; } break; case COLORN: { t = Variant::COLOR; @@ -889,6 +921,19 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(4); *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); } break; + case VisualScriptBuiltinFunc::MATH_SMOOTHSTEP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case VisualScriptBuiltinFunc::MATH_MOVE_TOWARD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case VisualScriptBuiltinFunc::MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -1192,9 +1237,16 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in } break; case VisualScriptBuiltinFunc::VAR_TO_BYTES: { + if (p_inputs[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; + return; + } PoolByteArray barr; int len; - Error err = encode_variant(*p_inputs[0], NULL, len); + bool full_objects = *p_inputs[1]; + Error err = encode_variant(*p_inputs[0], NULL, len, full_objects); if (err) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; @@ -1206,7 +1258,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in barr.resize(len); { PoolByteArray::Write w = barr.write(); - encode_variant(*p_inputs[0], w.ptr(), len); + encode_variant(*p_inputs[0], w.ptr(), len, full_objects); } *r_return = barr; } break; @@ -1216,15 +1268,21 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::POOL_BYTE_ARRAY; - + return; + } + if (p_inputs[1]->get_type() != Variant::BOOL) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::BOOL; return; } PoolByteArray varr = *p_inputs[0]; + bool allow_objects = *p_inputs[1]; Variant ret; { PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL, allow_objects); if (err != OK) { r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; @@ -1247,7 +1305,8 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in *r_return = String(color); } break; - default: {} + default: { + } } } @@ -1322,6 +1381,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_LERP); BIND_ENUM_CONSTANT(MATH_INVERSE_LERP); BIND_ENUM_CONSTANT(MATH_RANGE_LERP); + BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD); BIND_ENUM_CONSTANT(MATH_DECTIME); BIND_ENUM_CONSTANT(MATH_RANDOMIZE); BIND_ENUM_CONSTANT(MATH_RAND); @@ -1356,6 +1416,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(VAR_TO_BYTES); BIND_ENUM_CONSTANT(BYTES_TO_VAR); BIND_ENUM_CONSTANT(COLORN); + BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -1410,6 +1471,8 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 3345762b9f..f009f49b5b 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,8 @@ class VisualScriptBuiltinFunc : public VisualScriptNode { - GDCLASS(VisualScriptBuiltinFunc, VisualScriptNode) + GDCLASS(VisualScriptBuiltinFunc, VisualScriptNode); + public: enum BuiltinFunc { MATH_SIN, @@ -67,6 +68,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_MOVE_TOWARD, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, @@ -101,6 +103,7 @@ public: VAR_TO_BYTES, BYTES_TO_VAR, COLORN, + MATH_SMOOTHSTEP, FUNC_MAX }; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index afaa6a9b95..31d5e4665a 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "core/variant.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" +#include "scene/main/viewport.h" #include "visual_script_expression.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" @@ -45,7 +46,7 @@ #ifdef TOOLS_ENABLED class VisualScriptEditorSignalEdit : public Object { - GDCLASS(VisualScriptEditorSignalEdit, Object) + GDCLASS(VisualScriptEditorSignalEdit, Object); StringName sig; @@ -185,7 +186,7 @@ public: class VisualScriptEditorVariableEdit : public Object { - GDCLASS(VisualScriptEditorVariableEdit, Object) + GDCLASS(VisualScriptEditorVariableEdit, Object); StringName var; @@ -340,74 +341,74 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) { Color color; if (dark_theme) switch (p_type) { - case Variant::NIL: color = Color::html("#69ecbd"); break; - - case Variant::BOOL: color = Color::html("#8da6f0"); break; - case Variant::INT: color = Color::html("#7dc6ef"); break; - case Variant::REAL: color = Color::html("#61daf4"); break; - case Variant::STRING: color = Color::html("#6ba7ec"); break; - - case Variant::VECTOR2: color = Color::html("#bd91f1"); break; - case Variant::RECT2: color = Color::html("#f191a5"); break; - case Variant::VECTOR3: color = Color::html("#d67dee"); break; - case Variant::TRANSFORM2D: color = Color::html("#c4ec69"); break; - case Variant::PLANE: color = Color::html("#f77070"); break; - case Variant::QUAT: color = Color::html("#ec69a3"); break; - case Variant::AABB: color = Color::html("#ee7991"); break; - case Variant::BASIS: color = Color::html("#e3ec69"); break; - case Variant::TRANSFORM: color = Color::html("#f6a86e"); break; - - case Variant::COLOR: color = Color::html("#9dff70"); break; - case Variant::NODE_PATH: color = Color::html("#6993ec"); break; - case Variant::_RID: color = Color::html("#69ec9a"); break; - case Variant::OBJECT: color = Color::html("#79f3e8"); break; - case Variant::DICTIONARY: color = Color::html("#77edb1"); break; - - case Variant::ARRAY: color = Color::html("#e0e0e0"); break; - case Variant::POOL_BYTE_ARRAY: color = Color::html("#aaf4c8"); break; - case Variant::POOL_INT_ARRAY: color = Color::html("#afdcf5"); break; - case Variant::POOL_REAL_ARRAY: color = Color::html("#97e7f8"); break; - case Variant::POOL_STRING_ARRAY: color = Color::html("#9dc4f2"); break; - case Variant::POOL_VECTOR2_ARRAY: color = Color::html("#d1b3f5"); break; - case Variant::POOL_VECTOR3_ARRAY: color = Color::html("#df9bf2"); break; - case Variant::POOL_COLOR_ARRAY: color = Color::html("#e9ff97"); break; + case Variant::NIL: color = Color(0.41, 0.93, 0.74); break; + + case Variant::BOOL: color = Color(0.55, 0.65, 0.94); break; + case Variant::INT: color = Color(0.49, 0.78, 0.94); break; + case Variant::REAL: color = Color(0.38, 0.85, 0.96); break; + case Variant::STRING: color = Color(0.42, 0.65, 0.93); break; + + case Variant::VECTOR2: color = Color(0.74, 0.57, 0.95); break; + case Variant::RECT2: color = Color(0.95, 0.57, 0.65); break; + case Variant::VECTOR3: color = Color(0.84, 0.49, 0.93); break; + case Variant::TRANSFORM2D: color = Color(0.77, 0.93, 0.41); break; + case Variant::PLANE: color = Color(0.97, 0.44, 0.44); break; + case Variant::QUAT: color = Color(0.93, 0.41, 0.64); break; + case Variant::AABB: color = Color(0.93, 0.47, 0.57); break; + case Variant::BASIS: color = Color(0.89, 0.93, 0.41); break; + case Variant::TRANSFORM: color = Color(0.96, 0.66, 0.43); break; + + case Variant::COLOR: color = Color(0.62, 1.0, 0.44); break; + case Variant::NODE_PATH: color = Color(0.41, 0.58, 0.93); break; + case Variant::_RID: color = Color(0.41, 0.93, 0.6); break; + case Variant::OBJECT: color = Color(0.47, 0.95, 0.91); break; + case Variant::DICTIONARY: color = Color(0.47, 0.93, 0.69); break; + + case Variant::ARRAY: color = Color(0.88, 0.88, 0.88); break; + case Variant::POOL_BYTE_ARRAY: color = Color(0.67, 0.96, 0.78); break; + case Variant::POOL_INT_ARRAY: color = Color(0.69, 0.86, 0.96); break; + case Variant::POOL_REAL_ARRAY: color = Color(0.59, 0.91, 0.97); break; + case Variant::POOL_STRING_ARRAY: color = Color(0.62, 0.77, 0.95); break; + case Variant::POOL_VECTOR2_ARRAY: color = Color(0.82, 0.7, 0.96); break; + case Variant::POOL_VECTOR3_ARRAY: color = Color(0.87, 0.61, 0.95); break; + case Variant::POOL_COLOR_ARRAY: color = Color(0.91, 1.0, 0.59); break; default: color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.7, 0.7); } else switch (p_type) { - case Variant::NIL: color = Color::html("#25e3a0"); break; - - case Variant::BOOL: color = Color::html("#6d8eeb"); break; - case Variant::INT: color = Color::html("#4fb2e9"); break; - case Variant::REAL: color = Color::html("#27ccf0"); break; - case Variant::STRING: color = Color::html("#4690e7"); break; - - case Variant::VECTOR2: color = Color::html("#ad76ee"); break; - case Variant::RECT2: color = Color::html("#ee758e"); break; - case Variant::VECTOR3: color = Color::html("#dc6aed"); break; - case Variant::TRANSFORM2D: color = Color::html("#96ce1a"); break; - case Variant::PLANE: color = Color::html("#f77070"); break; - case Variant::QUAT: color = Color::html("#ec69a3"); break; - case Variant::AABB: color = Color::html("#ee7991"); break; - case Variant::BASIS: color = Color::html("#b2bb19"); break; - case Variant::TRANSFORM: color = Color::html("#f49047"); break; - - case Variant::COLOR: color = Color::html("#3cbf00"); break; - case Variant::NODE_PATH: color = Color::html("#6993ec"); break; - case Variant::_RID: color = Color::html("#2ce573"); break; - case Variant::OBJECT: color = Color::html("#12d5c3"); break; - case Variant::DICTIONARY: color = Color::html("#57e99f"); break; - - case Variant::ARRAY: color = Color::html("#737373"); break; - case Variant::POOL_BYTE_ARRAY: color = Color::html("#61ea98"); break; - case Variant::POOL_INT_ARRAY: color = Color::html("#61baeb"); break; - case Variant::POOL_REAL_ARRAY: color = Color::html("#40d3f2"); break; - case Variant::POOL_STRING_ARRAY: color = Color::html("#609fea"); break; - case Variant::POOL_VECTOR2_ARRAY: color = Color::html("#9d5dea"); break; - case Variant::POOL_VECTOR3_ARRAY: color = Color::html("#ca5aea"); break; - case Variant::POOL_COLOR_ARRAY: color = Color::html("#92ba00"); break; + case Variant::NIL: color = Color(0.15, 0.89, 0.63); break; + + case Variant::BOOL: color = Color(0.43, 0.56, 0.92); break; + case Variant::INT: color = Color(0.31, 0.7, 0.91); break; + case Variant::REAL: color = Color(0.15, 0.8, 0.94); break; + case Variant::STRING: color = Color(0.27, 0.56, 0.91); break; + + case Variant::VECTOR2: color = Color(0.68, 0.46, 0.93); break; + case Variant::RECT2: color = Color(0.93, 0.46, 0.56); break; + case Variant::VECTOR3: color = Color(0.86, 0.42, 0.93); break; + case Variant::TRANSFORM2D: color = Color(0.59, 0.81, 0.1); break; + case Variant::PLANE: color = Color(0.97, 0.44, 0.44); break; + case Variant::QUAT: color = Color(0.93, 0.41, 0.64); break; + case Variant::AABB: color = Color(0.93, 0.47, 0.57); break; + case Variant::BASIS: color = Color(0.7, 0.73, 0.1); break; + case Variant::TRANSFORM: color = Color(0.96, 0.56, 0.28); break; + + case Variant::COLOR: color = Color(0.24, 0.75, 0.0); break; + case Variant::NODE_PATH: color = Color(0.41, 0.58, 0.93); break; + case Variant::_RID: color = Color(0.17, 0.9, 0.45); break; + case Variant::OBJECT: color = Color(0.07, 0.84, 0.76); break; + case Variant::DICTIONARY: color = Color(0.34, 0.91, 0.62); break; + + case Variant::ARRAY: color = Color(0.45, 0.45, 0.45); break; + case Variant::POOL_BYTE_ARRAY: color = Color(0.38, 0.92, 0.6); break; + case Variant::POOL_INT_ARRAY: color = Color(0.38, 0.73, 0.92); break; + case Variant::POOL_REAL_ARRAY: color = Color(0.25, 0.83, 0.95); break; + case Variant::POOL_STRING_ARRAY: color = Color(0.38, 0.62, 0.92); break; + case Variant::POOL_VECTOR2_ARRAY: color = Color(0.62, 0.36, 0.92); break; + case Variant::POOL_VECTOR3_ARRAY: color = Color(0.79, 0.35, 0.92); break; + case Variant::POOL_COLOR_ARRAY: color = Color(0.57, 0.73, 0.0); break; default: color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.3, 0.3); @@ -579,7 +580,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { if (gnode->is_comment()) sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); - Color c = sbf->get_border_color(MARGIN_TOP); + Color c = sbf->get_border_color(); c.a = 1; if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); @@ -1098,6 +1099,9 @@ void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id void VisualScriptEditor::_available_node_doubleclicked() { + if (edited_func == String()) + return; + TreeItem *item = nodes->get_selected(); if (!item) @@ -1106,7 +1110,6 @@ void VisualScriptEditor::_available_node_doubleclicked() { String which = item->get_metadata(0); if (which == String()) return; - Vector2 ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5; if (graph->is_using_snap()) { @@ -1329,8 +1332,9 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { } } -void VisualScriptEditor::_generic_search() { - new_connect_node_select->select_from_visual_script(String(""), false); +void VisualScriptEditor::_generic_search(String p_base_type) { + port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position(); + new_connect_node_select->select_from_visual_script(p_base_type, false); } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1785,7 +1789,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da call->set_base_path(sn->get_path_to(node)); call->set_base_type(node->get_class()); n = call; - method_select->select_from_instance(node); selecting_method_id = base_id; } @@ -2040,7 +2043,7 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { Dictionary d = p_state; if (d.has("function")) { - edited_func = p_state; + edited_func = d["function"]; selected = edited_func; } @@ -2099,9 +2102,20 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { } } +void VisualScriptEditor::set_executing_line(int p_line) { + // todo: add a way to show which node is executing right now. +} + +void VisualScriptEditor::clear_executing_line() { + // todo: add a way to show which node is executing right now. +} + void VisualScriptEditor::trim_trailing_whitespace() { } +void VisualScriptEditor::insert_final_newline() { +} + void VisualScriptEditor::convert_indent_to_spaces() { } @@ -2209,7 +2223,7 @@ Control *VisualScriptEditor::get_edit_menu() { void VisualScriptEditor::_change_base_type() { - select_base_type->popup_create(true); + select_base_type->popup_create(true, true); } void VisualScriptEditor::clear_edit_menu() { @@ -2717,93 +2731,98 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); - vsfc->set_base_type(String("")); - if (tg.gdclass != StringName()) { - vsfc->set_base_type(tg.gdclass); + if (p_connecting) { + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (tg.type == Variant::OBJECT) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsfc->set_base_type(tg.gdclass); + + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; - if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { - vsfc->set_base_type(base_type); + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsfc->set_base_type(base_type); + } + if (p_text == "call" || p_text == "call_deferred") { + vsfc->set_function(String("")); + } } - if (p_text == "call" || p_text == "call_deferred") { - vsfc->set_function(String("")); + if (tg.script.is_valid()) { + vsfc->set_base_script(tg.script->get_path()); } + } else if (tg.type == Variant::NIL) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(String("")); + } else { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); + vsfc->set_basic_type(tg.type); } - if (tg.script.is_valid()) { - vsfc->set_base_script(tg.script->get_path()); - } - } else if (tg.type == Variant::NIL) { - vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); - vsfc->set_base_type(String("")); - } else { - vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); - vsfc->set_basic_type(tg.type); } } - if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { + // if connecting from another node the call mode shouldn't be self + if (p_connecting) { + if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { + Ref<VisualScriptPropertySet> vsp = vsn; - Ref<VisualScriptPropertySet> vsp = vsn; - - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - vsp->set_base_type(String("")); - if (tg.gdclass != StringName()) { - vsp->set_base_type(tg.gdclass); + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; - if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { - vsp->set_base_type(base_type); + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + } else { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); } - if (tg.script.is_valid()) { - vsp->set_base_script(tg.script->get_path()); - } - } else if (tg.type == Variant::NIL) { - vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - vsp->set_base_type(String("")); - } else { - vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); - vsp->set_basic_type(tg.type); } - } - - if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { - Ref<VisualScriptPropertyGet> vsp = vsn; - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); - vsp->set_base_type(String("")); - if (tg.gdclass != StringName()) { - vsp->set_base_type(tg.gdclass); + if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { + Ref<VisualScriptPropertyGet> vsp = vsn; - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; - if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { - vsp->set_base_type(base_type); + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + } else { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); } - if (tg.script.is_valid()) { - vsp->set_base_script(tg.script->get_path()); - } - } else if (tg.type == Variant::NIL) { - vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); - vsp->set_base_type(String("")); - } else { - vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); - vsp->set_basic_type(tg.type); } } Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); @@ -3035,29 +3054,29 @@ void VisualScriptEditor::_notification(int p_what) { List<Pair<String, Color> > colors; if (dark_theme) { - colors.push_back(Pair<String, Color>("flow_control", Color::html("#f4f4f4"))); - colors.push_back(Pair<String, Color>("functions", Color::html("#f58581"))); - colors.push_back(Pair<String, Color>("data", Color::html("#80f6cf"))); - colors.push_back(Pair<String, Color>("operators", Color::html("#ab97df"))); - colors.push_back(Pair<String, Color>("custom", Color::html("#80bbf6"))); - colors.push_back(Pair<String, Color>("constants", Color::html("#f680b0"))); + colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96))); + colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51))); + colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81))); + colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87))); + colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96))); + colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69))); } else { - colors.push_back(Pair<String, Color>("flow_control", Color::html("#424242"))); - colors.push_back(Pair<String, Color>("functions", Color::html("#f26661"))); - colors.push_back(Pair<String, Color>("data", Color::html("#13bb83"))); - colors.push_back(Pair<String, Color>("operators", Color::html("#8265d0"))); - colors.push_back(Pair<String, Color>("custom", Color::html("#4ea0f2"))); - colors.push_back(Pair<String, Color>("constants", Color::html("#f02f7d"))); + colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26))); + colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38))); + colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51))); + colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82))); + colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95))); + colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49))); } for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) { Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode"); if (!sb.is_null()) { Ref<StyleBoxFlat> frame_style = sb->duplicate(); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color cn = E->get().second; cn.a = c.a; - frame_style->set_border_color_all(cn); + frame_style->set_border_color(cn); node_styles[E->get().first] = frame_style; } } @@ -3103,7 +3122,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ graph->set_block_minimum_size_adjust(true); //faster resize - undo_redo->create_action("Resize Comment", UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Resize Comment"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(vsc.ptr(), "set_size", p_new_size / EDSCALE); undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size()); undo_redo->commit_action(); @@ -3146,7 +3165,7 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_FIND_NODE_TYPE: { - _generic_search(); + _generic_search(script->get_instance_base_type()); } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { @@ -3491,6 +3510,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); + edit_menu->set_switch_on_hover(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE); @@ -3563,7 +3583,7 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("scroll_offset_changed", this, "_graph_ofs_changed"); select_func_text = memnew(Label); - select_func_text->set_text(TTR("Select or create a function to edit graph")); + select_func_text->set_text(TTR("Select or create a function to edit its graph.")); select_func_text->set_align(Label::ALIGN_CENTER); select_func_text->set_valign(Label::VALIGN_CENTER); select_func_text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -3604,7 +3624,6 @@ VisualScriptEditor::VisualScriptEditor() { edit_signal_dialog = memnew(AcceptDialog); edit_signal_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_signal_dialog); - edit_signal_dialog->set_title(TTR("Edit Signal Arguments:")); signal_editor = memnew(VisualScriptEditorSignalEdit); edit_signal_edit = memnew(EditorInspector); @@ -3615,7 +3634,6 @@ VisualScriptEditor::VisualScriptEditor() { edit_variable_dialog = memnew(AcceptDialog); edit_variable_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_variable_dialog); - edit_variable_dialog->set_title(TTR("Edit Variable:")); variable_editor = memnew(VisualScriptEditorVariableEdit); edit_variable_edit = memnew(EditorInspector); @@ -3626,7 +3644,6 @@ VisualScriptEditor::VisualScriptEditor() { select_base_type = memnew(CreateDialog); select_base_type->set_base_type("Object"); //anything goes select_base_type->connect("create", this, "_change_base_type_callback"); - select_base_type->get_ok()->set_text(TTR("Change")); add_child(select_base_type); undo_redo = EditorNode::get_singleton()->get_undo_redo(); @@ -3744,4 +3761,7 @@ void _VisualScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &_VisualScriptEditor::remove_custom_node); ADD_SIGNAL(MethodInfo("custom_nodes_updated")); } + +void VisualScriptEditor::validate() { +} #endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 5f707c9e4c..4f302d1d72 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,7 +44,7 @@ class VisualScriptEditorVariableEdit; #ifdef TOOLS_ENABLED class VisualScriptEditor : public ScriptEditorBase { - GDCLASS(VisualScriptEditor, ScriptEditorBase) + GDCLASS(VisualScriptEditor, ScriptEditorBase); enum { TYPE_SEQUENCE = 1000, @@ -212,7 +212,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _input(const Ref<InputEvent> &p_event); - void _generic_search(); + void _generic_search(String p_base_type = ""); void _members_gui_input(const Ref<InputEvent> &p_event); void _on_nodes_delete(); @@ -263,7 +263,10 @@ public: virtual Variant get_edit_state(); virtual void set_edit_state(const Variant &p_state); virtual void goto_line(int p_line, bool p_with_error = false); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void trim_trailing_whitespace(); + virtual void insert_final_newline(); virtual void convert_indent_to_spaces(); virtual void convert_indent_to_tabs(); virtual void ensure_focus(); @@ -278,6 +281,7 @@ public: virtual Control *get_edit_menu(); virtual void clear_edit_menu(); virtual bool can_lose_focus_on_node_selection() { return false; } + virtual void validate(); static void register_editor(); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 868d22b541..4b74c088e0 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -679,10 +679,10 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { } str_ofs = cofs; //revert //parse an expression - ENode *expr = _parse_expression(); - if (!expr) + ENode *expr2 = _parse_expression(); + if (!expr2) return NULL; - dn->dict.push_back(expr); + dn->dict.push_back(expr2); _get_token(tk); if (tk.type != TK_COLON) { @@ -690,11 +690,11 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { return NULL; } - expr = _parse_expression(); - if (!expr) + expr2 = _parse_expression(); + if (!expr2) return NULL; - dn->dict.push_back(expr); + dn->dict.push_back(expr2); cofs = str_ofs; _get_token(tk); @@ -723,10 +723,10 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { } str_ofs = cofs; //revert //parse an expression - ENode *expr = _parse_expression(); - if (!expr) + ENode *expr2 = _parse_expression(); + if (!expr2) return NULL; - an->array.push_back(expr); + an->array.push_back(expr2); cofs = str_ofs; _get_token(tk); @@ -807,11 +807,11 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { } str_ofs = cofs; //revert //parse an expression - ENode *expr = _parse_expression(); - if (!expr) + ENode *expr2 = _parse_expression(); + if (!expr2) return NULL; - constructor->arguments.push_back(expr); + constructor->arguments.push_back(expr2); cofs = str_ofs; _get_token(tk); @@ -848,11 +848,11 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { } str_ofs = cofs; //revert //parse an expression - ENode *expr = _parse_expression(); - if (!expr) + ENode *expr2 = _parse_expression(); + if (!expr2) return NULL; - bifunc->arguments.push_back(expr); + bifunc->arguments.push_back(expr2); cofs = str_ofs; _get_token(tk); @@ -947,25 +947,25 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { while (true) { - int cofs = str_ofs; + int cofs3 = str_ofs; _get_token(tk); if (tk.type == TK_PARENTHESIS_CLOSE) { break; } - str_ofs = cofs; //revert + str_ofs = cofs3; //revert //parse an expression - ENode *expr = _parse_expression(); - if (!expr) + ENode *expr2 = _parse_expression(); + if (!expr2) return NULL; - func_call->arguments.push_back(expr); + func_call->arguments.push_back(expr2); - cofs = str_ofs; + cofs3 = str_ofs; _get_token(tk); if (tk.type == TK_COMMA) { //all good } else if (tk.type == TK_PARENTHESIS_CLOSE) { - str_ofs = cofs; + str_ofs = cofs3; } else { _set_error("Expected ',' or ')'"); } @@ -1032,7 +1032,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; - default: {}; + default: { + }; } if (op == Variant::OP_MAX) { //stop appending stuff @@ -1129,7 +1130,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { if (next_op == -1) { _set_error("Yet another parser bug...."); - ERR_FAIL_COND_V(next_op == -1, NULL); + ERR_FAIL_V(NULL); } // OK! create operator.. @@ -1426,8 +1427,8 @@ public: for (int i = 0; i < call->arguments.size(); i++) { Variant value; - bool ret = _execute(p_inputs, call->arguments[i], value, r_error_str, ce); - if (ret) + bool ret2 = _execute(p_inputs, call->arguments[i], value, r_error_str, ce); + if (ret2) return true; arr.write[i] = value; argp.write[i] = &arr[i]; diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h index 1f41e442c5..3a045d2cf7 100644 --- a/modules/visual_script/visual_script_expression.h +++ b/modules/visual_script/visual_script_expression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ class VisualScriptExpression : public VisualScriptNode { - GDCLASS(VisualScriptExpression, VisualScriptNode) + GDCLASS(VisualScriptExpression, VisualScriptNode); friend class VisualScriptNodeInstanceExpression; struct Input { diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index c3ab949d24..d927b75f89 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h index 40809d488a..d3c7aa68e3 100644 --- a/modules/visual_script/visual_script_flow_control.h +++ b/modules/visual_script/visual_script_flow_control.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,7 @@ class VisualScriptReturn : public VisualScriptNode { - GDCLASS(VisualScriptReturn, VisualScriptNode) + GDCLASS(VisualScriptReturn, VisualScriptNode); Variant::Type type; bool with_value; @@ -72,7 +72,7 @@ public: class VisualScriptCondition : public VisualScriptNode { - GDCLASS(VisualScriptCondition, VisualScriptNode) + GDCLASS(VisualScriptCondition, VisualScriptNode); protected: static void _bind_methods(); @@ -100,7 +100,7 @@ public: class VisualScriptWhile : public VisualScriptNode { - GDCLASS(VisualScriptWhile, VisualScriptNode) + GDCLASS(VisualScriptWhile, VisualScriptNode); protected: static void _bind_methods(); @@ -128,7 +128,7 @@ public: class VisualScriptIterator : public VisualScriptNode { - GDCLASS(VisualScriptIterator, VisualScriptNode) + GDCLASS(VisualScriptIterator, VisualScriptNode); protected: static void _bind_methods(); @@ -156,7 +156,7 @@ public: class VisualScriptSequence : public VisualScriptNode { - GDCLASS(VisualScriptSequence, VisualScriptNode) + GDCLASS(VisualScriptSequence, VisualScriptNode); int steps; @@ -189,7 +189,7 @@ public: class VisualScriptSwitch : public VisualScriptNode { - GDCLASS(VisualScriptSwitch, VisualScriptNode) + GDCLASS(VisualScriptSwitch, VisualScriptNode); struct Case { Variant::Type type; @@ -231,7 +231,7 @@ public: class VisualScriptTypeCast : public VisualScriptNode { - GDCLASS(VisualScriptTypeCast, VisualScriptNode) + GDCLASS(VisualScriptTypeCast, VisualScriptNode); StringName base_type; String script; diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 1913bfd8c7..c330fa1bc0 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,10 +51,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const { - if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) - return false; - else - return true; + return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))); } #ifdef TOOLS_ENABLED @@ -129,9 +126,8 @@ StringName VisualScriptFunctionCall::_get_base_type() const { int VisualScriptFunctionCall::get_input_value_port_count() const { if (call_mode == CALL_MODE_BASIC_TYPE) { - - Vector<StringName> names = Variant::get_method_argument_names(basic_type, function); - return names.size() + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) + 1; + Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type, function); + return types.size() + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) + 1; } else { @@ -574,7 +570,6 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const Node *bnode = _get_base_node(); if (bnode) { property.hint_string = bnode->get_path(); //convert to loong string - } else { } } } @@ -950,7 +945,7 @@ int VisualScriptPropertySet::get_output_sequence_port_count() const { bool VisualScriptPropertySet::has_input_sequence_port() const { - return call_mode != CALL_MODE_BASIC_TYPE ? true : false; + return call_mode != CALL_MODE_BASIC_TYPE; } Node *VisualScriptPropertySet::_get_base_node() const { @@ -1031,7 +1026,6 @@ void VisualScriptPropertySet::_adjust_input_index(PropertyInfo &pinfo) const { } PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const { - if (call_mode == CALL_MODE_INSTANCE || call_mode == CALL_MODE_BASIC_TYPE) { if (p_idx == 0) { PropertyInfo pi; @@ -1039,8 +1033,16 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower()); _adjust_input_index(pi); return pi; - } else { - p_idx--; + } + } + + List<PropertyInfo> props; + ClassDB::get_property_list(_get_base_type(), &props, true); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().name == property) { + PropertyInfo pinfo = PropertyInfo(E->get().type, "value", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); + _adjust_input_index(pinfo); + return pinfo; } } @@ -1054,6 +1056,13 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { + List<PropertyInfo> props; + ClassDB::get_property_list(_get_base_type(), &props, true); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().name == property) { + return PropertyInfo(E->get().type, "pass", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); + } + } return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); @@ -1356,7 +1365,6 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const { Node *bnode = _get_base_node(); if (bnode) { property.hint_string = bnode->get_path(); //convert to loong string - } else { } } } @@ -1563,7 +1571,8 @@ public: case VisualScriptPropertySet::ASSIGN_OP_BIT_XOR: { value = Variant::evaluate(Variant::OP_BIT_XOR, value, p_argument); } break; - default: {} + default: { + } } if (index != StringName()) { @@ -1797,22 +1806,18 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type); pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower()); return pi; - } else { - p_idx--; } } return PropertyInfo(); } PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const { - - if (index != StringName()) { - - Variant v; - Variant::CallError ce; - v = Variant::construct(type_cache, NULL, 0, ce); - Variant i = v.get(index); - return PropertyInfo(i.get_type(), "value." + String(index)); + List<PropertyInfo> props; + ClassDB::get_property_list(_get_base_type(), &props, true); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().name == property) { + return PropertyInfo(E->get().type, "value." + String(index)); + } } return PropertyInfo(type_cache, "value"); @@ -2076,7 +2081,6 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const { Node *bnode = _get_base_node(); if (bnode) { property.hint_string = bnode->get_path(); //convert to loong string - } else { } } } diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h index 3b522564d4..11eb96293b 100644 --- a/modules/visual_script/visual_script_func_nodes.h +++ b/modules/visual_script/visual_script_func_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,8 @@ class VisualScriptFunctionCall : public VisualScriptNode { - GDCLASS(VisualScriptFunctionCall, VisualScriptNode) + GDCLASS(VisualScriptFunctionCall, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -137,7 +138,8 @@ VARIANT_ENUM_CAST(VisualScriptFunctionCall::RPCCallMode); class VisualScriptPropertySet : public VisualScriptNode { - GDCLASS(VisualScriptPropertySet, VisualScriptNode) + GDCLASS(VisualScriptPropertySet, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -242,7 +244,8 @@ VARIANT_ENUM_CAST(VisualScriptPropertySet::AssignOp); class VisualScriptPropertyGet : public VisualScriptNode { - GDCLASS(VisualScriptPropertyGet, VisualScriptNode) + GDCLASS(VisualScriptPropertyGet, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -323,7 +326,7 @@ VARIANT_ENUM_CAST(VisualScriptPropertyGet::CallMode); class VisualScriptEmitSignal : public VisualScriptNode { - GDCLASS(VisualScriptEmitSignal, VisualScriptNode) + GDCLASS(VisualScriptEmitSignal, VisualScriptNode); private: StringName name; diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 99748af8a1..1b0e41b2de 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index f7ac995816..762a1bdfb6 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,7 @@ class VisualScriptFunction : public VisualScriptNode { - GDCLASS(VisualScriptFunction, VisualScriptNode) + GDCLASS(VisualScriptFunction, VisualScriptNode); struct Argument { String name; @@ -105,7 +105,7 @@ public: class VisualScriptOperator : public VisualScriptNode { - GDCLASS(VisualScriptOperator, VisualScriptNode) + GDCLASS(VisualScriptOperator, VisualScriptNode); Variant::Type typed; Variant::Operator op; @@ -141,7 +141,7 @@ public: class VisualScriptSelect : public VisualScriptNode { - GDCLASS(VisualScriptSelect, VisualScriptNode) + GDCLASS(VisualScriptSelect, VisualScriptNode); Variant::Type typed; @@ -174,7 +174,7 @@ public: class VisualScriptVariableGet : public VisualScriptNode { - GDCLASS(VisualScriptVariableGet, VisualScriptNode) + GDCLASS(VisualScriptVariableGet, VisualScriptNode); StringName variable; @@ -207,7 +207,7 @@ public: class VisualScriptVariableSet : public VisualScriptNode { - GDCLASS(VisualScriptVariableSet, VisualScriptNode) + GDCLASS(VisualScriptVariableSet, VisualScriptNode); StringName variable; @@ -240,7 +240,7 @@ public: class VisualScriptConstant : public VisualScriptNode { - GDCLASS(VisualScriptConstant, VisualScriptNode) + GDCLASS(VisualScriptConstant, VisualScriptNode); Variant::Type type; Variant value; @@ -277,7 +277,7 @@ public: class VisualScriptPreload : public VisualScriptNode { - GDCLASS(VisualScriptPreload, VisualScriptNode) + GDCLASS(VisualScriptPreload, VisualScriptNode); Ref<Resource> preload; @@ -309,7 +309,7 @@ public: class VisualScriptIndexGet : public VisualScriptNode { - GDCLASS(VisualScriptIndexGet, VisualScriptNode) + GDCLASS(VisualScriptIndexGet, VisualScriptNode); public: virtual int get_output_sequence_port_count() const; @@ -333,7 +333,7 @@ public: class VisualScriptIndexSet : public VisualScriptNode { - GDCLASS(VisualScriptIndexSet, VisualScriptNode) + GDCLASS(VisualScriptIndexSet, VisualScriptNode); public: virtual int get_output_sequence_port_count() const; @@ -357,7 +357,7 @@ public: class VisualScriptGlobalConstant : public VisualScriptNode { - GDCLASS(VisualScriptGlobalConstant, VisualScriptNode) + GDCLASS(VisualScriptGlobalConstant, VisualScriptNode); int index; @@ -388,7 +388,7 @@ public: class VisualScriptClassConstant : public VisualScriptNode { - GDCLASS(VisualScriptClassConstant, VisualScriptNode) + GDCLASS(VisualScriptClassConstant, VisualScriptNode); StringName base_type; StringName name; @@ -425,7 +425,7 @@ public: class VisualScriptBasicTypeConstant : public VisualScriptNode { - GDCLASS(VisualScriptBasicTypeConstant, VisualScriptNode) + GDCLASS(VisualScriptBasicTypeConstant, VisualScriptNode); Variant::Type type; StringName name; @@ -463,7 +463,8 @@ public: class VisualScriptMathConstant : public VisualScriptNode { - GDCLASS(VisualScriptMathConstant, VisualScriptNode) + GDCLASS(VisualScriptMathConstant, VisualScriptNode); + public: enum MathConstant { MATH_CONSTANT_ONE, @@ -512,7 +513,7 @@ VARIANT_ENUM_CAST(VisualScriptMathConstant::MathConstant) class VisualScriptEngineSingleton : public VisualScriptNode { - GDCLASS(VisualScriptEngineSingleton, VisualScriptNode) + GDCLASS(VisualScriptEngineSingleton, VisualScriptNode); String singleton; @@ -545,7 +546,7 @@ public: class VisualScriptSceneNode : public VisualScriptNode { - GDCLASS(VisualScriptSceneNode, VisualScriptNode) + GDCLASS(VisualScriptSceneNode, VisualScriptNode); NodePath path; @@ -580,7 +581,7 @@ public: class VisualScriptSceneTree : public VisualScriptNode { - GDCLASS(VisualScriptSceneTree, VisualScriptNode) + GDCLASS(VisualScriptSceneTree, VisualScriptNode); protected: virtual void _validate_property(PropertyInfo &property) const; @@ -610,7 +611,7 @@ public: class VisualScriptResourcePath : public VisualScriptNode { - GDCLASS(VisualScriptResourcePath, VisualScriptNode) + GDCLASS(VisualScriptResourcePath, VisualScriptNode); String path; @@ -642,7 +643,7 @@ public: class VisualScriptSelf : public VisualScriptNode { - GDCLASS(VisualScriptSelf, VisualScriptNode) + GDCLASS(VisualScriptSelf, VisualScriptNode); protected: static void _bind_methods(); @@ -671,7 +672,7 @@ public: class VisualScriptCustomNode : public VisualScriptNode { - GDCLASS(VisualScriptCustomNode, VisualScriptNode) + GDCLASS(VisualScriptCustomNode, VisualScriptNode); protected: static void _bind_methods(); @@ -719,7 +720,7 @@ VARIANT_ENUM_CAST(VisualScriptCustomNode::StartMode); class VisualScriptSubCall : public VisualScriptNode { - GDCLASS(VisualScriptSubCall, VisualScriptNode) + GDCLASS(VisualScriptSubCall, VisualScriptNode); protected: static void _bind_methods(); @@ -747,7 +748,7 @@ public: class VisualScriptComment : public VisualScriptNode { - GDCLASS(VisualScriptComment, VisualScriptNode) + GDCLASS(VisualScriptComment, VisualScriptNode); String title; String description; @@ -788,7 +789,7 @@ public: class VisualScriptConstructor : public VisualScriptNode { - GDCLASS(VisualScriptConstructor, VisualScriptNode) + GDCLASS(VisualScriptConstructor, VisualScriptNode); Variant::Type type; MethodInfo constructor; @@ -824,7 +825,7 @@ public: class VisualScriptLocalVar : public VisualScriptNode { - GDCLASS(VisualScriptLocalVar, VisualScriptNode) + GDCLASS(VisualScriptLocalVar, VisualScriptNode); StringName name; Variant::Type type; @@ -860,7 +861,7 @@ public: class VisualScriptLocalVarSet : public VisualScriptNode { - GDCLASS(VisualScriptLocalVarSet, VisualScriptNode) + GDCLASS(VisualScriptLocalVarSet, VisualScriptNode); StringName name; Variant::Type type; @@ -897,7 +898,8 @@ public: class VisualScriptInputAction : public VisualScriptNode { - GDCLASS(VisualScriptInputAction, VisualScriptNode) + GDCLASS(VisualScriptInputAction, VisualScriptNode); + public: enum Mode { MODE_PRESSED, @@ -944,7 +946,7 @@ VARIANT_ENUM_CAST(VisualScriptInputAction::Mode) class VisualScriptDeconstruct : public VisualScriptNode { - GDCLASS(VisualScriptDeconstruct, VisualScriptNode) + GDCLASS(VisualScriptDeconstruct, VisualScriptNode); struct Element { StringName name; diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index e5d12cb495..1e7ed3019c 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -87,38 +87,17 @@ void VisualScriptPropertySelector::_update_search() { TreeItem *root = search_options->create_item(); bool found = false; + StringName base = base_type; + List<StringName> base_list; + while (base) { + base_list.push_back(base); + base = ClassDB::get_parent_class_nocheck(base); + } - if (properties) { - + for (List<StringName>::Element *E = base_list.front(); E; E = E->next()) { + List<MethodInfo> methods; List<PropertyInfo> props; - - if (instance) { - instance->get_property_list(&props, true); - } else if (type != Variant::NIL) { - Variant v; - Variant::CallError ce; - v = Variant::construct(type, NULL, 0, ce); - - v.get_property_list(&props); - } else { - - Object *obj = ObjectDB::get_instance(script); - if (Object::cast_to<Script>(obj)) { - - props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); - Object::cast_to<Script>(obj)->get_script_property_list(&props); - } - - StringName base = base_type; - while (base) { - props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); - ClassDB::get_property_list(base, &props, true); - base = ClassDB::get_parent_class_nocheck(base); - } - } - TreeItem *category = NULL; - Ref<Texture> type_icons[Variant::VARIANT_MAX] = { Control::get_icon("Variant", "EditorIcons"), Control::get_icon("bool", "EditorIcons"), @@ -148,85 +127,163 @@ void VisualScriptPropertySelector::_update_search() { Control::get_icon("PoolVector3Array", "EditorIcons"), Control::get_icon("PoolColorArray", "EditorIcons") }; - - if (!seq_connect && !visual_script_generic) { - get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); - get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); - get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); - get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); - if (type == Variant::INT) { - get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); - } - if (type == Variant::BOOL) { - get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box); + { + String b = String(E->get()); + category = search_options->create_item(root); + category->set_text(0, b.replace_first("*", "")); + category->set_selectable(0, false); + Ref<Texture> icon; + String rep = b.replace("*", ""); + icon = EditorNode::get_singleton()->get_class_icon(rep); + category->set_icon(0, icon); + } + if (properties || seq_connect) { + if (instance) { + instance->get_property_list(&props, true); + } else { + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + Object::cast_to<Script>(obj)->get_script_property_list(&props); + } else { + ClassDB::get_property_list(E->get(), &props, true); + } } - if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) { - get_visual_node_names("operators/math/", Set<String>(), found, root, search_box); + for (List<PropertyInfo>::Element *F = props.front(); F; F = F->next()) { + if (!(F->get().usage & PROPERTY_USAGE_EDITOR) && !(F->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) + continue; + + if (type_filter.size() && type_filter.find(F->get().type) == -1) + continue; + + // capitalize() also converts underscore to space, we'll match again both possible styles + String get_text_raw = String(vformat(TTR("Get %s"), F->get().name)); + String get_text = get_text_raw.capitalize(); + String set_text_raw = String(vformat(TTR("Set %s"), F->get().name)); + String set_text = set_text_raw.capitalize(); + String input = search_box->get_text().capitalize(); + + if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, get_text); + item->set_metadata(0, F->get().name); + item->set_icon(0, type_icons[F->get().type]); + item->set_metadata(1, "get"); + item->set_collapsed(1); + item->set_selectable(0, true); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } + + if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, set_text); + item->set_metadata(0, F->get().name); + item->set_icon(0, type_icons[F->get().type]); + item->set_metadata(1, "set"); + item->set_selectable(0, true); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } } } + { + if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + v.get_method_list(&methods); + } else { - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().usage == PROPERTY_USAGE_CATEGORY) { - if (category && category->get_children() == NULL) { - memdelete(category); //old category was unused - } - category = search_options->create_item(root); - category->set_text(0, E->get().name); - category->set_selectable(0, false); + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + methods.push_back(MethodInfo("*Script Methods")); + Object::cast_to<Script>(obj)->get_script_method_list(&methods); - Ref<Texture> icon; - if (E->get().name == "Script Variables") { - icon = get_icon("Script", "EditorIcons"); } else { - icon = EditorNode::get_singleton()->get_class_icon(E->get().name); + methods.push_back(MethodInfo("*" + String(E->get()))); + ClassDB::get_method_list(E->get(), &methods, true, true); } - category->set_icon(0, icon); - continue; } + } + for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) { + + String name = M->get().name.get_slice(":", 0); + if (name.begins_with("_") && !(M->get().flags & METHOD_FLAG_VIRTUAL)) + continue; - if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) + if (virtuals_only && !(M->get().flags & METHOD_FLAG_VIRTUAL)) continue; - if (type_filter.size() && type_filter.find(E->get().type) == -1) + if (!virtuals_only && (M->get().flags & METHOD_FLAG_VIRTUAL)) continue; - // capitalize() also converts underscore to space, we'll match again both possible styles - String get_text_raw = String(vformat(TTR("Get %s"), E->get().name)); - String get_text = get_text_raw.capitalize(); - String set_text_raw = String(vformat(TTR("Set %s"), E->get().name)); - String set_text = set_text_raw.capitalize(); - String input = search_box->get_text().capitalize(); - - if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) { - TreeItem *item = search_options->create_item(category ? category : root); - item->set_text(0, get_text); - item->set_metadata(0, E->get().name); - item->set_icon(0, type_icons[E->get().type]); - item->set_metadata(1, "get"); - item->set_collapsed(1); - item->set_selectable(0, true); - item->set_selectable(1, false); - item->set_selectable(2, false); - item->set_metadata(2, connecting); + MethodInfo mi = M->get(); + String desc_arguments; + if (mi.arguments.size() > 0) { + desc_arguments = "("; + for (int i = 0; i < mi.arguments.size(); i++) { + + if (i > 0) { + desc_arguments += ", "; + } + if (mi.arguments[i].type == Variant::NIL) { + desc_arguments += "var"; + } else if (mi.arguments[i].name.find(":") != -1) { + desc_arguments += mi.arguments[i].name.get_slice(":", 1); + mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); + } else { + desc_arguments += Variant::get_type_name(mi.arguments[i].type); + } + } + desc_arguments += ")"; } + String desc_raw = mi.name + desc_arguments; + String desc = desc_raw.capitalize().replace("( ", "("); - if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) { - TreeItem *item = search_options->create_item(category ? category : root); - item->set_text(0, set_text); - item->set_metadata(0, E->get().name); - item->set_icon(0, type_icons[E->get().type]); - item->set_metadata(1, "set"); - item->set_selectable(0, true); - item->set_selectable(1, false); - item->set_selectable(2, false); - item->set_metadata(2, connecting); + if (search_box->get_text() != String() && + name.findn(search_box->get_text()) == -1 && + desc.findn(search_box->get_text()) == -1 && + desc_raw.findn(search_box->get_text()) == -1) { + continue; } + + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, desc); + item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); + item->set_metadata(0, name); + item->set_selectable(0, true); + + item->set_metadata(1, "method"); + item->set_collapsed(1); + item->set_selectable(1, false); + + item->set_selectable(2, false); + item->set_metadata(2, connecting); } if (category && category->get_children() == NULL) { memdelete(category); //old category was unused } } + if (properties) { + if (!seq_connect && !visual_script_generic) { + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); + get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); + if (type == Variant::INT) { + get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL) { + get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) { + get_visual_node_names("operators/math/", Set<String>(), found, root, search_box); + } + } + } if (seq_connect && !visual_script_generic) { String text = search_box->get_text(); @@ -240,127 +297,16 @@ void VisualScriptPropertySelector::_update_search() { get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); } - if (visual_script_generic) { + if ((properties || seq_connect) && visual_script_generic) { get_visual_node_names("", Set<String>(), found, root, search_box); } - List<MethodInfo> methods; - - if (type != Variant::NIL) { - Variant v; - Variant::CallError ce; - v = Variant::construct(type, NULL, 0, ce); - v.get_method_list(&methods); - } else { - - Object *obj = ObjectDB::get_instance(script); - if (Object::cast_to<Script>(obj)) { - - methods.push_back(MethodInfo("*Script Methods")); - Object::cast_to<Script>(obj)->get_script_method_list(&methods); - } - - StringName base = base_type; - while (base) { - methods.push_back(MethodInfo("*" + String(base))); - ClassDB::get_method_list(base, &methods, true, true); - base = ClassDB::get_parent_class_nocheck(base); - } - } - TreeItem *category = NULL; - bool script_methods = false; - - for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { - if (E->get().name.begins_with("*")) { - if (category && category->get_children() == NULL) { - memdelete(category); //old category was unused - } - category = search_options->create_item(root); - category->set_text(0, E->get().name.replace_first("*", "")); - category->set_selectable(0, false); - - Ref<Texture> icon; - script_methods = false; - String rep = E->get().name.replace("*", ""); - if (E->get().name == "*Script Methods") { - icon = get_icon("Script", "EditorIcons"); - script_methods = true; - } else { - icon = EditorNode::get_singleton()->get_class_icon(rep); - } - category->set_icon(0, icon); - - continue; - } - - String name = E->get().name.get_slice(":", 0); - if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL)) - continue; - - if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL)) - continue; - - if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL)) - continue; - - MethodInfo mi = E->get(); - String desc_arguments; - if (mi.arguments.size() > 0) { - desc_arguments = "("; - for (int i = 0; i < mi.arguments.size(); i++) { - - if (i > 0) { - desc_arguments += ", "; - } - if (mi.arguments[i].type == Variant::NIL) { - desc_arguments += "var"; - } else if (mi.arguments[i].name.find(":") != -1) { - desc_arguments += mi.arguments[i].name.get_slice(":", 1); - mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); - } else { - desc_arguments += Variant::get_type_name(mi.arguments[i].type); - } - } - desc_arguments += ")"; - } - String desc_raw = mi.name + desc_arguments; - String desc = desc_raw.capitalize().replace("( ", "("); - - if (search_box->get_text() != String() && - name.findn(search_box->get_text()) == -1 && - desc.findn(search_box->get_text()) == -1 && - desc_raw.findn(search_box->get_text()) == -1) { - continue; - } - - TreeItem *item = search_options->create_item(category ? category : root); - item->set_text(0, desc); - item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); - item->set_metadata(0, name); - item->set_selectable(0, true); - - item->set_metadata(1, "method"); - item->set_collapsed(1); - item->set_selectable(1, false); - - item->set_selectable(2, false); - item->set_metadata(2, connecting); - - if (category && category->get_children() == NULL) { - memdelete(category); //old category was unused - } - } - TreeItem *selected_item = search_options->search_item_text(search_box->get_text()); if (!found && selected_item != NULL) { selected_item->select(0); found = true; } - if (category && category->get_children() == NULL) { - memdelete(category); //old category was unused - } - get_ok()->set_disabled(root->get_children() == NULL); } @@ -421,7 +367,7 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt } Vector<String> desc = path[path.size() - 1].replace("(", "( ").replace(")", " )").replace(",", ", ").split(" "); - for (size_t i = 0; i < desc.size(); i++) { + for (int i = 0; i < desc.size(); i++) { desc.write[i] = desc[i].capitalize(); if (desc[i].ends_with(",")) { desc.write[i] = desc[i].replace(",", ", "); @@ -488,23 +434,23 @@ void VisualScriptPropertySelector::_item_selected() { while (at_class != String()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); - if (E) { - for (int i = 0; i < E->get().methods.size(); i++) { - if (E->get().methods[i].name == name) { - text = E->get().methods[i].description; + Map<String, DocData::ClassDoc>::Element *C = dd->class_list.find(at_class); + if (C) { + for (int i = 0; i < C->get().methods.size(); i++) { + if (C->get().methods[i].name == name) { + text = C->get().methods[i].description; } } } at_class = ClassDB::get_parent_class_nocheck(at_class); } - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); - if (E) { - for (int i = 0; i < E->get().methods.size(); i++) { + Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(class_type); + if (T) { + for (int i = 0; i < T->get().methods.size(); i++) { Vector<String> functions = name.rsplit("/", false, 1); - if (E->get().methods[i].name == functions[functions.size() - 1]) { - text = E->get().methods[i].description; + if (T->get().methods[i].name == functions[functions.size() - 1]) { + text = T->get().methods[i].description; } } } @@ -514,26 +460,26 @@ void VisualScriptPropertySelector::_item_selected() { if (names->find(name) != NULL) { Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name); if (operator_node.is_valid()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(operator_node->get_class_name()); - if (E) { + Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(operator_node->get_class_name()); + if (F) { text = Variant::get_operator_name(operator_node->get_operator()); } } Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name); if (typecast_node.is_valid()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(typecast_node->get_class_name()); - if (E) { - text = E->get().description; + Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(typecast_node->get_class_name()); + if (F) { + text = F->get().description; } } Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name); if (builtin_node.is_valid()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(builtin_node->get_class_name()); - if (E) { - for (int i = 0; i < E->get().constants.size(); i++) { - if (E->get().constants[i].value.to_int() == int(builtin_node->get_func())) { - text = E->get().constants[i].description; + Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(builtin_node->get_class_name()); + if (F) { + for (int i = 0; i < F->get().constants.size(); i++) { + if (F->get().constants[i].value.to_int() == int(builtin_node->get_func())) { + text = F->get().constants[i].description; } } } diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index f974ee3355..6235e4ba1d 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "scene/gui/rich_text_label.h" class VisualScriptPropertySelector : public ConfirmationDialog { - GDCLASS(VisualScriptPropertySelector, ConfirmationDialog) + GDCLASS(VisualScriptPropertySelector, ConfirmationDialog); LineEdit *search_box; Tree *search_options; diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index a21fff67fe..ebd0f0b3cb 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -431,7 +431,6 @@ void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const { Node *bnode = _get_base_node(); if (bnode) { property.hint_string = bnode->get_path(); //convert to loong string - } else { } } } diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h index 4009240581..647f2b1e2e 100644 --- a/modules/visual_script/visual_script_yield_nodes.h +++ b/modules/visual_script/visual_script_yield_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,8 @@ class VisualScriptYield : public VisualScriptNode { - GDCLASS(VisualScriptYield, VisualScriptNode) + GDCLASS(VisualScriptYield, VisualScriptNode); + public: enum YieldMode { YIELD_RETURN, @@ -84,7 +85,8 @@ VARIANT_ENUM_CAST(VisualScriptYield::YieldMode) class VisualScriptYieldSignal : public VisualScriptNode { - GDCLASS(VisualScriptYieldSignal, VisualScriptNode) + GDCLASS(VisualScriptYieldSignal, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 19587563ab..3824fdd789 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -40,11 +40,11 @@ if env['builtin_libvorbis']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_vorbis.Append(CPPPATH=[thirdparty_dir]) + env_vorbis.Prepend(CPPPATH=[thirdparty_dir]) # also requires libogg if env['builtin_libogg']: - env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) + env_vorbis.Prepend(CPPPATH=["#thirdparty/libogg"]) env_thirdparty = env_vorbis.Clone() env_thirdparty.disable_warnings() diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 5bc82f267f..2f4a45f108 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -103,7 +103,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { int todo = p_frames; - if (todo == 0 || todo < MIN_MIX) { + if (todo < MIN_MIX) { break; } @@ -143,8 +143,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { bool ok = ov_time_seek(&vf, loop_restart_time) == 0; if (!ok) { playing = false; - //ERR_EXPLAIN("loop restart time rejected"); - ERR_PRINT("loop restart time rejected") + ERR_PRINT("Loop restart time rejected"); } frames_mixed = stream_srate * loop_restart_time; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 73c4b5f3f4..a37867d9f9 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index 0ea1fbe8b2..bc6176a65f 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,16 +32,17 @@ #include "audio_stream_ogg_vorbis.h" -static ResourceFormatLoaderAudioStreamOGGVorbis *vorbis_stream_loader = NULL; +static Ref<ResourceFormatLoaderAudioStreamOGGVorbis> vorbis_stream_loader; void register_vorbis_types() { - vorbis_stream_loader = memnew(ResourceFormatLoaderAudioStreamOGGVorbis); + vorbis_stream_loader.instance(); ResourceLoader::add_resource_format_loader(vorbis_stream_loader); ClassDB::register_class<AudioStreamOGGVorbis>(); } void unregister_vorbis_types() { - memdelete(vorbis_stream_loader); + ResourceLoader::remove_resource_format_loader(vorbis_stream_loader); + vorbis_stream_loader.unref(); } diff --git a/modules/vorbis/register_types.h b/modules/vorbis/register_types.h index 3885502da2..0071a2d295 100644 --- a/modules/vorbis/register_types.h +++ b/modules/vorbis/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vorbis/stub/register_types.cpp b/modules/vorbis/stub/register_types.cpp index 02ac9518c8..c38967c60e 100644 --- a/modules/vorbis/stub/register_types.cpp +++ b/modules/vorbis/stub/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/vorbis/stub/register_types.h b/modules/vorbis/stub/register_types.h index 3885502da2..0071a2d295 100644 --- a/modules/vorbis/stub/register_types.h +++ b/modules/vorbis/stub/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/SCsub b/modules/webm/SCsub index cb35b926ab..e57437229f 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -15,22 +15,22 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_webm.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) +env_webm.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) # upstream uses c++11 if (not env_webm.msvc): - env_webm.Append(CCFLAGS="-std=c++11") + env_webm.Append(CXXFLAGS="-std=c++11") # also requires libogg, libvorbis and libopus if env['builtin_libogg']: - env_webm.Append(CPPPATH=["#thirdparty/libogg"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libogg"]) if env['builtin_libvorbis']: - env_webm.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libvorbis"]) if env['builtin_opus']: - env_webm.Append(CPPPATH=["#thirdparty/opus"]) + env_webm.Prepend(CPPPATH=["#thirdparty/opus"]) if env['builtin_libvpx']: - env_webm.Append(CPPPATH=["#thirdparty/libvpx"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libvpx"]) SConscript("libvpx/SCsub") env_thirdparty = env_webm.Clone() diff --git a/modules/webm/config.py b/modules/webm/config.py index 72a4073423..ba4dcce2f5 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -6,7 +6,6 @@ def configure(env): def get_doc_classes(): return [ - "ResourceImporterWebm", "VideoStreamWebm", ] diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index c02a7a8016..ff11bbb37d 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.1"> +<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.2"> <brief_description> </brief_description> <description> </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 98e38b9027..14fa6c1268 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -256,7 +256,7 @@ libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_source env_libvpx = env_modules.Clone() env_libvpx.disable_warnings() -env_libvpx.Append(CPPPATH=[libvpx_dir]) +env_libvpx.Prepend(CPPPATH=[libvpx_dir]) webm_multithread = env["platform"] != 'javascript' @@ -271,10 +271,10 @@ if env["platform"] == 'uwp': else: import platform is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and (platform.machine().startswith('arm') or platform.machine().startswith('aarch'))) - is_ios_x86 = (env["platform"] == 'iphone' and env["ios_sim"]) - is_android_x86 = (env["platform"] == 'android' and env["android_arch"] == 'x86') + is_ios_x86 = (env["platform"] == 'iphone' and ("arch" in env and env["arch"].startswith('x86'))) + is_android_x86 = (env["platform"] == 'android' and env["android_arch"].startswith('x86')) if is_android_x86: - cpu_bits = '32' + cpu_bits = '32' if env["android_arch"] == 'x86' else '64' webm_cpu_x86 = not is_x11_or_server_arm and (cpu_bits == '32' or cpu_bits == '64') and (env["platform"] == 'windows' or env["platform"] == 'x11' or env["platform"] == 'osx' or env["platform"] == 'haiku' or is_android_x86 or is_ios_x86) webm_cpu_arm = is_x11_or_server_arm or (not is_ios_x86 and env["platform"] == 'iphone') or (not is_android_x86 and env["platform"] == 'android') @@ -323,7 +323,7 @@ if webm_cpu_x86: elif cpu_bits == '64': env_libvpx["ASCPU"] = 'X86_64' - env_libvpx.Append(CCFLAGS=['-DWEBM_X86ASM']) + env_libvpx.Append(CPPDEFINES=['WEBM_X86ASM']) webm_simd_optimizations = True @@ -337,7 +337,7 @@ if webm_cpu_arm: env_libvpx["ASFLAGS"] = '' env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env_libvpx.Append(CCFLAGS=['-DWEBM_ARMASM']) + env_libvpx.Append(CPPDEFINES=['WEBM_ARMASM']) webm_simd_optimizations = True @@ -350,7 +350,7 @@ if webm_multithread: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt) if webm_cpu_x86: - is_clang_or_gcc = ('gcc' in os.path.basename(env["CC"])) or ('clang' in os.path.basename(env["CC"])) or ("OSXCROSS_ROOT" in os.environ) + is_clang_or_gcc = ('gcc' in os.path.basename(env["CC"])) or ('clang' in os.path.basename(env["CC"])) or ("osxcross" in env) env_libvpx_mmx = env_libvpx.Clone() if cpu_bits == '32' and is_clang_or_gcc: @@ -379,10 +379,11 @@ if webm_cpu_x86: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86_64asm) elif webm_cpu_arm: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm) + if env["platform"] == 'android': + env_libvpx.Prepend(CPPPATH=[libvpx_dir + "third_party/android"]) + env_libvpx.add_source_files(env.modules_sources, [libvpx_dir + "third_party/android/cpu-features.c"]) env_libvpx_neon = env_libvpx.Clone() - if env["platform"] == 'android' and env["android_arch"] == 'armv6': - env_libvpx_neon.Append(CCFLAGS=['-mfpu=neon']) env_libvpx_neon.add_source_files(env.modules_sources, libvpx_sources_arm_neon) if env["platform"] == 'uwp': diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp index 121b528d5b..ad8e2cf310 100644 --- a/modules/webm/register_types.cpp +++ b/modules/webm/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,11 @@ #include "video_stream_webm.h" -static ResourceFormatLoaderWebm *resource_loader_webm = NULL; +static Ref<ResourceFormatLoaderWebm> resource_loader_webm; void register_webm_types() { - resource_loader_webm = memnew(ResourceFormatLoaderWebm); + resource_loader_webm.instance(); ResourceLoader::add_resource_format_loader(resource_loader_webm, true); ClassDB::register_class<VideoStreamWebm>(); @@ -44,7 +44,6 @@ void register_webm_types() { void unregister_webm_types() { - if (resource_loader_webm) { - memdelete(resource_loader_webm); - } + ResourceLoader::remove_resource_format_loader(resource_loader_webm); + resource_loader_webm.unref(); } diff --git a/modules/webm/register_types.h b/modules/webm/register_types.h index 102da2b0f5..10811badaf 100644 --- a/modules/webm/register_types.h +++ b/modules/webm/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 675fc97b55..3670edc9ea 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #include "OpusVorbisDecoder.hpp" #include "VPXDecoder.hpp" +#include <vpx/vpx_image.h> #include "mkvparser/mkvparser.h" @@ -314,19 +315,37 @@ void VideoStreamPlaybackWebm::update(float p_delta) { PoolVector<uint8_t>::Write w = frame_data.write(); bool converted = false; - if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { + if (image.chromaShiftW == 0 && image.chromaShiftH == 0 && image.cs == VPX_CS_SRGB) { + + uint8_t *wp = w.ptr(); + unsigned char *rRow = image.planes[2]; + unsigned char *gRow = image.planes[0]; + unsigned char *bRow = image.planes[1]; + for (int i = 0; i < image.h; i++) { + for (int j = 0; j < image.w; j++) { + *wp++ = rRow[j]; + *wp++ = gRow[j]; + *wp++ = bRow[j]; + *wp++ = 255; + } + rRow += image.linesize[2]; + gRow += image.linesize[0]; + bRow += image.linesize[1]; + } + converted = true; + } else if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { - yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { - yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { - yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { @@ -375,7 +394,7 @@ int VideoStreamPlaybackWebm::get_mix_rate() const { inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { if (video_frames_pos > 0) { - const double audio_delay = AudioServer::get_singleton()->get_output_delay(); + const double audio_delay = AudioServer::get_singleton()->get_output_latency(); const double video_time = video_frames[video_frames_pos - 1]->time; return video_time >= time + audio_delay + delay_compensation; } @@ -383,7 +402,7 @@ inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { } bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) { - const double audio_delay = AudioServer::get_singleton()->get_output_delay(); + const double audio_delay = AudioServer::get_singleton()->get_output_latency(); return video_frame.time >= time + audio_delay + delay_compensation; } @@ -394,10 +413,11 @@ void VideoStreamPlaybackWebm::delete_pointers() { if (audio_frame) memdelete(audio_frame); - for (int i = 0; i < video_frames_capacity; ++i) - memdelete(video_frames[i]); - if (video_frames) + if (video_frames) { + for (int i = 0; i < video_frames_capacity; ++i) + memdelete(video_frames[i]); memfree(video_frames); + } if (video) memdelete(video); diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index 3739a73114..ddcbb1eb08 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,7 @@ class OpusVorbisDecoder; class VideoStreamPlaybackWebm : public VideoStreamPlayback { - GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback) + GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback); String file_name; int audio_track; diff --git a/modules/webp/SCsub b/modules/webp/SCsub index 8a4307fbe1..666628bb44 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -29,20 +29,17 @@ if env['builtin_libwebp']: "dsp/cost.c", "dsp/cost_mips32.c", "dsp/cost_mips_dsp_r2.c", + "dsp/cost_neon.c", "dsp/cost_sse2.c", "dsp/cpu.c", "dsp/dec.c", "dsp/dec_clip_tables.c", - "dsp/ssim.c", - "dsp/ssim_sse2.c", - "dsp/yuv_neon.c", "dsp/dec_mips32.c", "dsp/dec_mips_dsp_r2.c", "dsp/dec_msa.c", "dsp/dec_neon.c", "dsp/dec_sse2.c", "dsp/dec_sse41.c", - "dsp/enc_avx2.c", "dsp/enc.c", "dsp/enc_mips32.c", "dsp/enc_mips_dsp_r2.c", @@ -73,6 +70,8 @@ if env['builtin_libwebp']: "dsp/rescaler_msa.c", "dsp/rescaler_neon.c", "dsp/rescaler_sse2.c", + "dsp/ssim.c", + "dsp/ssim_sse2.c", "dsp/upsampling.c", "dsp/upsampling_mips_dsp_r2.c", "dsp/upsampling_msa.c", @@ -82,6 +81,7 @@ if env['builtin_libwebp']: "dsp/yuv.c", "dsp/yuv_mips32.c", "dsp/yuv_mips_dsp_r2.c", + "dsp/yuv_neon.c", "dsp/yuv_sse2.c", "dsp/yuv_sse41.c", "enc/alpha_enc.c", @@ -90,7 +90,6 @@ if env['builtin_libwebp']: "enc/backward_references_enc.c", "enc/config_enc.c", "enc/cost_enc.c", - "enc/delta_palettization_enc.c", "enc/filter_enc.c", "enc/frame_enc.c", "enc/histogram_enc.c", @@ -127,7 +126,7 @@ if env['builtin_libwebp']: ] thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources] - env_webp.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) + env_webp.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) env_thirdparty = env_webp.Clone() env_thirdparty.disable_warnings() diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp index 6734ae90d9..630c15f140 100644 --- a/modules/webp/image_loader_webp.cpp +++ b/modules/webp/image_loader_webp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -71,7 +71,7 @@ static PoolVector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_q w[3] = 'P'; copymem(&w[4], dst_buff, dst_size); free(dst_buff); - w = PoolVector<uint8_t>::Write(); + w.release(); return dst; } @@ -110,7 +110,7 @@ static Ref<Image> _webp_lossy_unpack(const PoolVector<uint8_t> &p_buffer) { //ERR_EXPLAIN("Error decoding webp! - "+p_file); ERR_FAIL_COND_V(errdec, Ref<Image>()); - dst_w = PoolVector<uint8_t>::Write(); + dst_w.release(); Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image)); return img; @@ -137,7 +137,7 @@ Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p } else { errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w.ptr(), datasize, 3 * features.width) == NULL; } - dst_w = PoolVector<uint8_t>::Write(); + dst_w.release(); //ERR_EXPLAIN("Error decoding webp!"); ERR_FAIL_COND_V(errdec, ERR_FILE_CORRUPT); @@ -171,8 +171,6 @@ Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_forc Error err = webp_load_image_from_buffer(p_image.ptr(), w.ptr(), src_image_len); - w = PoolVector<uint8_t>::Write(); - return err; } diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h index 256c787a16..0c4e54df09 100644 --- a/modules/webp/image_loader_webp.h +++ b/modules/webp/image_loader_webp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp index 9a2e83c65d..5055fa2034 100644 --- a/modules/webp/register_types.cpp +++ b/modules/webp/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webp/register_types.h b/modules/webp/register_types.h index 711f5be61d..bf76abf653 100644 --- a/modules/webp/register_types.h +++ b/modules/webp/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub new file mode 100644 index 0000000000..868553b879 --- /dev/null +++ b/modules/webrtc/SCsub @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +# Thirdparty source files + +env_webrtc = env_modules.Clone() +use_gdnative = env_webrtc["module_gdnative_enabled"] + +if use_gdnative: # GDNative is retained in Javascript for export compatibility + env_webrtc.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"]) + +env_webrtc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py new file mode 100644 index 0000000000..48b4c33c5d --- /dev/null +++ b/modules/webrtc/config.py @@ -0,0 +1,15 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "WebRTCPeerConnection", + "WebRTCDataChannel", + "WebRTCMultiplayer" + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml new file mode 100644 index 0000000000..98715ee99b --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCDataChannel" inherits="PacketPeer" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="close"> + <return type="void"> + </return> + <description> + Closes this data channel, notifying the other peer. + </description> + </method> + <method name="get_id" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the id assigned to this channel during creation (or auto-assigned during negotiation). + If the channel is not negotiated out-of-band the id will only be available after the connection is established (will return [code]65535[/code] until then). + </description> + </method> + <method name="get_label" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the label assigned to this channel during creation. + </description> + </method> + <method name="get_max_packet_life_time" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the [code]maxPacketLifeTime[/code] value assigned to this channel during creation. + Will be [code]65535[/code] if not specified. + </description> + </method> + <method name="get_max_retransmits" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the [code]maxRetransmits[/code] value assigned to this channel during creation. + Will be [code]65535[/code] if not specified. + </description> + </method> + <method name="get_protocol" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the sub-protocol assigned to this channel during creation. An empty string if not specified. + </description> + </method> + <method name="get_ready_state" qualifiers="const"> + <return type="int" enum="WebRTCDataChannel.ChannelState"> + </return> + <description> + Returns the current state of this channel, see [enum ChannelState]. + </description> + </method> + <method name="is_negotiated" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this channel was created with out-of-band configuration. + </description> + </method> + <method name="is_ordered" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this channel was created with ordering enabled (default). + </description> + </method> + <method name="poll"> + <return type="int" enum="Error"> + </return> + <description> + Reserved, but not used for now. + </description> + </method> + <method name="was_string_packet" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode]. + </description> + </method> + </methods> + <members> + <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode" default="1"> + The transfer mode to use when sending outgoing packet. Either text or binary. + </member> + </members> + <constants> + <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode"> + Tells the channel to send data over this channel as text. An external peer (non-Godot) would receive this as a string. + </constant> + <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode"> + Tells the channel to send data over this channel as binary. An external peer (non-Godot) would receive this as array buffer or blob. + </constant> + <constant name="STATE_CONNECTING" value="0" enum="ChannelState"> + The channel was created, but it's still trying to connect. + </constant> + <constant name="STATE_OPEN" value="1" enum="ChannelState"> + The channel is currently open, and data can flow over it. + </constant> + <constant name="STATE_CLOSING" value="2" enum="ChannelState"> + The channel is being closed, no new messages will be accepted, but those already in queue will be flushed. + </constant> + <constant name="STATE_CLOSED" value="3" enum="ChannelState"> + The channel was closed, or connection failed. + </constant> + </constants> +</class> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml new file mode 100644 index 0000000000..2b0622fffa --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCMultiplayer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2"> + <brief_description> + A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. + </brief_description> + <description> + This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer]. + You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. + [signal NetworkedMultiplayerPeer.connection_succeeded] and [signal NetworkedMultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [NetworkedMultiplayerPeer]. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_peer"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="peer" type="WebRTCPeerConnection"> + </argument> + <argument index="1" name="peer_id" type="int"> + </argument> + <argument index="2" name="unreliable_lifetime" type="int" default="1"> + </argument> + <description> + Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW]. + Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]). + </description> + </method> + <method name="close"> + <return type="void"> + </return> + <description> + Close all the add peer connections and channels, freeing all resources. + </description> + </method> + <method name="get_peer"> + <return type="Dictionary"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open). + </description> + </method> + <method name="get_peers"> + <return type="Dictionary"> + </return> + <description> + Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer] + </description> + </method> + <method name="has_peer"> + <return type="bool"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though). + </description> + </method> + <method name="initialize"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <argument index="1" name="server_compatibility" type="bool" default="false"> + </argument> + <description> + Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). + If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED] and [signal NetworkedMultiplayerPeer.connection_succeeded] will not be emitted. + If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal NetworkedMultiplayerPeer.peer_connected] signals until a peer with id [constant NetworkedMultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal NetworkedMultiplayerPeer.connection_succeeded]. After that the signal [signal NetworkedMultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal NetworkedMultiplayerPeer.server_disconnected] will be emitted and state will become [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED]. + </description> + </method> + <method name="remove_peer"> + <return type="void"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal NetworkedMultiplayerPeer.peer_connected] was emitted for it, then [signal NetworkedMultiplayerPeer.peer_disconnected] will be emitted. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml new file mode 100644 index 0000000000..26082d73a8 --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -0,0 +1,190 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2"> + <brief_description> + Interface to a WebRTC peer connection. + </brief_description> + <description> + A WebRTC connection between the local computer and a remote peer. Provides an interface to connect, maintain and monitor the connection. + Setting up a WebRTC connection between two peers from now on) may not seem a trivial task, but it can be broken down into 3 main steps: + - The peer that wants to initiate the connection ([code]A[/code] from now on) creates an offer and send it to the other peer ([code]B[/code] from now on). + - [code]B[/code] receives the offer, generate and answer, and sends it to [code]A[/code]). + - [code]A[/code] and [code]B[/code] then generates and exchange ICE candidates with each other. + After these steps, the connection should become connected. Keep on reading or look into the tutorial for more information. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_ice_candidate"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="media" type="String"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <description> + Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created]. + </description> + </method> + <method name="close"> + <return type="void"> + </return> + <description> + Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize]. + </description> + </method> + <method name="create_data_channel"> + <return type="WebRTCDataChannel"> + </return> + <argument index="0" name="label" type="String"> + </argument> + <argument index="1" name="options" type="Dictionary" default="{ + +}"> + </argument> + <description> + Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW]. + There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code]. + Valid [code]options[/code] are: + [codeblock] + { + "negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. data_channel_received will not be called. + "id": 1, # When "negotiated" is true this value must also be set to the same value on both peer. + + # Only one of maxRetransmits and maxPacketLifeTime can be specified, not both. They make the channel unreliable (but also better at real time). + "maxRetransmits": 1, # Specify the maximum number of attempt the peer will make to retransmits packets if they are not acknowledged. + "maxPacketLifeTime": 100, # Specify the maximum amount of time before giving up retransmitions of unacknowledged packets (in milliseconds). + "ordered": true, # When in unreliable mode (i.e. either "maxRetransmits" or "maxPacketLifetime" is set), "ordered" (true by default) specify if packet ordering is to be enforced. + + "protocol": "my-custom-protocol", # A custom sub-protocol string for this channel. + } + [/codeblock] + [b]Note:[/b] You must keep a reference to channels created this way, or it will be closed. + </description> + </method> + <method name="create_offer"> + <return type="int" enum="Error"> + </return> + <description> + Creates a new SDP offer to start a WebRTC connection with a remote peer. At least one [WebRTCDataChannel] must have been created before calling this method. + If this functions returns [constant OK], [signal session_description_created] will be called when the session is ready to be sent. + </description> + </method> + <method name="get_connection_state" qualifiers="const"> + <return type="int" enum="WebRTCPeerConnection.ConnectionState"> + </return> + <description> + Returns the connection state. See [enum ConnectionState]. + </description> + </method> + <method name="initialize"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="configuration" type="Dictionary" default="{ + +}"> + </argument> + <description> + Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection. + Valid [code]options[/code] are: + [codeblock] + { + "iceServers": [ + { + "urls": [ "stun:stun.example.com:3478" ], # One or more STUN servers. + }, + { + "urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers. + "username": "a_username", # Optional username for the TURN server. + "credentials": "a_password", # Optional password for the TURN server. + } + ] + } + [/codeblock] + </description> + </method> + <method name="poll"> + <return type="int" enum="Error"> + </return> + <description> + Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals. + </description> + </method> + <method name="set_local_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + Sets the SDP description of the local peer. This should be called in response to [signal session_description_created]. + If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. + </description> + </method> + <method name="set_remote_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server. + If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer. + If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. + </description> + </method> + </methods> + <signals> + <signal name="data_channel_received"> + <argument index="0" name="channel" type="Object"> + </argument> + <description> + Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default). + The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel] + </description> + </signal> + <signal name="ice_candidate_created"> + <argument index="0" name="media" type="String"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <description> + Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server. + </description> + </signal> + <signal name="session_description_created"> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server. + </description> + </signal> + </signals> + <constants> + <constant name="STATE_NEW" value="0" enum="ConnectionState"> + The connection is new, data channels and an offer can be created in this state. + </constant> + <constant name="STATE_CONNECTING" value="1" enum="ConnectionState"> + The peer is connecting, ICE is in progress, none of the transports has failed. + </constant> + <constant name="STATE_CONNECTED" value="2" enum="ConnectionState"> + The peer is connected, all ICE transports are connected. + </constant> + <constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState"> + At least one ICE transport is disconnected. + </constant> + <constant name="STATE_FAILED" value="4" enum="ConnectionState"> + One or more of the ICE transports failed. + </constant> + <constant name="STATE_CLOSED" value="5" enum="ConnectionState"> + The peer connection is closed (after calling [method close] for example). + </constant> + </constants> +</class> diff --git a/modules/mono/editor/mono_build_info.cpp b/modules/webrtc/register_types.cpp index e4af2aac4f..58b68d926b 100644 --- a/modules/mono/editor/mono_build_info.cpp +++ b/modules/webrtc/register_types.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* mono_build_info.cpp */ +/* register_types.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,35 +28,34 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "mono_build_info.h" - -#include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono_utils.h" - -uint32_t MonoBuildInfo::Hasher::hash(const MonoBuildInfo &p_key) { - - uint32_t hash = 0; - - GDMonoUtils::hash_combine(hash, p_key.solution.hash()); - GDMonoUtils::hash_combine(hash, p_key.configuration.hash()); - - return hash; -} - -bool MonoBuildInfo::operator==(const MonoBuildInfo &p_b) const { - - return p_b.solution == solution && p_b.configuration == configuration; -} - -String MonoBuildInfo::get_log_dirpath() { - - return GodotSharpDirs::get_build_logs_dir().plus_file(solution.md5_text() + "_" + configuration); +#include "register_types.h" +#include "webrtc_data_channel.h" +#include "webrtc_peer_connection.h" + +#ifdef JAVASCRIPT_ENABLED +#include "emscripten.h" +#include "webrtc_peer_connection_js.h" +#endif +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "webrtc_data_channel_gdnative.h" +#include "webrtc_peer_connection_gdnative.h" +#endif +#include "webrtc_multiplayer.h" + +void register_webrtc_types() { +#ifdef JAVASCRIPT_ENABLED + WebRTCPeerConnectionJS::make_default(); +#elif defined(WEBRTC_GDNATIVE_ENABLED) + WebRTCPeerConnectionGDNative::make_default(); +#endif + + ClassDB::register_custom_instance_class<WebRTCPeerConnection>(); +#ifdef WEBRTC_GDNATIVE_ENABLED + ClassDB::register_class<WebRTCPeerConnectionGDNative>(); + ClassDB::register_class<WebRTCDataChannelGDNative>(); +#endif + ClassDB::register_virtual_class<WebRTCDataChannel>(); + ClassDB::register_class<WebRTCMultiplayer>(); } -MonoBuildInfo::MonoBuildInfo() {} - -MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) { - - solution = p_solution; - configuration = p_config; -} +void unregister_webrtc_types() {} diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h new file mode 100644 index 0000000000..4923547a95 --- /dev/null +++ b/modules/webrtc/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_webrtc_types(); +void unregister_webrtc_types(); diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp new file mode 100644 index 0000000000..2bd30e68f5 --- /dev/null +++ b/modules/webrtc/webrtc_data_channel.cpp @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* webrtc_data_channel.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_data_channel.h" + +void WebRTCDataChannel::_bind_methods() { + ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll); + ClassDB::bind_method(D_METHOD("close"), &WebRTCDataChannel::close); + + ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCDataChannel::was_string_packet); + ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCDataChannel::set_write_mode); + ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCDataChannel::get_write_mode); + ClassDB::bind_method(D_METHOD("get_ready_state"), &WebRTCDataChannel::get_ready_state); + ClassDB::bind_method(D_METHOD("get_label"), &WebRTCDataChannel::get_label); + ClassDB::bind_method(D_METHOD("is_ordered"), &WebRTCDataChannel::is_ordered); + ClassDB::bind_method(D_METHOD("get_id"), &WebRTCDataChannel::get_id); + ClassDB::bind_method(D_METHOD("get_max_packet_life_time"), &WebRTCDataChannel::get_max_packet_life_time); + ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits); + ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol); + ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode"); + + BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); + BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); + + BIND_ENUM_CONSTANT(STATE_CONNECTING); + BIND_ENUM_CONSTANT(STATE_OPEN); + BIND_ENUM_CONSTANT(STATE_CLOSING); + BIND_ENUM_CONSTANT(STATE_CLOSED); +} + +WebRTCDataChannel::WebRTCDataChannel() { +} + +WebRTCDataChannel::~WebRTCDataChannel() { +} diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h new file mode 100644 index 0000000000..0b161da784 --- /dev/null +++ b/modules/webrtc/webrtc_data_channel.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* webrtc_data_channel.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_DATA_CHANNEL_H +#define WEBRTC_DATA_CHANNEL_H + +#include "core/io/packet_peer.h" + +class WebRTCDataChannel : public PacketPeer { + GDCLASS(WebRTCDataChannel, PacketPeer); + +public: + enum WriteMode { + WRITE_MODE_TEXT, + WRITE_MODE_BINARY, + }; + + enum ChannelState { + STATE_CONNECTING, + STATE_OPEN, + STATE_CLOSING, + STATE_CLOSED + }; + +protected: + static void _bind_methods(); + +public: + virtual void set_write_mode(WriteMode mode) = 0; + virtual WriteMode get_write_mode() const = 0; + virtual bool was_string_packet() const = 0; + + virtual ChannelState get_ready_state() const = 0; + virtual String get_label() const = 0; + virtual bool is_ordered() const = 0; + virtual int get_id() const = 0; + virtual int get_max_packet_life_time() const = 0; + virtual int get_max_retransmits() const = 0; + virtual String get_protocol() const = 0; + virtual bool is_negotiated() const = 0; + + virtual Error poll() = 0; + virtual void close() = 0; + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const = 0; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; + + virtual int get_max_packet_size() const = 0; + + WebRTCDataChannel(); + ~WebRTCDataChannel(); +}; + +VARIANT_ENUM_CAST(WebRTCDataChannel::WriteMode); +VARIANT_ENUM_CAST(WebRTCDataChannel::ChannelState); +#endif // WEBRTC_DATA_CHANNEL_H diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp new file mode 100644 index 0000000000..6362634626 --- /dev/null +++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp @@ -0,0 +1,137 @@ +/*************************************************************************/ +/* webrtc_data_channel_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#include "webrtc_data_channel_gdnative.h" +#include "core/io/resource_loader.h" +#include "modules/gdnative/nativescript/nativescript.h" + +void WebRTCDataChannelGDNative::_bind_methods() { + ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY); +} + +WebRTCDataChannelGDNative::WebRTCDataChannelGDNative() { + interface = NULL; +} + +WebRTCDataChannelGDNative::~WebRTCDataChannelGDNative() { +} + +Error WebRTCDataChannelGDNative::poll() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->poll(interface->data); +} + +void WebRTCDataChannelGDNative::close() { + ERR_FAIL_COND(interface == NULL); + interface->close(interface->data); +} + +void WebRTCDataChannelGDNative::set_write_mode(WriteMode p_mode) { + ERR_FAIL_COND(interface == NULL); + interface->set_write_mode(interface->data, p_mode); +} + +WebRTCDataChannel::WriteMode WebRTCDataChannelGDNative::get_write_mode() const { + ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY); + return (WriteMode)interface->get_write_mode(interface->data); +} + +bool WebRTCDataChannelGDNative::was_string_packet() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->was_string_packet(interface->data); +} + +WebRTCDataChannel::ChannelState WebRTCDataChannelGDNative::get_ready_state() const { + ERR_FAIL_COND_V(interface == NULL, STATE_CLOSED); + return (ChannelState)interface->get_ready_state(interface->data); +} + +String WebRTCDataChannelGDNative::get_label() const { + ERR_FAIL_COND_V(interface == NULL, ""); + return String(interface->get_label(interface->data)); +} + +bool WebRTCDataChannelGDNative::is_ordered() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->is_ordered(interface->data); +} + +int WebRTCDataChannelGDNative::get_id() const { + ERR_FAIL_COND_V(interface == NULL, -1); + return interface->get_id(interface->data); +} + +int WebRTCDataChannelGDNative::get_max_packet_life_time() const { + ERR_FAIL_COND_V(interface == NULL, -1); + return interface->get_max_packet_life_time(interface->data); +} + +int WebRTCDataChannelGDNative::get_max_retransmits() const { + ERR_FAIL_COND_V(interface == NULL, -1); + return interface->get_max_retransmits(interface->data); +} + +String WebRTCDataChannelGDNative::get_protocol() const { + ERR_FAIL_COND_V(interface == NULL, ""); + return String(interface->get_protocol(interface->data)); +} + +bool WebRTCDataChannelGDNative::is_negotiated() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->is_negotiated(interface->data); +} + +Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); +} + +Error WebRTCDataChannelGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); +} + +int WebRTCDataChannelGDNative::get_max_packet_size() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_max_packet_size(interface->data); +} + +int WebRTCDataChannelGDNative::get_available_packet_count() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_packet_count(interface->data); +} + +void WebRTCDataChannelGDNative::set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl) { + interface = p_impl; +} + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h new file mode 100644 index 0000000000..3685f86353 --- /dev/null +++ b/modules/webrtc/webrtc_data_channel_gdnative.h @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* webrtc_data_channel_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H +#define WEBRTC_DATA_CHANNEL_GDNATIVE_H + +#include "modules/gdnative/include/net/godot_net.h" +#include "webrtc_data_channel.h" + +class WebRTCDataChannelGDNative : public WebRTCDataChannel { + GDCLASS(WebRTCDataChannelGDNative, WebRTCDataChannel); + +protected: + static void _bind_methods(); + +private: + const godot_net_webrtc_data_channel *interface; + +public: + void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl); + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + + virtual ChannelState get_ready_state() const; + virtual String get_label() const; + virtual bool is_ordered() const; + virtual int get_id() const; + virtual int get_max_packet_life_time() const; + virtual int get_max_retransmits() const; + virtual String get_protocol() const; + virtual bool is_negotiated() const; + + virtual Error poll(); + virtual void close(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + WebRTCDataChannelGDNative(); + ~WebRTCDataChannelGDNative(); +}; + +#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp new file mode 100644 index 0000000000..069918cc9c --- /dev/null +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* webrtc_data_channel_js.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_data_channel_js.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); + peer->_on_error(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); + peer->_on_open(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); + peer->_on_close(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); + peer->_on_message(p_data, p_size, p_is_string); +} +} + +void WebRTCDataChannelJS::_on_open() { + in_buffer.resize(16); +} + +void WebRTCDataChannelJS::_on_close() { + close(); +} + +void WebRTCDataChannelJS::_on_error() { + close(); +} + +void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) { + if (in_buffer.space_left() < (int)(p_size + 5)) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL(); + } + + uint8_t is_string = p_is_string ? 1 : 0; + in_buffer.write((uint8_t *)&p_size, 4); + in_buffer.write((uint8_t *)&is_string, 1); + in_buffer.write(p_data, p_size); + queue_count++; +} + +void WebRTCDataChannelJS::close() { + in_buffer.resize(0); + queue_count = 0; + _was_string = false; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + if (!dict) return; + var channel = dict["channel"]; + channel.onopen = null; + channel.onclose = null; + channel.onerror = null; + channel.onmessage = null; + channel.close(); + }, _js_id); + /* clang-format on */ +} + +Error WebRTCDataChannelJS::poll() { + return OK; +} + +WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const { + /* clang-format off */ + return (ChannelState) EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict) return 3; // CLOSED + var channel = dict["channel"]; + switch(channel.readyState) { + case "connecting": + return 0; + case "open": + return 1; + case "closing": + return 2; + case "closed": + return 3; + } + return 3; // CLOSED + }, _js_id); + /* clang-format on */ +} + +int WebRTCDataChannelJS::get_available_packet_count() const { + return queue_count; +} + +Error WebRTCDataChannelJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED); + + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + in_buffer.read((uint8_t *)&to_read, 4); + --queue_count; + left = in_buffer.data_left(); + + if (left < to_read + 1) { + in_buffer.advance_read(left); + return FAILED; + } + + in_buffer.read(&is_string, 1); + _was_string = is_string == 1; + in_buffer.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + + return OK; +} + +Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED); + + int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0; + + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var channel = dict["channel"]; + var bytes_array = new Uint8Array($2); + var i = 0; + + for(i=0; i<$2; i++) { + bytes_array[i] = getValue($1+i, 'i8'); + } + + if ($3) { + channel.send(bytes_array.buffer); + } else { + var string = new TextDecoder("utf-8").decode(bytes_array); + channel.send(string); + } + }, _js_id, p_buffer, p_buffer_size, is_bin); + /* clang-format on */ + + return OK; +} + +int WebRTCDataChannelJS::get_max_packet_size() const { + return 1200; +} + +void WebRTCDataChannelJS::set_write_mode(WriteMode p_mode) { + _write_mode = p_mode; +} + +WebRTCDataChannel::WriteMode WebRTCDataChannelJS::get_write_mode() const { + return _write_mode; +} + +bool WebRTCDataChannelJS::was_string_packet() const { + return _was_string; +} + +String WebRTCDataChannelJS::get_label() const { + return _label; +} + +/* clang-format off */ +#define _JS_GET(PROP, DEF) \ +EM_ASM_INT({ \ + var dict = Module.IDHandler.get($0); \ + if (!dict || !dict["channel"]) { \ + return DEF; \ + } \ + var out = dict["channel"].PROP; \ + return out === null ? DEF : out; \ +}, _js_id) +/* clang-format on */ + +bool WebRTCDataChannelJS::is_ordered() const { + return _JS_GET(ordered, true); +} + +int WebRTCDataChannelJS::get_id() const { + return _JS_GET(id, 65535); +} + +int WebRTCDataChannelJS::get_max_packet_life_time() const { + // Can't use macro, webkit workaround. + /* clang-format off */ + return EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict || !dict["channel"]) { + return 65535; + } + if (dict["channel"].maxRetransmitTime !== undefined) { + // Guess someone didn't appreciate the standardization process. + return dict["channel"].maxRetransmitTime; + } + var out = dict["channel"].maxPacketLifeTime; + return out === null ? 65535 : out; + }, _js_id); + /* clang-format on */ +} + +int WebRTCDataChannelJS::get_max_retransmits() const { + return _JS_GET(maxRetransmits, 65535); +} + +String WebRTCDataChannelJS::get_protocol() const { + return _protocol; +} + +bool WebRTCDataChannelJS::is_negotiated() const { + return _JS_GET(negotiated, false); +} + +WebRTCDataChannelJS::WebRTCDataChannelJS() { + queue_count = 0; + _was_string = false; + _write_mode = WRITE_MODE_BINARY; + _js_id = 0; +} + +WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) { + queue_count = 0; + _was_string = false; + _write_mode = WRITE_MODE_BINARY; + _js_id = js_id; + + /* clang-format off */ + EM_ASM({ + var c_ptr = $0; + var dict = Module.IDHandler.get($1); + if (!dict) return; + var channel = dict["channel"]; + dict["ptr"] = c_ptr; + + channel.binaryType = "arraybuffer"; + channel.onopen = function (evt) { + ccall("_emrtc_on_ch_open", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onclose = function (evt) { + ccall("_emrtc_on_ch_close", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onerror = function (evt) { + ccall("_emrtc_on_ch_error", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onmessage = function(event) { + var buffer; + var is_string = 0; + if (event.data instanceof ArrayBuffer) { + buffer = new Uint8Array(event.data); + } else if (event.data instanceof Blob) { + console.error("Blob type not supported"); + return; + } else if (typeof event.data === "string") { + is_string = 1; + var enc = new TextEncoder("utf-8"); + buffer = new Uint8Array(enc.encode(event.data)); + } else { + console.error("Unknown message type"); + return; + } + var len = buffer.length*buffer.BYTES_PER_ELEMENT; + var out = Module._malloc(len); + Module.HEAPU8.set(buffer, out); + ccall("_emrtc_on_ch_message", + "void", + ["number", "number", "number", "number"], + [c_ptr, out, len, is_string] + ); + Module._free(out); + } + + }, this, js_id); + // Parse label + char *str; + str = (char *)EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict || !dict["channel"]) return 0; + var str = dict["channel"].label; + var len = lengthBytesUTF8(str)+1; + var ptr = _malloc(str); + stringToUTF8(str, ptr, len+1); + return ptr; + }, js_id); + if(str != NULL) { + _label.parse_utf8(str); + EM_ASM({ _free($0) }, str); + } + str = (char *)EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict || !dict["channel"]) return 0; + var str = dict["channel"].protocol; + var len = lengthBytesUTF8(str)+1; + var ptr = _malloc(str); + stringToUTF8(str, ptr, len+1); + return ptr; + }, js_id); + if(str != NULL) { + _protocol.parse_utf8(str); + EM_ASM({ _free($0) }, str); + } + /* clang-format on */ +} + +WebRTCDataChannelJS::~WebRTCDataChannelJS() { + close(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; +#endif diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h new file mode 100644 index 0000000000..b87f8e9326 --- /dev/null +++ b/modules/webrtc/webrtc_data_channel_js.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* webrtc_data_channel_js.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#ifndef WEBRTC_DATA_CHANNEL_JS_H +#define WEBRTC_DATA_CHANNEL_JS_H + +#include "webrtc_data_channel.h" + +class WebRTCDataChannelJS : public WebRTCDataChannel { + GDCLASS(WebRTCDataChannelJS, WebRTCDataChannel); + +private: + String _label; + String _protocol; + + bool _was_string; + WriteMode _write_mode; + + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type + }; + + int _js_id; + RingBuffer<uint8_t> in_buffer; + int queue_count; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + +public: + void _on_open(); + void _on_close(); + void _on_error(); + void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string); + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + + virtual ChannelState get_ready_state() const; + virtual String get_label() const; + virtual bool is_ordered() const; + virtual int get_id() const; + virtual int get_max_packet_life_time() const; + virtual int get_max_retransmits() const; + virtual String get_protocol() const; + virtual bool is_negotiated() const; + + virtual Error poll(); + virtual void close(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + WebRTCDataChannelJS(); + WebRTCDataChannelJS(int js_id); + ~WebRTCDataChannelJS(); +}; + +#endif // WEBRTC_DATA_CHANNEL_JS_H + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp new file mode 100644 index 0000000000..17dafff93a --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer.cpp @@ -0,0 +1,384 @@ +/*************************************************************************/ +/* webrtc_multiplayer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_multiplayer.h" + +#include "core/io/marshalls.h" +#include "core/os/os.h" + +void WebRTCMultiplayer::_bind_methods() { + ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayer::initialize, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayer::add_peer, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayer::remove_peer); + ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayer::has_peer); + ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayer::get_peer); + ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayer::get_peers); + ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayer::close); +} + +void WebRTCMultiplayer::set_transfer_mode(TransferMode p_mode) { + transfer_mode = p_mode; +} + +NetworkedMultiplayerPeer::TransferMode WebRTCMultiplayer::get_transfer_mode() const { + return transfer_mode; +} + +void WebRTCMultiplayer::set_target_peer(int p_peer_id) { + target_peer = p_peer_id; +} + +/* Returns the ID of the NetworkedMultiplayerPeer who sent the most recent packet: */ +int WebRTCMultiplayer::get_packet_peer() const { + return next_packet_peer; +} + +bool WebRTCMultiplayer::is_server() const { + return unique_id == TARGET_PEER_SERVER; +} + +void WebRTCMultiplayer::poll() { + if (peer_map.size() == 0) + return; + + List<int> remove; + List<int> add; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + Ref<ConnectedPeer> peer = E->get(); + peer->connection->poll(); + // Check peer state + switch (peer->connection->get_connection_state()) { + case WebRTCPeerConnection::STATE_NEW: + case WebRTCPeerConnection::STATE_CONNECTING: + // Go to next peer, not ready yet. + continue; + case WebRTCPeerConnection::STATE_CONNECTED: + // Good to go, go ahead and check channel state. + break; + default: + // Peer is closed or in error state. Got to next peer. + remove.push_back(E->key()); + continue; + } + // Check channels state + int ready = 0; + for (List<Ref<WebRTCDataChannel> >::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) { + Ref<WebRTCDataChannel> ch = C->get(); + switch (ch->get_ready_state()) { + case WebRTCDataChannel::STATE_CONNECTING: + continue; + case WebRTCDataChannel::STATE_OPEN: + ready++; + continue; + default: + // Channel was closed or in error state, remove peer id. + remove.push_back(E->key()); + } + // We got a closed channel break out, the peer will be removed. + break; + } + // This peer has newly connected, and all channels are now open. + if (ready == peer->channels.size() && !peer->connected) { + peer->connected = true; + add.push_back(E->key()); + } + } + // Remove disconnected peers + for (List<int>::Element *E = remove.front(); E; E = E->next()) { + remove_peer(E->get()); + if (next_packet_peer == E->get()) + next_packet_peer = 0; + } + // Signal newly connected peers + for (List<int>::Element *E = add.front(); E; E = E->next()) { + // Already connected to server: simply notify new peer. + // NOTE: Mesh is always connected. + if (connection_status == CONNECTION_CONNECTED) + emit_signal("peer_connected", E->get()); + + // Server emulation mode suppresses peer_conencted until server connects. + if (server_compat && E->get() == TARGET_PEER_SERVER) { + // Server connected. + connection_status = CONNECTION_CONNECTED; + emit_signal("peer_connected", TARGET_PEER_SERVER); + emit_signal("connection_succeeded"); + // Notify of all previously connected peers + for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) { + if (F->key() != 1 && F->get()->connected) + emit_signal("peer_connected", F->key()); + } + break; // Because we already notified of all newly added peers. + } + } + // Fetch next packet + if (next_packet_peer == 0) + _find_next_peer(); +} + +void WebRTCMultiplayer::_find_next_peer() { + Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.find(next_packet_peer); + if (E) E = E->next(); + // After last. + while (E) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + E = E->next(); + } + E = peer_map.front(); + // Before last + while (E) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + if (E->key() == (int)next_packet_peer) + break; + E = E->next(); + } + // No packet found + next_packet_peer = 0; +} + +void WebRTCMultiplayer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool WebRTCMultiplayer::is_refusing_new_connections() const { + return refuse_connections; +} + +NetworkedMultiplayerPeer::ConnectionStatus WebRTCMultiplayer::get_connection_status() const { + return connection_status; +} + +Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) { + ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); + unique_id = p_self_id; + server_compat = p_server_compat; + + // Mesh and server are always connected + if (!server_compat || p_self_id == 1) + connection_status = CONNECTION_CONNECTED; + else + connection_status = CONNECTION_CONNECTING; + return OK; +} + +int WebRTCMultiplayer::get_unique_id() const { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1); + return unique_id; +} + +void WebRTCMultiplayer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) { + Array channels; + for (List<Ref<WebRTCDataChannel> >::Element *F = p_connected_peer->channels.front(); F; F = F->next()) { + channels.push_back(F->get()); + } + r_dict["connection"] = p_connected_peer->connection; + r_dict["connected"] = p_connected_peer->connected; + r_dict["channels"] = channels; +} + +bool WebRTCMultiplayer::has_peer(int p_peer_id) { + return peer_map.has(p_peer_id); +} + +Dictionary WebRTCMultiplayer::get_peer(int p_peer_id) { + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary()); + Dictionary out; + _peer_to_dict(peer_map[p_peer_id], out); + return out; +} + +Dictionary WebRTCMultiplayer::get_peers() { + Dictionary out; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + Dictionary d; + _peer_to_dict(E->get(), d); + out[E->key()] = d; + } + return out; +} + +Error WebRTCMultiplayer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) { + ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED); + // Peer must be valid, and in new state (to create data channels) + ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER); + + Ref<ConnectedPeer> peer = memnew(ConnectedPeer); + peer->connection = p_peer; + + // Initialize data channels + Dictionary cfg; + cfg["negotiated"] = true; + cfg["ordered"] = true; + + cfg["id"] = 1; + peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED); + + cfg["id"] = 2; + cfg["maxPacketLifetime"] = p_unreliable_lifetime; + peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED); + + cfg["id"] = 3; + cfg["ordered"] = false; + peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED); + + peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map + + return OK; +} + +void WebRTCMultiplayer::remove_peer(int p_peer_id) { + ERR_FAIL_COND(!peer_map.has(p_peer_id)); + Ref<ConnectedPeer> peer = peer_map[p_peer_id]; + peer_map.erase(p_peer_id); + if (peer->connected) { + peer->connected = false; + emit_signal("peer_disconnected", p_peer_id); + if (server_compat && p_peer_id == TARGET_PEER_SERVER) { + emit_signal("server_disconnected"); + connection_status = CONNECTION_DISCONNECTED; + } + } +} + +Error WebRTCMultiplayer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + // Peer not available + if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) { + _find_next_peer(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + for (List<Ref<WebRTCDataChannel> >::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) { + if (E->get()->get_available_packet_count()) { + Error err = E->get()->get_packet(r_buffer, r_buffer_size); + _find_next_peer(); + return err; + } + } + // Channels for that peer were empty. Bug? + _find_next_peer(); + ERR_FAIL_V(ERR_BUG); +} + +Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); + + int ch = CH_RELIABLE; + switch (transfer_mode) { + case TRANSFER_MODE_RELIABLE: + ch = CH_RELIABLE; + break; + case TRANSFER_MODE_UNRELIABLE_ORDERED: + ch = CH_ORDERED; + break; + case TRANSFER_MODE_UNRELIABLE: + ch = CH_UNRELIABLE; + break; + } + + Map<int, Ref<ConnectedPeer> >::Element *E = NULL; + + if (target_peer > 0) { + + E = peer_map.find(target_peer); + if (!E) { + ERR_EXPLAIN("Invalid Target Peer: " + itos(target_peer)); + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG); + ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG); + return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + + } else { + int exclude = -target_peer; + + for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) { + + // Exclude packet. If target_peer == 0 then don't exclude any packets + if (target_peer != 0 && F->key() == exclude) + continue; + + ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); + F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + } + } + return OK; +} + +int WebRTCMultiplayer::get_available_packet_count() const { + if (next_packet_peer == 0) + return 0; // To be sure next call to get_packet works if size > 0 . + int size = 0; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + size += F->get()->get_available_packet_count(); + } + } + return size; +} + +int WebRTCMultiplayer::get_max_packet_size() const { + return 1200; +} + +void WebRTCMultiplayer::close() { + peer_map.clear(); + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + connection_status = CONNECTION_DISCONNECTED; +} + +WebRTCMultiplayer::WebRTCMultiplayer() { + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + transfer_mode = TRANSFER_MODE_RELIABLE; + refuse_connections = false; + connection_status = CONNECTION_DISCONNECTED; + server_compat = false; +} + +WebRTCMultiplayer::~WebRTCMultiplayer() { + close(); +} diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h new file mode 100644 index 0000000000..82bbfd4f68 --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* webrtc_multiplayer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_MULTIPLAYER_H +#define WEBRTC_MULTIPLAYER_H + +#include "core/io/networked_multiplayer_peer.h" +#include "webrtc_peer_connection.h" + +class WebRTCMultiplayer : public NetworkedMultiplayerPeer { + + GDCLASS(WebRTCMultiplayer, NetworkedMultiplayerPeer); + +protected: + static void _bind_methods(); + +private: + enum { + CH_RELIABLE = 0, + CH_ORDERED = 1, + CH_UNRELIABLE = 2, + CH_RESERVED_MAX = 3 + }; + + class ConnectedPeer : public Reference { + + public: + Ref<WebRTCPeerConnection> connection; + List<Ref<WebRTCDataChannel> > channels; + bool connected; + + ConnectedPeer() { + connected = false; + for (int i = 0; i < CH_RESERVED_MAX; i++) + channels.push_front(Ref<WebRTCDataChannel>()); + } + }; + + uint32_t unique_id; + int target_peer; + int client_count; + bool refuse_connections; + ConnectionStatus connection_status; + TransferMode transfer_mode; + int next_packet_peer; + bool server_compat; + + Map<int, Ref<ConnectedPeer> > peer_map; + + void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict); + void _find_next_peer(); + +public: + WebRTCMultiplayer(); + ~WebRTCMultiplayer(); + + Error initialize(int p_self_id, bool p_server_compat = false); + Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1); + void remove_peer(int p_peer_id); + bool has_peer(int p_peer_id); + Dictionary get_peer(int p_peer_id); + Dictionary get_peers(); + void close(); + + // PacketPeer + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + int get_available_packet_count() const; + int get_max_packet_size() const; + + // NetworkedMultiplayerPeer + void set_transfer_mode(TransferMode p_mode); + TransferMode get_transfer_mode() const; + void set_target_peer(int p_peer_id); + + int get_unique_id() const; + int get_packet_peer() const; + + bool is_server() const; + + void poll(); + + void set_refuse_new_connections(bool p_enable); + bool is_refusing_new_connections() const; + + ConnectionStatus get_connection_status() const; +}; + +#endif diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp new file mode 100644 index 0000000000..69c7a51a40 --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection.cpp @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* webrtc_peer_connection.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_peer_connection.h" + +WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = NULL; + +Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() { + + return create(); +} + +WebRTCPeerConnection *WebRTCPeerConnection::create() { + + if (!_create) + return NULL; + return _create(); +} + +void WebRTCPeerConnection::_bind_methods() { + ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary())); + ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary())); + ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer); + ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeerConnection::set_local_description); + ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeerConnection::set_remote_description); + ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeerConnection::add_ice_candidate); + ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeerConnection::poll); + ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close); + + ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state); + + ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp"))); + ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel"))); + + BIND_ENUM_CONSTANT(STATE_NEW); + BIND_ENUM_CONSTANT(STATE_CONNECTING); + BIND_ENUM_CONSTANT(STATE_CONNECTED); + BIND_ENUM_CONSTANT(STATE_DISCONNECTED); + BIND_ENUM_CONSTANT(STATE_FAILED); + BIND_ENUM_CONSTANT(STATE_CLOSED); +} + +WebRTCPeerConnection::WebRTCPeerConnection() { +} + +WebRTCPeerConnection::~WebRTCPeerConnection() { +} diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h new file mode 100644 index 0000000000..7be1390dab --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* webrtc_peer_connection.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_PEER_CONNECTION_H +#define WEBRTC_PEER_CONNECTION_H + +#include "core/io/packet_peer.h" +#include "modules/webrtc/webrtc_data_channel.h" + +class WebRTCPeerConnection : public Reference { + GDCLASS(WebRTCPeerConnection, Reference); + +public: + enum ConnectionState { + STATE_NEW, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTED, + STATE_FAILED, + STATE_CLOSED + }; + +protected: + static void _bind_methods(); + static WebRTCPeerConnection *(*_create)(); + +public: + virtual ConnectionState get_connection_state() const = 0; + + virtual Error initialize(Dictionary p_config = Dictionary()) = 0; + virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0; + virtual Error create_offer() = 0; + virtual Error set_remote_description(String type, String sdp) = 0; + virtual Error set_local_description(String type, String sdp) = 0; + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0; + virtual Error poll() = 0; + virtual void close() = 0; + + static Ref<WebRTCPeerConnection> create_ref(); + static WebRTCPeerConnection *create(); + + WebRTCPeerConnection(); + ~WebRTCPeerConnection(); +}; + +VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState); +#endif // WEBRTC_PEER_CONNECTION_H diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp new file mode 100644 index 0000000000..af98aa750a --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection_gdnative.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* webrtc_peer_connection_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#include "webrtc_peer_connection_gdnative.h" + +#include "core/io/resource_loader.h" +#include "modules/gdnative/nativescript/nativescript.h" +#include "webrtc_data_channel_gdnative.h" + +const godot_net_webrtc_library *WebRTCPeerConnectionGDNative::default_library = NULL; + +Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_library *p_lib) { + if (default_library) { + const godot_net_webrtc_library *old = default_library; + default_library = NULL; + old->unregistered(); + } + default_library = p_lib; + return OK; // Maybe add version check and fail accordingly +} + +WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() { + + WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative); + ERR_EXPLAIN("Default GDNative WebRTC implementation not defined."); + ERR_FAIL_COND_V(!default_library, obj); + + // Call GDNative constructor + Error err = (Error)default_library->create_peer_connection(obj); + ERR_EXPLAIN("GDNative default library constructor returned an error"); + ERR_FAIL_COND_V(err != OK, obj); + + return obj; +} + +void WebRTCPeerConnectionGDNative::_bind_methods() { +} + +WebRTCPeerConnectionGDNative::WebRTCPeerConnectionGDNative() { + interface = NULL; +} + +WebRTCPeerConnectionGDNative::~WebRTCPeerConnectionGDNative() { +} + +Error WebRTCPeerConnectionGDNative::initialize(Dictionary p_config) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->initialize(interface->data, (const godot_dictionary *)&p_config); +} + +Ref<WebRTCDataChannel> WebRTCPeerConnectionGDNative::create_data_channel(String p_label, Dictionary p_options) { + ERR_FAIL_COND_V(interface == NULL, NULL); + return (WebRTCDataChannel *)interface->create_data_channel(interface->data, p_label.utf8().get_data(), (const godot_dictionary *)&p_options); +} + +Error WebRTCPeerConnectionGDNative::create_offer() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->create_offer(interface->data); +} + +Error WebRTCPeerConnectionGDNative::set_local_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerConnectionGDNative::set_remote_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerConnectionGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); +} + +Error WebRTCPeerConnectionGDNative::poll() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->poll(interface->data); +} + +void WebRTCPeerConnectionGDNative::close() { + ERR_FAIL_COND(interface == NULL); + interface->close(interface->data); +} + +WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionGDNative::get_connection_state() const { + ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED); + return (ConnectionState)interface->get_connection_state(interface->data); +} + +void WebRTCPeerConnectionGDNative::set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl) { + interface = p_impl; +} + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h new file mode 100644 index 0000000000..0a281c3d89 --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection_gdnative.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* webrtc_peer_connection_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H +#define WEBRTC_PEER_CONNECTION_GDNATIVE_H + +#include "modules/gdnative/include/net/godot_net.h" +#include "webrtc_peer_connection.h" + +class WebRTCPeerConnectionGDNative : public WebRTCPeerConnection { + GDCLASS(WebRTCPeerConnectionGDNative, WebRTCPeerConnection); + +protected: + static void _bind_methods(); + static WebRTCPeerConnection *_create(); + +private: + static const godot_net_webrtc_library *default_library; + const godot_net_webrtc_peer_connection *interface; + +public: + static Error set_default_library(const godot_net_webrtc_library *p_library); + static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionGDNative::_create; } + + void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl); + + virtual ConnectionState get_connection_state() const; + + virtual Error initialize(Dictionary p_config = Dictionary()); + virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()); + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + virtual void close(); + + WebRTCPeerConnectionGDNative(); + ~WebRTCPeerConnectionGDNative(); +}; + +#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp new file mode 100644 index 0000000000..9758ab3644 --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection_js.cpp @@ -0,0 +1,314 @@ +/*************************************************************************/ +/* webrtc_peer_connection_js.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer_connection_js.h" + +#include "webrtc_data_channel_js.h" + +#include "core/io/json.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) { + WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); + peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) { + WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); + peer->emit_signal("session_description_created", String(p_type), String(p_offer)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) { + WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); + peer->_on_connection_state_changed(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() { + ERR_PRINT("RTCPeerConnection error!"); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) { + WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); + peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id))); +} +} + +void _emrtc_create_pc(int p_id, const Dictionary &p_config) { + String config = JSON::print(p_config); + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var c_ptr = dict["ptr"]; + var config = JSON.parse(UTF8ToString($1)); + // Setup local connaction + var conn = null; + try { + conn = new RTCPeerConnection(config); + } catch (e) { + console.log(e); + return; + } + conn.oniceconnectionstatechange = function(event) { + if (!Module.IDHandler.get($0)) return; + ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]); + }; + conn.onicecandidate = function(event) { + if (!Module.IDHandler.get($0)) return; + if (!event.candidate) return; + + var c = event.candidate; + // should emit on ice candidate + ccall("_emrtc_on_ice_candidate", + "void", + ["number", "string", "number", "string"], + [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate] + ); + }; + conn.ondatachannel = function (evt) { + var dict = Module.IDHandler.get($0); + if (!dict) { + return; + } + var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null}); + ccall("_emrtc_emit_channel", + "void", + ["number", "number"], + [c_ptr, id] + ); + }; + dict["conn"] = conn; + }, p_id, config.utf8().get_data()); + /* clang-format on */ +} + +void WebRTCPeerConnectionJS::_on_connection_state_changed() { + /* clang-format off */ + _conn_state = (ConnectionState)EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict) return 5; // CLOSED + var conn = dict["conn"]; + switch(conn.iceConnectionState) { + case "new": + return 0; + case "checking": + return 1; + case "connected": + case "completed": + return 2; + case "disconnected": + return 3; + case "failed": + return 4; + case "closed": + return 5; + } + return 5; // CLOSED + }, _js_id); + /* clang-format on */ +} + +void WebRTCPeerConnectionJS::close() { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + if (!dict) return; + if (dict["conn"]) { + dict["conn"].close(); + } + }, _js_id); + /* clang-format on */ + _conn_state = STATE_CLOSED; +} + +Error WebRTCPeerConnectionJS::create_offer() { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + + _conn_state = STATE_CONNECTING; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var onError = function(error) { + console.error(error); + ccall("_emrtc_on_error", "void", [], []); + }; + var onCreated = function(offer) { + ccall("_emrtc_session_description_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + conn.createOffer().then(onCreated).catch(onError); + }, _js_id); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + var onError = function(error) { + console.error(error); + ccall("_emrtc_on_error", "void", [], []); + }; + conn.setLocalDescription({ + "sdp": sdp, + "type": type + }).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) { + if (type == "offer") { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + _conn_state = STATE_CONNECTING; + } + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + + var onError = function(error) { + console.error(error); + ccall("_emrtc_on_error", "void", [], []); + }; + var onCreated = function(offer) { + ccall("_emrtc_session_description_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + var onSet = function() { + if (type != "offer") { + return; + } + conn.createAnswer().then(onCreated); + }; + conn.setRemoteDescription({ + "sdp": sdp, + "type": type + }).then(onSet).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var sdpMidName = UTF8ToString($1); + var sdpMlineIndexName = UTF8ToString($2); + var sdpName = UTF8ToString($3); + conn.addIceCandidate(new RTCIceCandidate({ + "candidate": sdpName, + "sdpMid": sdpMidName, + "sdpMlineIndex": sdpMlineIndexName + })); + }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) { + _emrtc_create_pc(_js_id, p_config); + return OK; +} + +Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) { + String config = JSON::print(p_channel_config); + /* clang-format off */ + int id = EM_ASM_INT({ + try { + var dict = Module.IDHandler.get($0); + if (!dict) return 0; + var label = UTF8ToString($1); + var config = JSON.parse(UTF8ToString($2)); + var conn = dict["conn"]; + return Module.IDHandler.add({ + "channel": conn.createDataChannel(label, config), + "ptr": null + }) + } catch (e) { + return 0; + } + }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data()); + /* clang-format on */ + ERR_FAIL_COND_V(id == 0, NULL); + return memnew(WebRTCDataChannelJS(id)); +} + +Error WebRTCPeerConnectionJS::poll() { + return OK; +} + +WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const { + return _conn_state; +} + +WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() { + _conn_state = STATE_NEW; + + /* clang-format off */ + _js_id = EM_ASM_INT({ + return Module.IDHandler.add({"conn": null, "ptr": $0}); + }, this); + /* clang-format on */ + Dictionary config; + _emrtc_create_pc(_js_id, config); +} + +WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() { + close(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; +#endif diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h new file mode 100644 index 0000000000..43c0e3d6ee --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection_js.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* webrtc_peer_connection_js.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_PEER_CONNECTION_JS_H +#define WEBRTC_PEER_CONNECTION_JS_H + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer_connection.h" + +class WebRTCPeerConnectionJS : public WebRTCPeerConnection { + +private: + int _js_id; + ConnectionState _conn_state; + +public: + static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); } + static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; } + + void _on_connection_state_changed(); + virtual ConnectionState get_connection_state() const; + + virtual Error initialize(Dictionary configuration = Dictionary()); + virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()); + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + virtual void close(); + + WebRTCPeerConnectionJS(); + ~WebRTCPeerConnectionJS(); +}; + +#endif + +#endif // WEBRTC_PEER_CONNECTION_JS_H diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index b67a836fe8..033169411f 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -5,89 +5,26 @@ Import('env_modules') # Thirdparty source files -env_lws = env_modules.Clone() - -if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript - thirdparty_dir = "#thirdparty/libwebsockets/" - helper_dir = "win32helpers/" - thirdparty_sources = [ - - "core/alloc.c", - "core/context.c", - "core/libwebsockets.c", - "core/output.c", - "core/pollfd.c", - "core/service.c", - - "event-libs/poll/poll.c", - - "misc/base64-decode.c", - "misc/lejp.c", - "misc/sha-1.c", - - "roles/h1/ops-h1.c", - "roles/http/header.c", - "roles/http/client/client.c", - "roles/http/client/client-handshake.c", - "roles/http/server/fops-zip.c", - "roles/http/server/lejp-conf.c", - "roles/http/server/parsers.c", - "roles/http/server/server.c", - "roles/listen/ops-listen.c", - "roles/pipe/ops-pipe.c", - "roles/raw/ops-raw.c", - - "roles/ws/client-ws.c", - "roles/ws/client-parser-ws.c", - "roles/ws/ops-ws.c", - "roles/ws/server-ws.c", - - "tls/tls.c", - "tls/tls-client.c", - "tls/tls-server.c", - - "tls/mbedtls/wrapper/library/ssl_cert.c", - "tls/mbedtls/wrapper/library/ssl_pkey.c", - "tls/mbedtls/wrapper/library/ssl_stack.c", - "tls/mbedtls/wrapper/library/ssl_methods.c", - "tls/mbedtls/wrapper/library/ssl_lib.c", - "tls/mbedtls/wrapper/library/ssl_x509.c", - "tls/mbedtls/wrapper/platform/ssl_port.c", - "tls/mbedtls/wrapper/platform/ssl_pm.c", - "tls/mbedtls/lws-genhash.c", - "tls/mbedtls/mbedtls-client.c", - "tls/mbedtls/lws-genrsa.c", - "tls/mbedtls/ssl.c", - "tls/mbedtls/mbedtls-server.c" +env_ws = env_modules.Clone() + +if env['builtin_wslay'] and not env["platform"] == "javascript": # already builtin for javascript + wslay_dir = "#thirdparty/wslay/" + wslay_sources = [ + "wslay_net.c", + "wslay_event.c", + "wslay_queue.c", + "wslay_stack.c", + "wslay_frame.c", ] - - if env["platform"] == "android": # Builtin getifaddrs - thirdparty_sources += ["misc/getifaddrs.c"] - - if env["platform"] == "windows" or env["platform"] == "uwp": # Winsock - thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"] - else: # Unix socket - thirdparty_sources += ["plat/lws-plat-unix.c"] - - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - - env_lws.Append(CPPPATH=[thirdparty_dir]) - - if env['builtin_mbedtls']: - mbedtls_includes = "#thirdparty/mbedtls/include" - env_lws.Prepend(CPPPATH=[mbedtls_includes]) - - wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] - env_lws.Prepend(CPPPATH=wrapper_includes) - + wslay_sources = [wslay_dir + s for s in wslay_sources] + env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"]) + env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"]) if env["platform"] == "windows" or env["platform"] == "uwp": - env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) - - if env["platform"] == "uwp": - env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) - - env_thirdparty = env_lws.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) - -env_lws.add_source_files(env.modules_sources, "*.cpp") + env_ws.Append(CPPDEFINES=["HAVE_WINSOCK2_H"]) + else: + env_ws.Append(CPPDEFINES=["HAVE_NETINET_IN_H"]) + env_wslay = env_ws.Clone() + env_wslay.disable_warnings() + env_wslay.add_source_files(env.modules_sources, wslay_sources) + +env_ws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index b3ea535495..c3baf9de83 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -1,18 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1"> +<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.2"> <brief_description> - A WebSocket client implementation + A WebSocket client implementation. </brief_description> <description> - This class implements a WebSocket client compatible with any RFC 6455 complaint WebSocket server. + This class implements a WebSocket client compatible with any RFC 6455-compliant WebSocket server. This client can be optionally used as a network peer for the [MultiplayerAPI]. After starting the client ([method connect_to_url]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). - You will received appropriate signals when connecting, disconnecting, or when new data is available. + You will receive appropriate signals when connecting, disconnecting, or when new data is available. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_url"> <return type="int" enum="Error"> @@ -24,8 +22,9 @@ <argument index="2" name="gd_mp_api" type="bool" default="false"> </argument> <description> - Connect to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. - If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true. + Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. + If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. + If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]). </description> </method> <method name="disconnect_from_host"> @@ -36,13 +35,14 @@ <argument index="1" name="reason" type="String" default=""""> </argument> <description> - Disconnect this client from the connected host. See [method WebSocketPeer.close] for more info. + Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information. </description> </method> </methods> <members> <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled"> - Enable or disable SSL certificate verification. Note: You must specify the certificates to be used in the project settings for it to work when exported. + If [code]true[/code], SSL certificate verification is enabled. + [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported. </member> </members> <signals> @@ -67,7 +67,8 @@ </signal> <signal name="data_received"> <description> - Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer. + Emitted when a WebSocket message is received. + [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer. </description> </signal> <signal name="server_close_request"> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 1a841f85ed..b80a28e648 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> +<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2"> <brief_description> Base class for WebSocket server and client. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_peer" qualifiers="const"> <return type="WebSocketPeer"> @@ -20,13 +18,32 @@ Returns the [WebSocketPeer] associated to the given [code]peer_id[/code]. </description> </method> + <method name="set_buffers"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="input_buffer_size_kb" type="int"> + </argument> + <argument index="1" name="input_max_packets" type="int"> + </argument> + <argument index="2" name="output_buffer_size_kb" type="int"> + </argument> + <argument index="3" name="output_max_packets" type="int"> + </argument> + <description> + Configures the buffer sizes for this WebSocket peer. Default values can be specified in the Project Settings under [code]network/limits[/code]. For server, values are meant per connected peer. + The first two parameters define the size and queued packets limits of the input buffer, the last two of the output buffer. + Buffer sizes are expressed in KiB, so [code]4 = 2^12 = 4096 bytes[/code]. All parameters will be rounded up to the nearest power of two. + [b]Note:[/b] HTML5 exports only use the input buffer since the output one is managed by browsers. + </description> + </method> </methods> <signals> <signal name="peer_packet"> <argument index="0" name="peer_source" type="int"> </argument> <description> - Emitted when a packet is received from a peer. Note: this signal is only emitted when the client or server is configured to use Godot multiplayer API. + Emitted when a packet is received from a peer. + [b]Note:[/b] This signal is only emitted when the client or server is configured to use Godot multiplayer API. </description> </signal> </signals> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index cc59706916..dd95f7432e 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.1"> +<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.2"> <brief_description> A class representing a specific WebSocket connection. </brief_description> @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> @@ -20,30 +18,32 @@ <argument index="1" name="reason" type="String" default=""""> </argument> <description> - Close this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC6455 section 7.4 for a list of valid status codes). [reason] is the human readable reason for closing the connection (can be any UTF8 string, must be less than 123 bytes). - Note: To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received. - Note: HTML5 export might not support all status codes. Please refer to browsers-specific documentation for more details. + Closes this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [code]reason[/code] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes). + [b]Note:[/b] To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received. + [b]Note:[/b] The HTML5 export might not support all status codes. Please refer to browser-specific documentation for more details. </description> </method> <method name="get_connected_host" qualifiers="const"> <return type="String"> </return> <description> - Returns the IP Address of the connected peer. (Not available in HTML5 export) + Returns the IP address of the connected peer. + [b]Note:[/b] Not available in the HTML5 export. </description> </method> <method name="get_connected_port" qualifiers="const"> <return type="int"> </return> <description> - Returns the remote port of the connected peer. (Not available in HTML5 export) + Returns the remote port of the connected peer. + [b]Note:[/b] Not available in the HTML5 export. </description> </method> <method name="get_write_mode" qualifiers="const"> <return type="int" enum="WebSocketPeer.WriteMode"> </return> <description> - Get the current selected write mode. See [enum WriteMode]. + Gets the current selected write mode. See [enum WriteMode]. </description> </method> <method name="is_connected_to_host" qualifiers="const"> @@ -72,10 +72,10 @@ </methods> <constants> <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode"> - Specify that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed). + Specifies that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed). </constant> <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode"> - Specify that WebSockets messages should be transferred as binary payload (any byte combination is allowed). + Specifies that WebSockets messages should be transferred as binary payload (any byte combination is allowed). </constant> </constants> </class> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index ba66fdd89b..63318e5874 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1"> +<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.2"> <brief_description> - A WebSocket server implementation + A WebSocket server implementation. </brief_description> <description> - This class implements a WebSocket server that can also support the high level multiplayer API. + This class implements a WebSocket server that can also support the high-level multiplayer API. After starting the server ([method listen]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal. - Note: This class will not work in HTML5 exports due to browser restrictions. + [b]Note:[/b] This class will not work in HTML5 exports due to browser restrictions. </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="disconnect_peer"> <return type="void"> @@ -23,7 +21,7 @@ <argument index="2" name="reason" type="String" default=""""> </argument> <description> - Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more info. + Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information. </description> </method> <method name="get_peer_address" qualifiers="const"> @@ -70,16 +68,17 @@ <argument index="2" name="gd_mp_api" type="bool" default="false"> </argument> <description> - Start listening on the given port. - You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used. - You can use this server as a network peer for [MultiplayerAPI] by passing [code]true[/code] as [code]gd_mp_api[/code]. Note: [signal data_received] will not be fired and clients other than Godot will not work in this case. + Starts listening on the given port. + You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested. + If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a network peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted. + If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.), on the [WebSocketPeer] returned via [code]get_peer(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]). </description> </method> <method name="stop"> <return type="void"> </return> <description> - Stop the server and clear its state. + Stops the server and clear its state. </description> </method> </methods> @@ -117,7 +116,8 @@ <argument index="0" name="id" type="int"> </argument> <description> - Emitted when a new message is received. Note: This signal is NOT emitted when used as high level multiplayer peer. + Emitted when a new message is received. + [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer. </description> </signal> </signals> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 82a577790e..409cc9f699 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef JAVASCRIPT_ENABLED #include "emws_client.h" @@ -204,9 +205,15 @@ int EMWSClient::get_max_packet_size() const { return (1 << _in_buf_size) - PROTO_SIZE; } +Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + return OK; +} + EMWSClient::EMWSClient() { - _in_buf_size = GLOBAL_GET(WSC_IN_BUF); - _in_pkt_size = GLOBAL_GET(WSC_IN_PKT); + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); _is_connecting = false; _peer = Ref<EMWSPeer>(memnew(EMWSPeer)); /* clang-format off */ diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index a21090a1a3..1811d05eea 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef EMWSCLIENT_H #define EMWSCLIENT_H @@ -48,6 +49,7 @@ private: public: bool _is_connecting; + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index bb97934824..a9f38f1a82 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef JAVASCRIPT_ENABLED #include "emws_peer.h" diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index 4beb86d45b..6ceb69a1b7 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef EMWSPEER_H #define EMWSPEER_H diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 09f9c1ceec..c4bb459ad0 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef JAVASCRIPT_ENABLED #include "emws_server.h" @@ -78,6 +79,10 @@ int EMWSServer::get_max_packet_size() const { return 0; } +Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + return OK; +} + EMWSServer::EMWSServer() { } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 2dc455c389..a5e5b4090e 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef EMWSSERVER_H #define EMWSSERVER_H @@ -41,6 +42,7 @@ class EMWSServer : public WebSocketServer { GDCIIMPL(EMWSServer, WebSocketServer); public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp deleted file mode 100644 index fa0bb4cfb2..0000000000 --- a/modules/websocket/lws_client.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/*************************************************************************/ -/* lws_client.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef JAVASCRIPT_ENABLED - -#include "lws_client.h" -#include "core/io/ip.h" -#include "core/io/stream_peer_ssl.h" -#include "core/project_settings.h" -#include "tls/mbedtls/wrapper/include/openssl/ssl.h" - -Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { - - ERR_FAIL_COND_V(context != NULL, FAILED); - - IP_Address addr; - - if (!p_host.is_valid_ip_address()) { - addr = IP::get_singleton()->resolve_hostname(p_host); - } else { - addr = p_host; - } - - ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); - - // Prepare protocols - _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref); - - // Init lws client - struct lws_context_creation_info info; - struct lws_client_connect_info i; - - memset(&i, 0, sizeof i); - memset(&info, 0, sizeof info); - - info.port = CONTEXT_PORT_NO_LISTEN; - info.protocols = _lws_ref->lws_structs; - info.gid = -1; - info.uid = -1; - //info.ws_ping_pong_interval = 5; - info.user = _lws_ref; -#if defined(LWS_OPENSSL_SUPPORT) - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; -#endif - context = lws_create_context(&info); - - if (context == NULL) { - _lws_free_ref(_lws_ref); - _lws_ref = NULL; - ERR_EXPLAIN("Unable to create lws context"); - ERR_FAIL_V(FAILED); - } - - i.context = context; - if (p_protocols.size() > 0) - i.protocol = _lws_ref->lws_names; - else - i.protocol = NULL; - - if (p_ssl) { - i.ssl_connection = LCCSCF_USE_SSL; - if (!verify_ssl) - i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; - } else { - i.ssl_connection = 0; - } - - // These CharStrings needs to survive till we call lws_client_connect_via_info - CharString addr_ch = ((String)addr).ascii(); - CharString host_ch = p_host.utf8(); - CharString path_ch = p_path.utf8(); - i.address = addr_ch.get_data(); - i.host = host_ch.get_data(); - i.path = path_ch.get_data(); - i.port = p_port; - - lws_client_connect_via_info(&i); - - return OK; -}; - -int LWSClient::get_max_packet_size() const { - return (1 << _out_buf_size) - PROTO_SIZE; -} - -void LWSClient::poll() { - - _lws_poll(); -} - -int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - - Ref<LWSPeer> peer = static_cast<Ref<LWSPeer> >(_peer); - LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; - - switch (reason) { - case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: { - PoolByteArray arr = StreamPeerSSL::get_project_cert_array(); - if (arr.size() > 0) - SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size())); - else if (verify_ssl) - WARN_PRINTS("No CA cert specified in project settings, SSL will not work"); - } break; - - case LWS_CALLBACK_CLIENT_ESTABLISHED: - peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); - peer_data->peer_id = 0; - peer_data->force_close = false; - peer_data->clean_close = false; - _on_connect(lws_get_protocol(wsi)->name); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - _on_error(); - destroy_context(); - return -1; // We should close the connection (would probably happen anyway) - - case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { - int code; - String reason = peer->get_close_reason(in, len, code); - peer_data->clean_close = true; - _on_close_request(code, reason); - return 0; - } - - case LWS_CALLBACK_CLIENT_CLOSED: - peer->close(); - destroy_context(); - _on_disconnect(peer_data->clean_close); - return 0; // We can end here - - case LWS_CALLBACK_CLIENT_RECEIVE: - peer->read_wsi(in, len); - if (peer->get_available_packet_count() > 0) - _on_peer_packet(); - break; - - case LWS_CALLBACK_CLIENT_WRITEABLE: - if (peer_data->force_close) { - peer->send_close_status(wsi); - return -1; - } - - peer->write_wsi(); - break; - - default: - break; - } - - return 0; -} - -Ref<WebSocketPeer> LWSClient::get_peer(int p_peer_id) const { - - return _peer; -} - -NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() const { - - if (context == NULL) - return CONNECTION_DISCONNECTED; - - if (_peer->is_connected_to_host()) - return CONNECTION_CONNECTED; - - return CONNECTION_CONNECTING; -} - -void LWSClient::disconnect_from_host(int p_code, String p_reason) { - - if (context == NULL) - return; - - _peer->close(p_code, p_reason); -}; - -IP_Address LWSClient::get_connected_host() const { - - return IP_Address(); -}; - -uint16_t LWSClient::get_connected_port() const { - - return 1025; -}; - -LWSClient::LWSClient() { - _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; - _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); - _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10; - _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1); - - context = NULL; - _lws_ref = NULL; - _peer = Ref<LWSPeer>(memnew(LWSPeer)); -}; - -LWSClient::~LWSClient() { - - invalidate_lws_ref(); // We do not want any more callback - disconnect_from_host(); - destroy_context(); - _peer = Ref<LWSPeer>(); -}; - -#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_helper.cpp b/modules/websocket/lws_helper.cpp deleted file mode 100644 index b5216615e9..0000000000 --- a/modules/websocket/lws_helper.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************/ -/* lws_helper.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#if !defined(JAVASCRIPT_ENABLED) - -#include "lws_helper.h" - -_LWSRef *_lws_create_ref(void *obj) { - - _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); - out->is_destroying = false; - out->free_context = false; - out->is_polling = false; - out->obj = obj; - out->is_valid = true; - out->lws_structs = NULL; - out->lws_names = NULL; - return out; -} - -void _lws_free_ref(_LWSRef *ref) { - // Free strings and structs - memfree(ref->lws_structs); - memfree(ref->lws_names); - // Free ref - memfree(ref); -} - -bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { - if (context == NULL || ref->is_destroying) - return false; - - if (ref->is_polling) { - ref->free_context = true; - return false; - } - - ref->is_destroying = true; - lws_context_destroy(context); - _lws_free_ref(ref); - return true; -} - -bool _lws_poll(struct lws_context *context, _LWSRef *ref) { - - ERR_FAIL_COND_V(context == NULL, false); - ERR_FAIL_COND_V(ref == NULL, false); - - ref->is_polling = true; - lws_service(context, 0); - ref->is_polling = false; - - if (!ref->free_context) - return false; // Nothing to do - - bool is_valid = ref->is_valid; // Might have been destroyed by poll - - _lws_destroy(context, ref); // Will destroy context and ref - - return is_valid; // If the object should NULL its context and ref -} - -/* - * Prepare the protocol_structs to be fed to context. - * Also prepare the protocol string used by the client. - */ -void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { - // The input strings might go away after this call, we need to copy them. - // We will clear them when destroying the context. - int i; - int len = p_names.size(); - size_t data_size = sizeof(struct LWSPeer::PeerData); - PoolVector<String>::Read pnr = p_names.read(); - - // This is a reference connecting the object with lws keep track of status, mallocs, etc. - // Must survive as long the context. - // Must be freed manually when context creation fails. - _LWSRef *ref = _lws_create_ref(p_obj); - - // LWS protocol structs. - ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); - memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2)); - - CharString strings = p_names.join(",").ascii(); - int str_len = strings.length(); - - // Joined string of protocols, double the size: comma separated first, NULL separated last - ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator - - char *names_ptr = ref->lws_names; - struct lws_protocols *structs_ptr = ref->lws_structs; - - // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header - if (str_len > 0) - copymem(names_ptr, strings.get_data(), str_len); - names_ptr[str_len] = '\0'; // NULL terminator - - // NULL terminated protocol strings to be used in protocol structs - if (str_len > 0) - copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); - names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator - int pos = str_len + 1; - - // The first protocol is the default for any http request (before upgrade). - // It is also used as the websocket protocol when no subprotocol is specified. - structs_ptr[0].name = "default"; - structs_ptr[0].callback = p_callback; - structs_ptr[0].per_session_data_size = data_size; - structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE; - // Add user defined protocols - for (i = 0; i < len; i++) { - structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; - structs_ptr[i + 1].callback = p_callback; - structs_ptr[i + 1].per_session_data_size = data_size; - structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE; - pos += pnr[i].ascii().length() + 1; - names_ptr[pos - 1] = '\0'; - } - // Add protocols terminator - structs_ptr[len + 1].name = NULL; - structs_ptr[len + 1].callback = NULL; - structs_ptr[len + 1].per_session_data_size = 0; - structs_ptr[len + 1].rx_buffer_size = 0; - - *r_lws_ref = ref; -} - -#endif diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h deleted file mode 100644 index def4f5cfd0..0000000000 --- a/modules/websocket/lws_helper.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************/ -/* lws_helper.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef LWS_HELPER_H -#define LWS_HELPER_H - -#define LWS_BUF_SIZE 65536 -#define LWS_PACKET_SIZE LWS_BUF_SIZE - -#include "core/io/stream_peer.h" -#include "core/os/os.h" -#include "core/reference.h" -#include "core/ring_buffer.h" -#include "lws_peer.h" - -struct _LWSRef { - bool free_context; - bool is_polling; - bool is_valid; - bool is_destroying; - void *obj; - struct lws_protocols *lws_structs; - char *lws_names; -}; - -_LWSRef *_lws_create_ref(void *obj); -void _lws_free_ref(_LWSRef *ref); -bool _lws_destroy(struct lws_context *context, _LWSRef *ref); -bool _lws_poll(struct lws_context *context, _LWSRef *ref); -void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref); - -/* clang-format off */ -#define LWS_HELPER(CNAME) \ -protected: \ - struct _LWSRef *_lws_ref; \ - struct lws_context *context; \ - bool _keep_servicing; \ - \ - static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \ - \ - if (wsi == NULL) { \ - return 0; \ - } \ - \ - struct _LWSRef *ref = (struct _LWSRef *)lws_context_user(lws_get_context(wsi)); \ - if (!ref->is_valid) \ - return 0; \ - CNAME *helper = (CNAME *)ref->obj; \ - helper->_keep_servicing = true; \ - return helper->_handle_cb(wsi, reason, user, in, len); \ - } \ - \ - void invalidate_lws_ref() { \ - if (_lws_ref != NULL) \ - _lws_ref->is_valid = false; \ - } \ - \ - void destroy_context() { \ - if (_lws_destroy(context, _lws_ref)) { \ - context = NULL; \ - _lws_ref = NULL; \ - } \ - } \ - \ -public: \ - virtual int _handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); \ - \ - void _lws_poll() { \ - ERR_FAIL_COND(context == NULL); \ - do { \ - _keep_servicing = false; \ - if (::_lws_poll(context, _lws_ref)) { \ - context = NULL; \ - _lws_ref = NULL; \ - break; \ - } \ - } while (_keep_servicing); \ - } \ - \ -protected: - -/* clang-format on */ - -#endif // LWS_HELPER_H diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp deleted file mode 100644 index 04e6e7c951..0000000000 --- a/modules/websocket/lws_peer.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/*************************************************************************/ -/* lws_peer.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef JAVASCRIPT_ENABLED - -#include "lws_peer.h" - -#include "core/io/ip.h" - -// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows -#if !defined(WINDOWS_ENABLED) -#include <netinet/in.h> -#include <sys/socket.h> -#endif - -#include "drivers/unix/net_socket_posix.h" - -void LWSPeer::set_wsi(struct lws *p_wsi, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) { - ERR_FAIL_COND(wsi != NULL); - - _in_buffer.resize(p_in_pkt_size, p_in_buf_size); - _out_buffer.resize(p_out_pkt_size, p_out_buf_size); - _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE); - wsi = p_wsi; -}; - -void LWSPeer::set_write_mode(WriteMode p_mode) { - write_mode = p_mode; -} - -LWSPeer::WriteMode LWSPeer::get_write_mode() const { - return write_mode; -} - -Error LWSPeer::read_wsi(void *in, size_t len) { - - ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - - if (lws_is_first_fragment(wsi)) - _in_size = 0; - else if (_in_size == -1) // Trash this frame - return ERR_FILE_CORRUPT; - - Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL); - - if (err != OK) { - _in_buffer.discard_payload(_in_size); - _in_size = -1; - ERR_FAIL_V(err); - } - - _in_size += len; - - if (lws_is_final_fragment(wsi)) { - uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; - err = _in_buffer.write_packet(NULL, _in_size, &is_string); - if (err != OK) { - _in_buffer.discard_payload(_in_size); - _in_size = -1; - ERR_FAIL_V(err); - } - } - - return OK; -} - -Error LWSPeer::write_wsi() { - - ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - - PoolVector<uint8_t> tmp; - int count = _out_buffer.packets_left(); - - if (count == 0) - return OK; - - int read = 0; - uint8_t is_string; - PoolVector<uint8_t>::Write rw = _packet_buffer.write(); - _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read); - - enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY; - lws_write(wsi, &(rw[LWS_PRE]), read, mode); - - if (count > 1) - lws_callback_on_writable(wsi); // we want to write more! - - return OK; -} - -Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - - ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - - uint8_t is_string = write_mode == WRITE_MODE_TEXT; - _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string); - lws_callback_on_writable(wsi); // notify that we want to write - return OK; -}; - -Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - - r_buffer_size = 0; - - ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - - if (_in_buffer.packets_left() == 0) - return ERR_UNAVAILABLE; - - int read = 0; - PoolVector<uint8_t>::Write rw = _packet_buffer.write(); - _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); - - *r_buffer = rw.ptr(); - r_buffer_size = read; - - return OK; -}; - -int LWSPeer::get_available_packet_count() const { - - if (!is_connected_to_host()) - return 0; - - return _in_buffer.packets_left(); -}; - -bool LWSPeer::was_string_packet() const { - - return _is_string; -}; - -bool LWSPeer::is_connected_to_host() const { - - return wsi != NULL; -}; - -String LWSPeer::get_close_reason(void *in, size_t len, int &r_code) { - String s; - r_code = 0; - if (len < 2) // From docs this should not happen - return s; - - const uint8_t *b = (const uint8_t *)in; - r_code = b[0] << 8 | b[1]; - - if (len > 2) { - const char *utf8 = (const char *)&b[2]; - s.parse_utf8(utf8, len - 2); - } - return s; -} - -void LWSPeer::send_close_status(struct lws *p_wsi) { - if (close_code == -1) - return; - - int len = close_reason.size(); - ERR_FAIL_COND(len > 123); // Maximum allowed reason size in bytes - - lws_close_status code = (lws_close_status)close_code; - unsigned char *reason = len > 0 ? (unsigned char *)close_reason.utf8().ptrw() : NULL; - - lws_close_reason(p_wsi, code, reason, len); - - close_code = -1; - close_reason = ""; -} - -void LWSPeer::close(int p_code, String p_reason) { - if (wsi != NULL) { - close_code = p_code; - close_reason = p_reason; - PeerData *data = ((PeerData *)lws_wsi_user(wsi)); - data->force_close = true; - data->clean_close = true; - lws_callback_on_writable(wsi); // Notify that we want to disconnect - } else { - close_code = -1; - close_reason = ""; - } - wsi = NULL; - _in_buffer.clear(); - _out_buffer.clear(); - _in_size = 0; - _is_string = 0; - _packet_buffer.resize(0); -}; - -IP_Address LWSPeer::get_connected_host() const { - - ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); - - IP_Address ip; - uint16_t port = 0; - - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - - int fd = lws_get_socket_fd(wsi); - ERR_FAIL_COND_V(fd == -1, IP_Address()); - - int ret = getpeername(fd, (struct sockaddr *)&addr, &len); - ERR_FAIL_COND_V(ret != 0, IP_Address()); - - NetSocketPosix::_set_ip_port(&addr, ip, port); - - return ip; -}; - -uint16_t LWSPeer::get_connected_port() const { - - ERR_FAIL_COND_V(!is_connected_to_host(), 0); - - IP_Address ip; - uint16_t port = 0; - - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - - int fd = lws_get_socket_fd(wsi); - ERR_FAIL_COND_V(fd == -1, 0); - - int ret = getpeername(fd, (struct sockaddr *)&addr, &len); - ERR_FAIL_COND_V(ret != 0, 0); - - NetSocketPosix::_set_ip_port(&addr, ip, port); - - return port; -}; - -LWSPeer::LWSPeer() { - wsi = NULL; - write_mode = WRITE_MODE_BINARY; - close(); -}; - -LWSPeer::~LWSPeer() { - - close(); -}; - -#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp deleted file mode 100644 index 0e551eb318..0000000000 --- a/modules/websocket/lws_server.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************/ -/* lws_server.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef JAVASCRIPT_ENABLED - -#include "lws_server.h" -#include "core/os/os.h" -#include "core/project_settings.h" - -Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { - - ERR_FAIL_COND_V(context != NULL, FAILED); - - _is_multiplayer = gd_mp_api; - - struct lws_context_creation_info info; - memset(&info, 0, sizeof info); - - // Prepare lws protocol structs - _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref); - - info.port = p_port; - info.user = _lws_ref; - info.protocols = _lws_ref->lws_structs; - info.gid = -1; - info.uid = -1; - //info.ws_ping_pong_interval = 5; - - context = lws_create_context(&info); - - if (context == NULL) { - _lws_free_ref(_lws_ref); - _lws_ref = NULL; - ERR_EXPLAIN("Unable to create LWS context"); - ERR_FAIL_V(FAILED); - } - - return OK; -} - -bool LWSServer::is_listening() const { - return context != NULL; -} - -int LWSServer::get_max_packet_size() const { - return (1 << _out_buf_size) - PROTO_SIZE; -} - -int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - - LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; - - switch (reason) { - case LWS_CALLBACK_HTTP: - // no http for now - // closing immediately returning -1; - return -1; - - case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: - // check header here? - break; - - case LWS_CALLBACK_ESTABLISHED: { - int32_t id = _gen_unique_id(); - - Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); - peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); - _peer_map[id] = peer; - - peer_data->peer_id = id; - peer_data->force_close = false; - peer_data->clean_close = false; - _on_connect(id, lws_get_protocol(wsi)->name); - break; - } - - case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { - if (peer_data == NULL) - return 0; - - int32_t id = peer_data->peer_id; - if (_peer_map.has(id)) { - int code; - Ref<LWSPeer> peer = _peer_map[id]; - String reason = peer->get_close_reason(in, len, code); - peer_data->clean_close = true; - _on_close_request(id, code, reason); - } - return 0; - } - - case LWS_CALLBACK_CLOSED: { - if (peer_data == NULL) - return 0; - int32_t id = peer_data->peer_id; - bool clean = peer_data->clean_close; - if (_peer_map.has(id)) { - _peer_map[id]->close(); - _peer_map.erase(id); - } - _on_disconnect(id, clean); - return 0; // we can end here - } - - case LWS_CALLBACK_RECEIVE: { - int32_t id = peer_data->peer_id; - if (_peer_map.has(id)) { - static_cast<Ref<LWSPeer> >(_peer_map[id])->read_wsi(in, len); - if (_peer_map[id]->get_available_packet_count() > 0) - _on_peer_packet(id); - } - break; - } - - case LWS_CALLBACK_SERVER_WRITEABLE: { - int id = peer_data->peer_id; - if (peer_data->force_close) { - if (_peer_map.has(id)) { - Ref<LWSPeer> peer = _peer_map[id]; - peer->send_close_status(wsi); - } - return -1; - } - - if (_peer_map.has(id)) - static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi(); - break; - } - - default: - break; - } - - return 0; -} - -void LWSServer::stop() { - if (context == NULL) - return; - - _peer_map.clear(); - destroy_context(); - context = NULL; -} - -bool LWSServer::has_peer(int p_id) const { - return _peer_map.has(p_id); -} - -Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const { - ERR_FAIL_COND_V(!has_peer(p_id), NULL); - return _peer_map[p_id]; -} - -IP_Address LWSServer::get_peer_address(int p_peer_id) const { - ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address()); - - return _peer_map[p_peer_id]->get_connected_host(); -} - -int LWSServer::get_peer_port(int p_peer_id) const { - ERR_FAIL_COND_V(!has_peer(p_peer_id), 0); - - return _peer_map[p_peer_id]->get_connected_port(); -} - -void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { - ERR_FAIL_COND(!has_peer(p_peer_id)); - - get_peer(p_peer_id)->close(p_code, p_reason); -} - -LWSServer::LWSServer() { - _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10; - _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1); - _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10; - _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1); - context = NULL; - _lws_ref = NULL; -} - -LWSServer::~LWSServer() { - invalidate_lws_ref(); // we do not want any more callbacks - stop(); -} - -#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h index a3af7f728a..057fecfb56 100644 --- a/modules/websocket/packet_buffer.h +++ b/modules/websocket/packet_buffer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,7 +50,7 @@ public: Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) { #ifdef TOOLS_ENABLED // Verbose buffer warnings - if (p_payload && _payload.space_left() < p_size) { + if (p_payload && _payload.space_left() < (int32_t)p_size) { ERR_PRINT("Buffer payload full! Dropping data."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -59,7 +59,7 @@ public: ERR_FAIL_V(ERR_OUT_OF_MEMORY); } #else - ERR_FAIL_COND_V(p_payload && _payload.space_left() < p_size, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(p_payload && (uint32_t)_payload.space_left() < p_size, ERR_OUT_OF_MEMORY); ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY); #endif @@ -83,8 +83,8 @@ public: ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE); _Packet p; _packets.read(&p, 1); - ERR_FAIL_COND_V(_payload.data_left() < p.size, ERR_BUG); - ERR_FAIL_COND_V(p_bytes < p.size, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_payload.data_left() < (int)p.size, ERR_BUG); + ERR_FAIL_COND_V(p_bytes < (int)p.size, ERR_OUT_OF_MEMORY); r_read = p.size; copymem(r_info, &p.info, sizeof(T)); diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 8946faffa9..1c808f0d5c 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "register_types.h" #include "core/error_macros.h" #include "core/project_settings.h" @@ -36,9 +37,8 @@ #include "emws_peer.h" #include "emws_server.h" #else -#include "lws_client.h" -#include "lws_peer.h" -#include "lws_server.h" +#include "wsl_client.h" +#include "wsl_server.h" #endif void register_websocket_types() { @@ -59,32 +59,13 @@ void register_websocket_types() { _SET_HINT(WSS_OUT_PKT, 1024, 16384); #ifdef JAVASCRIPT_ENABLED - EM_ASM({ - var IDHandler = {}; - IDHandler["ids"] = {}; - IDHandler["has"] = function(id) { - return IDHandler.ids.hasOwnProperty(id); - }; - IDHandler["add"] = function(obj) { - var id = crypto.getRandomValues(new Int32Array(32))[0]; - IDHandler.ids[id] = obj; - return id; - }; - IDHandler["get"] = function(id) { - return IDHandler.ids[id]; - }; - IDHandler["remove"] = function(id) { - delete IDHandler.ids[id]; - }; - Module["IDHandler"] = IDHandler; - }); EMWSPeer::make_default(); EMWSClient::make_default(); EMWSServer::make_default(); #else - LWSPeer::make_default(); - LWSClient::make_default(); - LWSServer::make_default(); + WSLPeer::make_default(); + WSLClient::make_default(); + WSLServer::make_default(); #endif ClassDB::register_virtual_class<WebSocketMultiplayerPeer>(); diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h index 89ce93e286..0bcae50d14 100644 --- a/modules/websocket/register_types.h +++ b/modules/websocket/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,5 +27,6 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_websocket_types(); void unregister_websocket_types(); diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 6c5018bb79..4ff5404c61 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "websocket_client.h" GDCINULL(WebSocketClient); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 948f128e9f..7ddb9468a5 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,11 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WEBSOCKET_CLIENT_H #define WEBSOCKET_CLIENT_H #include "core/error_list.h" -#include "websocket_multiplayer.h" +#include "websocket_multiplayer_peer.h" #include "websocket_peer.h" class WebSocketClient : public WebSocketMultiplayerPeer { @@ -66,6 +67,8 @@ public: void _on_disconnect(bool p_was_clean); void _on_error(); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; + WebSocketClient(); ~WebSocketClient(); }; diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index 45dd30d0ce..56f278187f 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WEBSOCKETMACTOS_H #define WEBSOCKETMACTOS_H diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 9a95c17e47..e24cb850ec 100644 --- a/modules/websocket/websocket_multiplayer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* websocket_multiplayer.cpp */ +/* websocket_multiplayer_peer.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,7 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "websocket_multiplayer.h" + +#include "websocket_multiplayer_peer.h" + #include "core/os/os.h" WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() { @@ -85,6 +87,7 @@ void WebSocketMultiplayerPeer::_clear() { void WebSocketMultiplayerPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_buffers", "input_buffer_size_kb", "input_max_packets", "output_buffer_size_kb", "output_max_packets"), &WebSocketMultiplayerPeer::set_buffers); ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer); ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source"))); @@ -95,6 +98,7 @@ void WebSocketMultiplayerPeer::_bind_methods() { // int WebSocketMultiplayerPeer::get_available_packet_count() const { + ERR_EXPLAIN("Please use get_peer(ID).get_available_packet_count to get available packet count from peers when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); return _incoming_packets.size(); @@ -102,9 +106,11 @@ int WebSocketMultiplayerPeer::get_available_packet_count() const { Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - r_buffer_size = 0; + ERR_EXPLAIN("Please use get_peer(ID).get_packet/var to communicate with peers when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + r_buffer_size = 0; + if (_current_packet.data != NULL) { memfree(_current_packet.data); _current_packet.data = NULL; @@ -121,6 +127,7 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_EXPLAIN("Please use get_peer(ID).put_packet/var to communicate with peers when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size); @@ -153,6 +160,7 @@ void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { int WebSocketMultiplayerPeer::get_packet_peer() const { + ERR_EXPLAIN("This function is not available when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, 1); ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1); @@ -183,7 +191,7 @@ void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_ty p_peer->put_packet(&(message.read()[0]), message.size()); } -PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) { +PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) { PoolVector<uint8_t> out; out.resize(PROTO_SIZE + p_data_size); @@ -206,7 +214,7 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) { _send_sys(get_peer(p_peer_id), SYS_ADD, 1); for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { - uint32_t id = E->key(); + int32_t id = E->key(); if (p_peer_id == id) continue; // Skip the newwly added peer (already confirmed) @@ -219,7 +227,7 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) { void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) { for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { - uint32_t id = E->key(); + int32_t id = E->key(); if (p_peer_id != id) _send_sys(get_peer(id), SYS_DEL, p_peer_id); } @@ -281,7 +289,7 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u data_size = size - PROTO_SIZE; uint8_t type = 0; - int32_t from = 0; + uint32_t from = 0; int32_t to = 0; copymem(&type, in_buffer, 1); copymem(&from, &in_buffer[1], 4); diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer_peer.h index 3cba0011fc..e3ab0784ab 100644 --- a/modules/websocket/websocket_multiplayer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* websocket_multiplayer.h */ +/* websocket_multiplayer_peer.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WEBSOCKET_MULTIPLAYER_PEER_H #define WEBSOCKET_MULTIPLAYER_PEER_H @@ -40,7 +41,7 @@ class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer { GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer); private: - PoolVector<uint8_t> _make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size); + PoolVector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size); void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size); Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size); @@ -81,7 +82,7 @@ public: /* NetworkedMultiplayerPeer */ void set_transfer_mode(TransferMode p_mode); TransferMode get_transfer_mode() const; - void set_target_peer(int p_peer_id); + void set_target_peer(int p_target_peer); int get_packet_peer() const; int get_unique_id() const; virtual bool is_server() const = 0; @@ -96,6 +97,7 @@ public: virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); /* WebSocketPeer */ + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0; void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id); diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index 3ecb32ce19..15df1bf85a 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "websocket_peer.h" GDCINULL(WebSocketPeer); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 4966cdfc72..62c1f3e9a3 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WEBSOCKETPEER_H #define WEBSOCKETPEER_H diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index c631ed70d5..ef5f6f5c20 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "websocket_server.h" GDCINULL(WebSocketServer); diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 156f25897c..83c0c10419 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,11 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef WEBSOCKET_H #define WEBSOCKET_H #include "core/reference.h" -#include "websocket_multiplayer.h" +#include "websocket_multiplayer_peer.h" #include "websocket_peer.h" class WebSocketServer : public WebSocketMultiplayerPeer { @@ -61,6 +62,8 @@ public: void _on_disconnect(int32_t p_peer_id, bool p_was_clean); void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; + WebSocketServer(); ~WebSocketServer(); }; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp new file mode 100644 index 0000000000..86374e8f80 --- /dev/null +++ b/modules/websocket/wsl_client.cpp @@ -0,0 +1,363 @@ +/*************************************************************************/ +/* wsl_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JAVASCRIPT_ENABLED + +#include "wsl_client.h" +#include "core/io/ip.h" +#include "core/project_settings.h" + +void WSLClient::_do_handshake() { + if (_requested < _request.size() - 1) { + int sent = 0; + Error err = _connection->put_partial_data(((const uint8_t *)_request.get_data() + _requested), _request.size() - _requested - 1, sent); + // Sending handshake failed + if (err != OK) { + disconnect_from_host(); + _on_error(); + return; + } + _requested += sent; + + } else { + int read = 0; + while (true) { + if (_resp_pos >= WSL_MAX_HEADER_SIZE) { + // Header is too big + disconnect_from_host(); + _on_error(); + ERR_EXPLAIN("Response headers too big"); + ERR_FAIL(); + } + Error err = _connection->get_partial_data(&_resp_buf[_resp_pos], 1, read); + if (err == ERR_FILE_EOF) { + // We got a disconnect. + disconnect_from_host(); + _on_error(); + return; + } else if (err != OK) { + // Got some error. + disconnect_from_host(); + _on_error(); + return; + } else if (read != 1) { + // Busy, wait next poll. + break; + } + // Check "\r\n\r\n" header terminator + char *r = (char *)_resp_buf; + int l = _resp_pos; + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + r[l - 3] = '\0'; + String protocol; + // Response is over, verify headers and create peer. + if (!_verify_headers(protocol)) { + disconnect_from_host(); + _on_error(); + ERR_EXPLAIN("Invalid response headers"); + ERR_FAIL(); + } + // Create peer. + WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); + data->obj = this; + data->conn = _connection; + data->is_server = false; + data->id = 1; + _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); + _on_connect(protocol); + break; + } + _resp_pos += 1; + } + } +} + +bool WSLClient::_verify_headers(String &r_protocol) { + String s = (char *)_resp_buf; + Vector<String> psa = s.split("\r\n"); + int len = psa.size(); + if (len < 4) { + ERR_EXPLAIN("Not enough response headers."); + ERR_FAIL_V(false); + } + + Vector<String> req = psa[0].split(" ", false); + if (req.size() < 2) { + ERR_EXPLAIN("Invalid protocol or status code."); + ERR_FAIL_V(false); + } + // Wrong protocol + if (req[0] != "HTTP/1.1" || req[1] != "101") { + ERR_EXPLAIN("Invalid protocol or status code."); + ERR_FAIL_V(false); + } + + Map<String, String> headers; + for (int i = 1; i < len; i++) { + Vector<String> header = psa[i].split(":", false, 1); + if (header.size() != 2) { + ERR_EXPLAIN("Invalid header -> " + psa[i]); + ERR_FAIL_V(false); + } + String name = header[0].to_lower(); + String value = header[1].strip_edges(); + if (headers.has(name)) + headers[name] += "," + value; + else + headers[name] = value; + } + +#define _WLS_EXPLAIN(NAME, VALUE) \ + ERR_EXPLAIN("Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'"); +#define _WLS_CHECK(NAME, VALUE) \ + _WLS_EXPLAIN(NAME, VALUE); \ + ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false); +#define _WLS_CHECK_NC(NAME, VALUE) \ + _WLS_EXPLAIN(NAME, VALUE); \ + ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME] != VALUE, false); + _WLS_CHECK("connection", "upgrade"); + _WLS_CHECK("upgrade", "websocket"); + _WLS_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key)); + if (_protocols.size() == 0) { + // We didn't request a custom protocol + ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false); + } else { + ERR_FAIL_COND_V(!headers.has("sec-websocket-protocol"), false); + r_protocol = headers["sec-websocket-protocol"]; + bool valid = false; + for (int i = 0; i < _protocols.size(); i++) { + if (_protocols[i] != r_protocol) + continue; + valid = true; + break; + } + if (!valid) + return false; + } +#undef _WLS_CHECK_NC +#undef _WLS_CHECK +#undef _WLS_EXPLAIN + + return true; +} + +Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + + ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); + + _peer = Ref<WSLPeer>(memnew(WSLPeer)); + IP_Address addr; + + if (!p_host.is_valid_ip_address()) { + addr = IP::get_singleton()->resolve_hostname(p_host); + } else { + addr = p_host; + } + + ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); + + String port = ""; + if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { + port = ":" + itos(p_port); + } + + Error err = _tcp->connect_to_host(addr, p_port); + if (err != OK) { + _on_error(); + _tcp->disconnect_from_host(); + return err; + } + _connection = _tcp; + _use_ssl = p_ssl; + _host = p_host; + _protocols = p_protocols; + + _key = WSLPeer::generate_key(); + // TODO custom extra headers (allow overriding this too?) + String request = "GET " + p_path + " HTTP/1.1\r\n"; + request += "Host: " + p_host + port + "\r\n"; + request += "Upgrade: websocket\r\n"; + request += "Connection: Upgrade\r\n"; + request += "Sec-WebSocket-Key: " + _key + "\r\n"; + request += "Sec-WebSocket-Version: 13\r\n"; + if (p_protocols.size() > 0) { + request += "Sec-WebSocket-Protocol: "; + for (int i = 0; i < p_protocols.size(); i++) { + if (i != 0) + request += ","; + request += p_protocols[i]; + } + request += "\r\n"; + } + request += "\r\n"; + _request = request.utf8(); + + return OK; +} + +int WSLClient::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + +void WSLClient::poll() { + if (_peer->is_connected_to_host()) { + _peer->poll(); + if (!_peer->is_connected_to_host()) { + _on_disconnect(_peer->close_code != -1); + disconnect_from_host(); + } + return; + } + + if (_connection.is_null()) + return; // Not connected. + + switch (_tcp->get_status()) { + case StreamPeerTCP::STATUS_NONE: + // Clean close + _on_error(); + disconnect_from_host(); + break; + case StreamPeerTCP::STATUS_CONNECTED: { + Ref<StreamPeerSSL> ssl; + if (_use_ssl) { + if (_connection == _tcp) { + // Start SSL handshake + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + ERR_EXPLAIN("SSL is not available in this build"); + ERR_FAIL_COND(ssl.is_null()); + ssl->set_blocking_handshake_enabled(false); + if (ssl->connect_to_stream(_tcp, verify_ssl, _host) != OK) { + _on_error(); + disconnect_from_host(); + return; + } + _connection = ssl; + } else { + ssl = static_cast<Ref<StreamPeerSSL> >(_connection); + ERR_FAIL_COND(ssl.is_null()); // Bug? + ssl->poll(); + } + if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) + return; // Need more polling. + else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + _on_error(); + disconnect_from_host(); + return; // Error. + } + } + // Do websocket handshake. + _do_handshake(); + } break; + case StreamPeerTCP::STATUS_ERROR: + _on_error(); + disconnect_from_host(); + break; + case StreamPeerTCP::STATUS_CONNECTING: + break; // Wait for connection + } +} + +Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const { + + ERR_FAIL_COND_V(p_peer_id != 1, NULL); + + return _peer; +} + +NetworkedMultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const { + + if (_peer->is_connected_to_host()) + return CONNECTION_CONNECTED; + + if (_tcp->is_connected_to_host()) + return CONNECTION_CONNECTING; + + return CONNECTION_DISCONNECTED; +} + +void WSLClient::disconnect_from_host(int p_code, String p_reason) { + + _peer->close(p_code, p_reason); + _connection = Ref<StreamPeer>(NULL); + _tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + + _key = ""; + _host = ""; + _protocols.resize(0); + _use_ssl = false; + + _request = ""; + _requested = 0; + + memset(_resp_buf, 0, sizeof(_resp_buf)); + _resp_pos = 0; +} + +IP_Address WSLClient::get_connected_host() const { + + return IP_Address(); +} + +uint16_t WSLClient::get_connected_port() const { + + return 1025; +} + +Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting"); + ERR_FAIL_COND_V(_connection.is_valid(), FAILED); + + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + _out_buf_size = nearest_shift(p_out_buffer - 1) + 10; + _out_pkt_size = nearest_shift(p_out_packets - 1); + return OK; +} + +WSLClient::WSLClient() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1); + + _peer.instance(); + _tcp.instance(); + disconnect_from_host(); +} + +WSLClient::~WSLClient() { + + _peer->close_now(); + _peer->invalidate(); + disconnect_from_host(); +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_client.h b/modules/websocket/wsl_client.h index fdecb99925..57dfd635b7 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/wsl_client.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* lws_client.h */ +/* wsl_client.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,21 +27,22 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LWSCLIENT_H -#define LWSCLIENT_H + +#ifndef WSLCLIENT_H +#define WSLCLIENT_H #ifndef JAVASCRIPT_ENABLED #include "core/error_list.h" -#include "lws_helper.h" -#include "lws_peer.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tcp.h" #include "websocket_client.h" +#include "wsl_peer.h" +#include "wslay/wslay.h" -class LWSClient : public WebSocketClient { - - GDCIIMPL(LWSClient, WebSocketClient); +class WSLClient : public WebSocketClient { - LWS_HELPER(LWSClient); + GDCIIMPL(WSLClient, WebSocketClient); private: int _in_buf_size; @@ -49,7 +50,28 @@ private: int _out_buf_size; int _out_pkt_size; + Ref<WSLPeer> _peer; + Ref<StreamPeerTCP> _tcp; + Ref<StreamPeer> _connection; + + CharString _request; + int _requested; + + uint8_t _resp_buf[WSL_MAX_HEADER_SIZE]; + int _resp_pos; + + String _response; + + String _key; + String _host; + PoolVector<String> _protocols; + bool _use_ssl; + + void _do_handshake(); + bool _verify_headers(String &r_protocol); + public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; @@ -59,10 +81,10 @@ public: virtual ConnectionStatus get_connection_status() const; virtual void poll(); - LWSClient(); - ~LWSClient(); + WSLClient(); + ~WSLClient(); }; #endif // JAVASCRIPT_ENABLED -#endif // LWSCLIENT_H +#endif // WSLCLIENT_H diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp new file mode 100644 index 0000000000..b11bd2b70f --- /dev/null +++ b/modules/websocket/wsl_peer.cpp @@ -0,0 +1,339 @@ +/*************************************************************************/ +/* lws_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JAVASCRIPT_ENABLED + +#include "wsl_peer.h" + +#include "wsl_client.h" +#include "wsl_server.h" + +#include "core/math/crypto_core.h" +#include "core/math/random_number_generator.h" +#include "core/os/os.h" + +String WSLPeer::generate_key() { + // Random key + RandomNumberGenerator rng; + rng.set_seed(OS::get_singleton()->get_unix_time()); + PoolVector<uint8_t> bkey; + int len = 16; // 16 bytes, as per RFC + bkey.resize(len); + PoolVector<uint8_t>::Write w = bkey.write(); + for (int i = 0; i < len; i++) { + w[i] = (uint8_t)rng.randi_range(0, 255); + } + return CryptoCore::b64_encode_str(&w[0], len); +} + +String WSLPeer::compute_key_response(String p_key) { + String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC + Vector<uint8_t> sha = key.sha1_buffer(); + return CryptoCore::b64_encode_str(sha.ptr(), sha.size()); +} + +void WSLPeer::_wsl_destroy(struct PeerData **p_data) { + if (!p_data || !(*p_data)) + return; + struct PeerData *data = *p_data; + if (data->polling) { + data->destroy = true; + return; + } + wslay_event_context_free(data->ctx); + memdelete(data); + *p_data = NULL; +} + +bool WSLPeer::_wsl_poll(struct PeerData *p_data) { + p_data->polling = true; + int err = 0; + if ((err = wslay_event_recv(p_data->ctx)) != 0 || (err = wslay_event_send(p_data->ctx)) != 0) { + print_verbose("Websocket (wslay) poll error: " + itos(err)); + p_data->destroy = true; + } + p_data->polling = false; + + if (p_data->destroy || (wslay_event_get_close_sent(p_data->ctx) && wslay_event_get_close_received(p_data->ctx))) { + bool valid = p_data->valid; + _wsl_destroy(&p_data); + return valid; + } + return false; +} + +ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) { + struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data; + if (!peer_data->valid) { + wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); + return -1; + } + Ref<StreamPeer> conn = peer_data->conn; + int read = 0; + Error err = conn->get_partial_data(data, len, read); + if (err != OK) { + print_verbose("Websocket get data error: " + itos(err) + ", read (should be 0!): " + itos(read)); + wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); + return -1; + } + if (read == 0) { + wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); + return -1; + } + return read; +} + +ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) { + struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data; + if (!peer_data->valid) { + wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); + return -1; + } + Ref<StreamPeer> conn = peer_data->conn; + int sent = 0; + Error err = conn->put_partial_data(data, len, sent); + if (err != OK) { + wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); + return -1; + } + if (sent == 0) { + wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); + return -1; + } + return sent; +} + +int wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) { + RandomNumberGenerator rng; + // TODO maybe use crypto in the future? + rng.set_seed(OS::get_singleton()->get_unix_time()); + for (unsigned int i = 0; i < len; i++) { + buf[i] = (uint8_t)rng.randi_range(0, 255); + } + return 0; +} + +void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { + struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data; + if (!peer_data->valid) { + return; + } + WSLPeer *peer = (WSLPeer *)peer_data->peer; + + if (peer->parse_message(arg) != OK) + return; + + if (peer_data->is_server) { + WSLServer *helper = (WSLServer *)peer_data->obj; + helper->_on_peer_packet(peer_data->id); + } else { + WSLClient *helper = (WSLClient *)peer_data->obj; + helper->_on_peer_packet(); + } +} + +wslay_event_callbacks wsl_callbacks = { + wsl_recv_callback, + wsl_send_callback, + wsl_genmask_callback, + NULL, /* on_frame_recv_start_callback */ + NULL, /* on_frame_recv_callback */ + NULL, /* on_frame_recv_end_callback */ + wsl_msg_recv_callback +}; + +Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) { + uint8_t is_string = 0; + if (arg->opcode == WSLAY_TEXT_FRAME) { + is_string = 1; + } else if (arg->opcode == WSLAY_CONNECTION_CLOSE) { + close_code = arg->status_code; + size_t len = arg->msg_length; + close_reason = ""; + if (len > 2 /* first 2 bytes = close code */) { + close_reason.parse_utf8((char *)arg->msg + 2, len - 2); + } + if (!wslay_event_get_close_sent(_data->ctx)) { + if (_data->is_server) { + WSLServer *helper = (WSLServer *)_data->obj; + helper->_on_close_request(_data->id, close_code, close_reason); + } else { + WSLClient *helper = (WSLClient *)_data->obj; + helper->_on_close_request(close_code, close_reason); + } + } + return ERR_FILE_EOF; + } else if (arg->opcode != WSLAY_BINARY_FRAME) { + // Ping or pong + return ERR_SKIP; + } + _in_buffer.write_packet(arg->msg, arg->msg_length, &is_string); + return OK; +} + +void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) { + ERR_FAIL_COND(_data != NULL); + ERR_FAIL_COND(p_data == NULL); + + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size))); + + _data = p_data; + _data->peer = this; + _data->valid = true; + _connection = Ref<StreamPeer>(_data->conn); + + if (_data->is_server) + wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data); + else + wslay_event_context_client_init(&(_data->ctx), &wsl_callbacks, _data); + wslay_event_config_set_max_recv_msg_length(_data->ctx, (1 << p_in_buf_size)); +} + +void WSLPeer::set_write_mode(WriteMode p_mode) { + write_mode = p_mode; +} + +WSLPeer::WriteMode WSLPeer::get_write_mode() const { + return write_mode; +} + +void WSLPeer::poll() { + if (!_data) + return; + + if (_wsl_poll(_data)) { + _data = NULL; + } +} + +Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + struct wslay_event_msg msg; // Should I use fragmented? + msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME; + msg.msg = p_buffer; + msg.msg_length = p_buffer_size; + + wslay_event_queue_msg(_data->ctx, &msg); + return OK; +} + +Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + r_buffer_size = 0; + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + if (_in_buffer.packets_left() == 0) + return ERR_UNAVAILABLE; + + int read = 0; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); + + *r_buffer = rw.ptr(); + r_buffer_size = read; + + return OK; +} + +int WSLPeer::get_available_packet_count() const { + + if (!is_connected_to_host()) + return 0; + + return _in_buffer.packets_left(); +} + +bool WSLPeer::was_string_packet() const { + + return _is_string; +} + +bool WSLPeer::is_connected_to_host() const { + + return _data != NULL; +} + +void WSLPeer::close_now() { + close(1000, ""); + _wsl_destroy(&_data); +} + +void WSLPeer::close(int p_code, String p_reason) { + if (_data && !wslay_event_get_close_sent(_data->ctx)) { + CharString cs = p_reason.utf8(); + wslay_event_queue_close(_data->ctx, p_code, (uint8_t *)cs.ptr(), cs.size()); + wslay_event_send(_data->ctx); + } + + _in_buffer.clear(); + _packet_buffer.resize(0); +} + +IP_Address WSLPeer::get_connected_host() const { + + ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); + + IP_Address ip; + return ip; +} + +uint16_t WSLPeer::get_connected_port() const { + + ERR_FAIL_COND_V(!is_connected_to_host(), 0); + + uint16_t port = 0; + return port; +} + +void WSLPeer::invalidate() { + if (_data) + _data->valid = false; +} + +WSLPeer::WSLPeer() { + _data = NULL; + _is_string = 0; + close_code = -1; + write_mode = WRITE_MODE_BINARY; +} + +WSLPeer::~WSLPeer() { + + close(); + invalidate(); + _wsl_destroy(&_data); + _data = NULL; +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_peer.h b/modules/websocket/wsl_peer.h index 3ded3810d1..d51b304fe1 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/wsl_peer.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* lws_peer.h */ +/* wsl_peer.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,50 +27,77 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LWSPEER_H -#define LWSPEER_H + +#ifndef WSLPEER_H +#define WSLPEER_H #ifndef JAVASCRIPT_ENABLED #include "core/error_list.h" #include "core/io/packet_peer.h" #include "core/ring_buffer.h" -#include "libwebsockets.h" -#include "lws_config.h" #include "packet_buffer.h" #include "websocket_peer.h" +#include "wslay/wslay.h" + +#define WSL_MAX_HEADER_SIZE 4096 + +class WSLPeer : public WebSocketPeer { + + GDCIIMPL(WSLPeer, WebSocketPeer); -class LWSPeer : public WebSocketPeer { +public: + struct PeerData { + bool polling; + bool destroy; + bool valid; + bool is_server; + void *obj; + void *peer; + Ref<StreamPeer> conn; + int id; + wslay_event_context_ptr ctx; + + PeerData() { + polling = false; + destroy = false; + valid = false; + is_server = false; + id = 1; + ctx = NULL; + obj = NULL; + peer = NULL; + } + }; - GDCIIMPL(LWSPeer, WebSocketPeer); + static String compute_key_response(String p_key); + static String generate_key(); private: - int _in_size; + static bool _wsl_poll(struct PeerData *p_data); + static void _wsl_destroy(struct PeerData **p_data); + + Ref<StreamPeer> _connection; + struct PeerData *_data; uint8_t _is_string; // Our packet info is just a boolean (is_string), using uint8_t for it. PacketBuffer<uint8_t> _in_buffer; - PacketBuffer<uint8_t> _out_buffer; PoolVector<uint8_t> _packet_buffer; - struct lws *wsi; WriteMode write_mode; +public: int close_code; String close_reason; - -public: - struct PeerData { - uint32_t peer_id; - bool force_close; - bool clean_close; - }; + void poll(); // Used by client and server. virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); virtual int get_max_packet_size() const { return _packet_buffer.size(); }; + virtual void close_now(); virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; virtual IP_Address get_connected_host() const; @@ -80,14 +107,12 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size); - Error read_wsi(void *in, size_t len); - Error write_wsi(); - void send_close_status(struct lws *wsi); - String get_close_reason(void *in, size_t len, int &r_code); + void make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size); + Error parse_message(const wslay_event_on_msg_recv_arg *arg); + void invalidate(); - LWSPeer(); - ~LWSPeer(); + WSLPeer(); + ~WSLPeer(); }; #endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp new file mode 100644 index 0000000000..0d09a4d74e --- /dev/null +++ b/modules/websocket/wsl_server.cpp @@ -0,0 +1,302 @@ +/*************************************************************************/ +/* lws_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JAVASCRIPT_ENABLED + +#include "wsl_server.h" +#include "core/os/os.h" +#include "core/project_settings.h" + +WSLServer::PendingPeer::PendingPeer() { + time = 0; + has_request = false; + response_sent = 0; + req_pos = 0; + memset(req_buf, 0, sizeof(req_buf)); +} + +bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { + Vector<String> psa = String((char *)req_buf).split("\r\n"); + int len = psa.size(); + if (len < 4) { + ERR_EXPLAIN("Not enough response headers."); + ERR_FAIL_V(false); + } + + Vector<String> req = psa[0].split(" ", false); + if (req.size() < 2) { + ERR_EXPLAIN("Invalid protocol or status code."); + ERR_FAIL_V(false); + } + // Wrong protocol + if (req[0] != "GET" || req[2] != "HTTP/1.1") { + ERR_EXPLAIN("Invalid method or HTTP version."); + ERR_FAIL_V(false); + } + + Map<String, String> headers; + for (int i = 1; i < len; i++) { + Vector<String> header = psa[i].split(":", false, 1); + if (header.size() != 2) { + ERR_EXPLAIN("Invalid header -> " + psa[i]); + ERR_FAIL_V(false); + } + String name = header[0].to_lower(); + String value = header[1].strip_edges(); + if (headers.has(name)) + headers[name] += "," + value; + else + headers[name] = value; + } +#define _WLS_CHECK(NAME, VALUE) \ + ERR_EXPLAIN("Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'"); \ + ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false); +#define _WLS_CHECK_EX(NAME) \ + ERR_EXPLAIN("Missing header '" + String(NAME) + "'."); \ + ERR_FAIL_COND_V(!headers.has(NAME), false); + _WLS_CHECK("upgrade", "websocket"); + _WLS_CHECK("sec-websocket-version", "13"); + _WLS_CHECK_EX("sec-websocket-key"); + _WLS_CHECK_EX("connection"); +#undef _WLS_CHECK_EX +#undef _WLS_CHECK + key = headers["sec-websocket-key"]; + if (headers.has("sec-websocket-protocol")) { + Vector<String> protos = headers["sec-websocket-protocol"].split(","); + for (int i = 0; i < protos.size(); i++) { + // Check if we have the given protocol + for (int j = 0; j < p_protocols.size(); j++) { + if (protos[i] != p_protocols[j]) + continue; + protocol = protos[i]; + break; + } + // Found a protocol + if (protocol != "") + break; + } + if (protocol == "") // Invalid protocol(s) requested + return false; + } else if (p_protocols.size() > 0) // No protocol requested, but we need one + return false; + return true; +} + +Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { + if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) + return ERR_TIMEOUT; + if (!has_request) { + int read = 0; + while (true) { + if (req_pos >= WSL_MAX_HEADER_SIZE) { + // Header is too big + ERR_EXPLAIN("Response headers too big"); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + } + Error err = connection->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) // Got an error + return FAILED; + else if (read != 1) // Busy, wait next poll + return ERR_BUSY; + char *r = (char *)req_buf; + int l = req_pos; + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + r[l - 3] = '\0'; + if (!_parse_request(p_protocols)) { + return FAILED; + } + String s = "HTTP/1.1 101 Switching Protocols\r\n"; + s += "Upgrade: websocket\r\n"; + s += "Connection: Upgrade\r\n"; + s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n"; + if (protocol != "") + s += "Sec-WebSocket-Protocol: " + protocol + "\r\n"; + s += "\r\n"; + response = s.utf8(); + has_request = true; + break; + } + req_pos += 1; + } + } + if (has_request && response_sent < response.size() - 1) { + int sent = 0; + Error err = connection->put_partial_data((const uint8_t *)response.get_data() + response_sent, response.size() - response_sent - 1, sent); + if (err != OK) { + return err; + } + response_sent += sent; + } + if (response_sent < response.size() - 1) + return ERR_BUSY; + return OK; +} + +Error WSLServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { + ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); + + _is_multiplayer = gd_mp_api; + _protocols = p_protocols; + _server->listen(p_port); + + return OK; +} + +void WSLServer::poll() { + + List<int> remove_ids; + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr(); + peer->poll(); + if (!peer->is_connected_to_host()) { + _on_disconnect(E->key(), peer->close_code != -1); + remove_ids.push_back(E->key()); + } + } + for (List<int>::Element *E = remove_ids.front(); E; E = E->next()) { + _peer_map.erase(E->get()); + } + remove_ids.clear(); + + List<Ref<PendingPeer> > remove_peers; + for (List<Ref<PendingPeer> >::Element *E = _pending.front(); E; E = E->next()) { + Ref<PendingPeer> ppeer = E->get(); + Error err = ppeer->do_handshake(_protocols); + if (err == ERR_BUSY) { + continue; + } else if (err != OK) { + remove_peers.push_back(ppeer); + continue; + } + // Creating new peer + int32_t id = _gen_unique_id(); + + WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); + data->obj = this; + data->conn = ppeer->connection; + data->is_server = true; + data->id = id; + + Ref<WSLPeer> ws_peer = memnew(WSLPeer); + ws_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); + + _peer_map[id] = ws_peer; + remove_peers.push_back(ppeer); + _on_connect(id, ppeer->protocol); + } + for (List<Ref<PendingPeer> >::Element *E = remove_peers.front(); E; E = E->next()) { + _pending.erase(E->get()); + } + remove_peers.clear(); + + if (!_server->is_listening()) + return; + + while (_server->is_connection_available()) { + Ref<StreamPeer> conn = _server->take_connection(); + if (is_refusing_new_connections()) + continue; // Conn will go out-of-scope and be closed. + + Ref<PendingPeer> peer = memnew(PendingPeer); + peer->connection = conn; + peer->time = OS::get_singleton()->get_ticks_msec(); + _pending.push_back(peer); + } +} + +bool WSLServer::is_listening() const { + return _server->is_listening(); +} + +int WSLServer::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + +void WSLServer::stop() { + _server->stop(); + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr(); + peer->close_now(); + } + _pending.clear(); + _peer_map.clear(); +} + +bool WSLServer::has_peer(int p_id) const { + return _peer_map.has(p_id); +} + +Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const { + ERR_FAIL_COND_V(!has_peer(p_id), NULL); + return _peer_map[p_id]; +} + +IP_Address WSLServer::get_peer_address(int p_peer_id) const { + ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address()); + + return _peer_map[p_peer_id]->get_connected_host(); +} + +int WSLServer::get_peer_port(int p_peer_id) const { + ERR_FAIL_COND_V(!has_peer(p_peer_id), 0); + + return _peer_map[p_peer_id]->get_connected_port(); +} + +void WSLServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { + ERR_FAIL_COND(!has_peer(p_peer_id)); + + get_peer(p_peer_id)->close(p_code, p_reason); +} + +Error WSLServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting"); + ERR_FAIL_COND_V(_server->is_listening(), FAILED); + + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + _out_buf_size = nearest_shift(p_out_buffer - 1) + 10; + _out_pkt_size = nearest_shift(p_out_packets - 1); + return OK; +} + +WSLServer::WSLServer() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1); + _server.instance(); +} + +WSLServer::~WSLServer() { + stop(); +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_server.h b/modules/websocket/wsl_server.h index c43044f194..2ceb941073 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/wsl_server.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* lws_server.h */ +/* wsl_server.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,30 +27,58 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef LWSSERVER_H -#define LWSSERVER_H + +#ifndef WSLSERVER_H +#define WSLSERVER_H #ifndef JAVASCRIPT_ENABLED -#include "core/reference.h" -#include "lws_helper.h" -#include "lws_peer.h" #include "websocket_server.h" +#include "wsl_peer.h" + +#include "core/io/stream_peer_tcp.h" +#include "core/io/tcp_server.h" -class LWSServer : public WebSocketServer { +#define WSL_SERVER_TIMEOUT 1000 - GDCIIMPL(LWSServer, WebSocketServer); +class WSLServer : public WebSocketServer { - LWS_HELPER(LWSServer); + GDCIIMPL(WSLServer, WebSocketServer); private: - Map<int, Ref<LWSPeer> > peer_map; + class PendingPeer : public Reference { + + private: + bool _parse_request(const PoolStringArray p_protocols); + + public: + Ref<StreamPeer> connection; + + int time; + uint8_t req_buf[WSL_MAX_HEADER_SIZE]; + int req_pos; + String key; + String protocol; + bool has_request; + CharString response; + int response_sent; + + PendingPeer(); + + Error do_handshake(const PoolStringArray p_protocols); + }; + int _in_buf_size; int _in_pkt_size; int _out_buf_size; int _out_pkt_size; + List<Ref<PendingPeer> > _pending; + Ref<TCP_Server> _server; + PoolStringArray _protocols; + public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; @@ -60,12 +88,12 @@ public: IP_Address get_peer_address(int p_peer_id) const; int get_peer_port(int p_peer_id) const; void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); - virtual void poll() { _lws_poll(); } + virtual void poll(); - LWSServer(); - ~LWSServer(); + WSLServer(); + ~WSLServer(); }; #endif // JAVASCRIPT_ENABLED -#endif // LWSSERVER_H +#endif // WSLSERVER_H diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub index ad364d5aaf..50e3cb1551 100644 --- a/modules/xatlas_unwrap/SCsub +++ b/modules/xatlas_unwrap/SCsub @@ -1,7 +1,5 @@ #!/usr/bin/env python -import platform - Import('env') Import('env_modules') @@ -15,29 +13,12 @@ if env['builtin_xatlas']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_xatlas_unwrap.Append(CPPPATH=[thirdparty_dir]) + env_xatlas_unwrap.Prepend(CPPPATH=[thirdparty_dir]) # upstream uses c++11 if (not env.msvc): env_xatlas_unwrap.Append(CXXFLAGS="-std=c++11") - if env["platform"] == 'x11': - # if not specifically one of the *BSD, then use LINUX as default - if platform.system() == "FreeBSD": - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) - elif platform.system() == "OpenBSD": - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) - else: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'osx': - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'windows': - if env.msvc: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) - else: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) - env.Append(LIBS=["dbghelp"]) - env_thirdparty = env_xatlas_unwrap.Clone() env_thirdparty.disable_warnings() env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py index 2dda5db7e0..bd092bdc16 100644 --- a/modules/xatlas_unwrap/config.py +++ b/modules/xatlas_unwrap/config.py @@ -1,5 +1,4 @@ def can_build(env, platform): - #return False #xatlas is buggy return (env['tools'] and platform not in ["android", "ios"]) def configure(env): diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp index 57eea4eda6..c18aa04336 100644 --- a/modules/xatlas_unwrap/register_types.cpp +++ b/modules/xatlas_unwrap/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,57 +39,37 @@ extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const flo bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { //set up input mesh - xatlas::InputMesh input_mesh; - input_mesh.indexData = malloc(sizeof(int) * p_index_count); + xatlas::MeshDecl input_mesh; + input_mesh.indexData = p_indices; input_mesh.indexCount = p_index_count; - input_mesh.indexFormat = xatlas::IndexFormat::Float; //really xatlas? - input_mesh.faceMaterialData = (uint16_t *)malloc(sizeof(uint16_t) * p_index_count); - - for (int i = 0; i < p_index_count; i++) { - int *index = (int *)input_mesh.indexData; - index[i] = p_indices[i]; - } - for (int i = 0; i < p_index_count / 3; i++) { - uint16_t *mat_index = (uint16_t *)input_mesh.faceMaterialData; - mat_index[i] = p_face_materials[i]; - } + input_mesh.indexFormat = xatlas::IndexFormat::UInt32; input_mesh.vertexCount = p_vertex_count; - input_mesh.vertexPositionData = malloc(sizeof(float) * p_vertex_count * 3); + input_mesh.vertexPositionData = p_vertices; input_mesh.vertexPositionStride = sizeof(float) * 3; - input_mesh.vertexNormalData = malloc(sizeof(float) * p_vertex_count * 3); - input_mesh.vertexNormalStride = sizeof(float) * 3; - - //material is a better hint than this i guess? + input_mesh.vertexNormalData = p_normals; + input_mesh.vertexNormalStride = sizeof(uint32_t) * 3; input_mesh.vertexUvData = NULL; input_mesh.vertexUvStride = 0; - for (int i = 0; i < p_vertex_count * 3; i++) { - float *vertex_ptr = (float *)input_mesh.vertexPositionData; - float *normal_ptr = (float *)input_mesh.vertexNormalData; + xatlas::ChartOptions chart_options; + xatlas::PackOptions pack_options; - vertex_ptr[i] = p_vertices[i]; - normal_ptr[i] = p_normals[i]; - } - - xatlas::CharterOptions chart_options; - xatlas::PackerOptions pack_options; - - pack_options.method = xatlas::PackMethod::TexelArea; - pack_options.texelArea = 1.0 / p_texel_size; - pack_options.quality = 3; + pack_options.maxChartSize = 4096; + pack_options.bruteForce = true; + pack_options.texelsPerUnit = 1.0 / p_texel_size; xatlas::Atlas *atlas = xatlas::Create(); - printf("adding mesh..\n"); - xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh); - ERR_EXPLAINC(xatlas::StringForEnum(err.code)); - ERR_FAIL_COND_V(err.code != xatlas::AddMeshErrorCode::Success, false); + printf("Adding mesh..\n"); + xatlas::AddMeshError::Enum err = xatlas::AddMesh(atlas, input_mesh, 1); + ERR_EXPLAINC(xatlas::StringForEnum(err)); + ERR_FAIL_COND_V(err != xatlas::AddMeshError::Enum::Success, false); - printf("generate..\n"); - xatlas::Generate(atlas, chart_options, pack_options); + printf("Generate..\n"); + xatlas::Generate(atlas, chart_options, NULL, pack_options); - *r_size_hint_x = xatlas::GetWidth(atlas); - *r_size_hint_y = xatlas::GetHeight(atlas); + *r_size_hint_x = atlas->width; + *r_size_hint_y = atlas->height; float w = *r_size_hint_x; float h = *r_size_hint_y; @@ -98,39 +78,33 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver return false; //could not bake } - const xatlas::OutputMesh *const *output_meshes = xatlas::GetOutputMeshes(atlas); - - const xatlas::OutputMesh *output = output_meshes[0]; + const xatlas::Mesh &output = atlas->meshes[0]; - *r_vertex = (int *)malloc(sizeof(int) * output->vertexCount); - *r_uv = (float *)malloc(sizeof(float) * output->vertexCount * 2); - *r_index = (int *)malloc(sizeof(int) * output->indexCount); + *r_vertex = (int *)malloc(sizeof(int) * output.vertexCount); + *r_uv = (float *)malloc(sizeof(float) * output.vertexCount * 2); + *r_index = (int *)malloc(sizeof(int) * output.indexCount); float max_x = 0; float max_y = 0; - for (int i = 0; i < output->vertexCount; i++) { - (*r_vertex)[i] = output->vertexArray[i].xref; - (*r_uv)[i * 2 + 0] = output->vertexArray[i].uv[0] / w; - (*r_uv)[i * 2 + 1] = output->vertexArray[i].uv[1] / h; - max_x = MAX(max_x, output->vertexArray[i].uv[0]); - max_y = MAX(max_y, output->vertexArray[i].uv[1]); + for (uint32_t i = 0; i < output.vertexCount; i++) { + (*r_vertex)[i] = output.vertexArray[i].xref; + (*r_uv)[i * 2 + 0] = output.vertexArray[i].uv[0] / w; + (*r_uv)[i * 2 + 1] = output.vertexArray[i].uv[1] / h; + max_x = MAX(max_x, output.vertexArray[i].uv[0]); + max_y = MAX(max_y, output.vertexArray[i].uv[1]); } - printf("final texsize: %f,%f - max %f,%f\n", w, h, max_x, max_y); - *r_vertex_count = output->vertexCount; + printf("Final texture size: %f,%f - max %f,%f\n", w, h, max_x, max_y); + *r_vertex_count = output.vertexCount; - for (int i = 0; i < output->indexCount; i++) { - (*r_index)[i] = output->indexArray[i]; + for (uint32_t i = 0; i < output.indexCount; i++) { + (*r_index)[i] = output.indexArray[i]; } - *r_index_count = output->indexCount; + *r_index_count = output.indexCount; //xatlas::Destroy(atlas); - free((void *)input_mesh.indexData); - free((void *)input_mesh.vertexPositionData); - free((void *)input_mesh.vertexNormalData); - free((void *)input_mesh.faceMaterialData); - printf("done"); + printf("Done\n"); return true; } diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h index fd8d56fa53..9b57fff63b 100644 --- a/modules/xatlas_unwrap/register_types.h +++ b/modules/xatlas_unwrap/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |