diff options
Diffstat (limited to 'modules')
113 files changed, 3366 insertions, 402 deletions
diff --git a/modules/bmp/config.py b/modules/bmp/config.py index fb920482f5..1c8cd12a2d 100644 --- a/modules/bmp/config.py +++ b/modules/bmp/config.py @@ -1,7 +1,5 @@ - -def can_build(platform): +def can_build(env, platform): return True - def configure(env): pass diff --git a/modules/bullet/config.py b/modules/bullet/config.py index 0a31c2e503..92dbcf5cb0 100644 --- a/modules/bullet/config.py +++ b/modules/bullet/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 3a1f5d78dd..971fd39509 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -841,20 +841,19 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f } #endif - btTransform body_safe_position; - G_TO_B(p_from, body_safe_position); - UNSCALE_BT_BASIS(body_safe_position); + btTransform body_transform; + G_TO_B(p_from, body_transform); + UNSCALE_BT_BASIS(body_transform); - btVector3 recover_initial_position(0, 0, 0); + btVector3 initial_recover_motion(0, 0, 0); { /// Phase one - multi shapes depenetration using margin for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { - if (!recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, recover_initial_position)) { + if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) { break; } } - // Add recover movement in order to make it safe - body_safe_position.getOrigin() += recover_initial_position; + body_transform.getOrigin() += initial_recover_motion; } btVector3 motion; @@ -885,7 +884,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f } btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex))); - btTransform shape_world_from = body_safe_position * p_body->get_kinematic_utilities()->shapes[shIndex].transform; + btTransform shape_world_from = body_transform * p_body->get_kinematic_utilities()->shapes[shIndex].transform; btTransform shape_world_to(shape_world_from); shape_world_to.getOrigin() += motion; @@ -903,62 +902,49 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f } } - body_safe_position.getOrigin() += motion; + body_transform.getOrigin() += motion; } bool has_penetration = false; - { /// Phase three - Recover + contact test with margin + { /// Phase three - contact test with margin - btVector3 delta_recover_movement(0, 0, 0); + btVector3 __rec(0, 0, 0); RecoverResult r_recover_result; - bool l_has_penetration; - real_t l_penetration_distance = 1e20; - for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { - l_has_penetration = recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, delta_recover_movement, &r_recover_result); + has_penetration = recover_from_penetration(p_body, body_transform, 0, p_infinite_inertia, __rec, &r_recover_result); - if (r_result) { - B_TO_G(motion + delta_recover_movement + recover_initial_position, r_result->motion); + // Parse results + if (r_result) { + B_TO_G(motion + initial_recover_motion, r_result->motion); - if (l_has_penetration) { - has_penetration = true; - if (l_penetration_distance <= r_recover_result.penetration_distance) { - continue; - } + if (has_penetration) { - l_penetration_distance = r_recover_result.penetration_distance; + const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object); + CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer()); - const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object); - CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer()); + B_TO_G(motion, r_result->remainder); // is the remaining movements + r_result->remainder = p_motion - r_result->remainder; - B_TO_G(motion, r_result->remainder); // is the remaining movements - r_result->remainder = p_motion - r_result->remainder; - B_TO_G(r_recover_result.pointWorld, r_result->collision_point); - B_TO_G(r_recover_result.normal, r_result->collision_normal); - B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot - r_result->collider = collisionObject->get_self(); - r_result->collider_id = collisionObject->get_instance_id(); - r_result->collider_shape = r_recover_result.other_compound_shape_index; - r_result->collision_local_shape = r_recover_result.local_shape_most_recovered; + B_TO_G(r_recover_result.pointWorld, r_result->collision_point); + B_TO_G(r_recover_result.normal, r_result->collision_normal); + B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot + r_result->collider = collisionObject->get_self(); + r_result->collider_id = collisionObject->get_instance_id(); + r_result->collider_shape = r_recover_result.other_compound_shape_index; + r_result->collision_local_shape = r_recover_result.local_shape_most_recovered; #if debug_test_motion - Vector3 sup_line2; - B_TO_G(motion, sup_line2); - normalLine->clear(); - normalLine->begin(Mesh::PRIMITIVE_LINES, NULL); - normalLine->add_vertex(r_result->collision_point); - normalLine->add_vertex(r_result->collision_point + r_result->collision_normal * 10); - normalLine->end(); + Vector3 sup_line2; + B_TO_G(motion, sup_line2); + normalLine->clear(); + normalLine->begin(Mesh::PRIMITIVE_LINES, NULL); + normalLine->add_vertex(r_result->collision_point); + normalLine->add_vertex(r_result->collision_point + r_result->collision_normal * 10); + normalLine->end(); #endif - } else { - r_result->remainder = Vector3(); - } } else { - if (!l_has_penetration) - break; - else - has_penetration = true; + r_result->remainder = Vector3(); } } } diff --git a/modules/csg/config.py b/modules/csg/config.py index 5e1d916790..38ccc66d91 100644 --- a/modules/csg/config.py +++ b/modules/csg/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 82db1871da..5f13474d2c 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -162,6 +162,10 @@ CSGBrush *CSGShape::_get_brush() { void CSGShape::_update_shape() { //print_line("updating shape for " + String(get_path())); + + if (parent) + return; + set_base(RID()); root_mesh.unref(); //byebye root mesh @@ -349,6 +353,10 @@ void CSGShape::_notification(int p_what) { Node *parentn = get_parent(); if (parentn) { parent = Object::cast_to<CSGShape>(parentn); + if (parent) { + set_base(RID()); + root_mesh.unref(); + } } if (use_collision && is_root_shape()) { @@ -371,6 +379,7 @@ void CSGShape::_notification(int p_what) { } if (p_what == NOTIFICATION_EXIT_TREE) { + if (parent) parent->_make_dirty(); parent = NULL; @@ -2011,7 +2020,7 @@ void CSGPolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node"), "set_path_node", "get_path_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path"), "set_path_node", "get_path_node"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_path_interval", "get_path_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp index 020724ee59..0dea09808a 100644 --- a/modules/csg/register_types.cpp +++ b/modules/csg/register_types.cpp @@ -30,8 +30,8 @@ #include "register_types.h" -#include "csg_shape.h" #include "csg_gizmos.h" +#include "csg_shape.h" void register_csg_types() { @@ -51,9 +51,7 @@ void register_csg_types() { EditorPlugins::add_by_type<EditorPluginCSG>(); #endif #endif - } void unregister_csg_types() { - } diff --git a/modules/dds/config.py b/modules/dds/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/dds/config.py +++ b/modules/dds/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/enet/config.py b/modules/enet/config.py index 8031fbb4b6..3e30bbe778 100644 --- a/modules/enet/config.py +++ b/modules/enet/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index d5fd4bff09..fab4b05da9 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -7,8 +7,8 @@ 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. </description> <tutorials> - http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html - http://enet.bespin.org/usergroup0.html + <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link> + <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> <demos> </demos> diff --git a/modules/etc/config.py b/modules/etc/config.py index 395fc1bb02..098f1eafa9 100644 --- a/modules/etc/config.py +++ b/modules/etc/config.py @@ -1,9 +1,5 @@ -def can_build(platform): - return True +def can_build(env, platform): + return env['tools'] def configure(env): - # Tools only, disabled for non-tools - # TODO: Find a cleaner way to achieve that - if not env['tools']: - env['module_etc_enabled'] = False - env.disabled_modules.append("etc") + pass diff --git a/modules/freetype/config.py b/modules/freetype/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/freetype/config.py +++ b/modules/freetype/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 6d2f8ce8ad..116a86b27b 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -5,6 +5,7 @@ Import('env') gdn_env = env.Clone() gdn_env.add_source_files(env.modules_sources, "gdnative.cpp") gdn_env.add_source_files(env.modules_sources, "register_types.cpp") +gdn_env.add_source_files(env.modules_sources, "android/*.cpp") gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp") gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp") gdn_env.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") @@ -12,6 +13,7 @@ gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cp gdn_env.Append(CPPPATH=['#modules/gdnative/include/']) +SConscript("net/SCsub") SConscript("arvr/SCsub") SConscript("pluginscript/SCsub") @@ -49,6 +51,7 @@ def _build_gdnative_api_struct_header(api): '#define GODOT_GDNATIVE_API_STRUCT_H', '', '#include <gdnative/gdnative.h>', + '#include <android/godot_android.h>', '#include <arvr/godot_arvr.h>', '#include <nativescript/godot_nativescript.h>', '#include <pluginscript/godot_pluginscript.h>', @@ -194,7 +197,7 @@ def build_gdnative_api_struct(target, source, env): with open(source.path, 'w') as fd: fd.write(_build_gdnative_api_struct_source(api)) -_, gensource = gdn_env.Command(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], +_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], 'gdnative_api.json', build_gdnative_api_struct) gdn_env.add_source_files(env.modules_sources, [gensource]) @@ -275,7 +278,7 @@ def build_gdnative_wrapper_code(target, source, env): if ARGUMENTS.get('gdnative_wrapper', False): #build wrapper code - gensource, = gdn_env.Command('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code) + gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code) gd_wrapper_env = env.Clone() gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) diff --git a/modules/gdnative/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp new file mode 100644 index 0000000000..edc948e086 --- /dev/null +++ b/modules/gdnative/android/android_gdn.cpp @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* android_gdn.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 "modules/gdnative/gdnative.h" + +// Code by Paritosh97 with minor tweaks by Mux213 +// These entry points are only for the android platform and are simple stubs in all others. + +#ifdef __ANDROID__ +#include "platform/android/thread_jandroid.h" +#else +#define JNIEnv void +#define jobject void * +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEnv *GDAPI godot_android_get_env() { +#ifdef __ANDROID__ + return ThreadAndroid::get_env(); +#else + return NULL; +#endif +} + +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); + + return env->NewGlobalRef(context); +#else + return NULL; +#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 49e0a19d9e..385c020a78 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -217,6 +217,10 @@ void ARVRInterfaceGDNative::process() { extern "C" { void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface) { + // If our major version is 0 or bigger then 10, we're likely looking at our constructor pointer from an older plugin + ERR_EXPLAINC("GDNative ARVR interfaces build for Godot 3.0 are not supported"); + ERR_FAIL_COND((p_interface->version.major == 0) || (p_interface->version.major > 10)); + Ref<ARVRInterfaceGDNative> new_interface; new_interface.instance(); new_interface->set_interface((godot_arvr_interface_gdnative *const)p_interface); diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 68148c4d87..c5b37d35b4 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): @@ -10,6 +10,7 @@ def get_doc_classes(): "GDNative", "GDNativeLibrary", "NativeScript", + "PacketPeerGDNative", "PluginScript", ] diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 9fcf61af8a..217fd87c3e 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5962,11 +5962,34 @@ ] }, { + "name": "android", + "type": "ANDROID", + "version": { + "major": 1, + "minor": 0 + }, + "next": null, + "api": [ + { + "name": "godot_android_get_env", + "return_type": "JNIEnv*", + "arguments": [ + ] + }, + { + "name": "godot_android_get_activity", + "return_type": "jobject", + "arguments": [ + ] + } + ] + }, + { "name": "arvr", "type": "ARVR", "version": { "major": 1, - "minor": 0 + "minor": 1 }, "next": null, "api": [ diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h new file mode 100644 index 0000000000..832dac9ac3 --- /dev/null +++ b/modules/gdnative/include/android/godot_android.h @@ -0,0 +1,54 @@ +/*************************************************************************/ +/* godot_android.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 GODOT_ANDROID_GDN_H +#define GODOT_ANDROID_GDN_H + +#include <gdnative/gdnative.h> + +#ifdef __ANDROID__ +#include <jni.h> +#else +#define JNIEnv void +#define jobject void * +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEnv *GDAPI godot_android_get_env(); +jobject GDAPI godot_android_get_activity(); + +#ifdef __cplusplus +} +#endif + +#endif /* !GODOT_ANDROID_GDN_H */ diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h index b9aedc0bef..63de62b507 100644 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ b/modules/gdnative/include/arvr/godot_arvr.h @@ -37,7 +37,15 @@ extern "C" { #endif +// For future versions of the API we should only add new functions at the end of the structure and use the +// version info to detect whether a call is available + +// Use these to populate version in your plugin +#define GODOTVR_API_MAJOR 1 +#define GODOTVR_API_MINOR 0 + typedef struct { + godot_gdnative_api_version version; /* version of our API */ void *(*constructor)(godot_object *); void (*destructor)(void *); godot_string (*get_name)(const void *); diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index cfbe16fa7d..f28ba352ab 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -43,6 +43,9 @@ typedef enum { GODOT_METHOD_RPC_MODE_SYNC, GODOT_METHOD_RPC_MODE_MASTER, GODOT_METHOD_RPC_MODE_SLAVE, + GODOT_METHOD_RPC_MODE_REMOTESYNC, + GODOT_METHOD_RPC_MODE_MASTERSYNC, + GODOT_METHOD_RPC_MODE_SLAVESYNC, } godot_method_rpc_mode; typedef enum { diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h new file mode 100644 index 0000000000..bfa688592d --- /dev/null +++ b/modules/gdnative/include/net/godot_net.h @@ -0,0 +1,118 @@ +/*************************************************************************/ +/* godot_net.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 GODOT_NATIVENET_H +#define GODOT_NATIVENET_H + +#include <gdnative/gdnative.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// For future versions of the API we should only add new functions at the end of the structure and use the +// version info to detect whether a call is available + +// Use these to populate version in your plugin +#define GODOT_NET_API_MAJOR 3 +#define GODOT_NET_API_MINOR 1 + +typedef struct { + + godot_gdnative_api_version version; /* version of our API */ + godot_object *data; /* User reference */ + + /* 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 (*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); + + int (*get_available_bytes)(const void *user); + + void *next; /* For extension? */ +} 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); + +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 *); + + void *next; /* For extension? */ +} godot_net_packet_peer; + +/* Binds a PacketPeerGDNative to the provided interface */ +void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *); + +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 NetworkedMultiplayerPeer */ + void (*set_transfer_mode)(void *, godot_int); + godot_int (*get_transfer_mode)(const void *); + // 0 = broadcast, 1 = server, <0 = all but abs(value) + void (*set_target_peer)(void *, godot_int); + godot_int (*get_packet_peer)(const void *); + godot_bool (*is_server)(const void *); + void (*poll)(void *); + // Must be > 0, 1 is for server + int32_t (*get_unique_id)(const void *); + void (*set_refuse_new_connections)(void *, godot_bool); + godot_bool (*is_refusing_new_connections)(const void *); + godot_int (*get_connection_status)(const void *); + + void *next; /* For extension? Or maybe not... */ +} godot_net_multiplayer_peer; + +/* Binds a MultiplayerPeerGDNative to the provided interface */ +void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *); + +#ifdef __cplusplus +} +#endif + +#endif /* GODOT_NATIVENET_H */ diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index cf8977f3ea..d6abbc1bcf 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -747,7 +747,7 @@ Ref<Script> NativeScriptInstance::get_script() const { return script; } -NativeScriptInstance::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const { NativeScriptDesc *script_data = GET_SCRIPT_DESC(); @@ -757,27 +757,33 @@ NativeScriptInstance::RPCMode NativeScriptInstance::get_rpc_mode(const StringNam if (E) { switch (E->get().rpc_mode) { case GODOT_METHOD_RPC_MODE_DISABLED: - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; case GODOT_METHOD_RPC_MODE_REMOTE: - return RPC_MODE_REMOTE; + return MultiplayerAPI::RPC_MODE_REMOTE; case GODOT_METHOD_RPC_MODE_SYNC: - return RPC_MODE_SYNC; + return MultiplayerAPI::RPC_MODE_SYNC; case GODOT_METHOD_RPC_MODE_MASTER: - return RPC_MODE_MASTER; + return MultiplayerAPI::RPC_MODE_MASTER; case GODOT_METHOD_RPC_MODE_SLAVE: - return RPC_MODE_SLAVE; + return MultiplayerAPI::RPC_MODE_SLAVE; + case GODOT_METHOD_RPC_MODE_REMOTESYNC: + return MultiplayerAPI::RPC_MODE_REMOTESYNC; + case GODOT_METHOD_RPC_MODE_MASTERSYNC: + return MultiplayerAPI::RPC_MODE_MASTERSYNC; + case GODOT_METHOD_RPC_MODE_SLAVESYNC: + return MultiplayerAPI::RPC_MODE_SLAVESYNC; default: - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } } script_data = script_data->base_data; } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -NativeScriptInstance::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const { NativeScriptDesc *script_data = GET_SCRIPT_DESC(); @@ -787,24 +793,24 @@ NativeScriptInstance::RPCMode NativeScriptInstance::get_rset_mode(const StringNa if (E) { switch (E.get().rset_mode) { case GODOT_METHOD_RPC_MODE_DISABLED: - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; case GODOT_METHOD_RPC_MODE_REMOTE: - return RPC_MODE_REMOTE; + return MultiplayerAPI::RPC_MODE_REMOTE; case GODOT_METHOD_RPC_MODE_SYNC: - return RPC_MODE_SYNC; + return MultiplayerAPI::RPC_MODE_SYNC; case GODOT_METHOD_RPC_MODE_MASTER: - return RPC_MODE_MASTER; + return MultiplayerAPI::RPC_MODE_MASTER; case GODOT_METHOD_RPC_MODE_SLAVE: - return RPC_MODE_SLAVE; + return MultiplayerAPI::RPC_MODE_SLAVE; default: - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } } script_data = script_data->base_data; } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } ScriptLanguage *NativeScriptInstance::get_language() { diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 68a8126a32..b47962dc37 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -196,8 +196,8 @@ public: virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); virtual void notification(int p_notification); virtual Ref<Script> get_script() const; - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; virtual ScriptLanguage *get_language(); virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub new file mode 100644 index 0000000000..53f9271128 --- /dev/null +++ b/modules/gdnative/net/SCsub @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import os +import methods + +Import('env') +Import('env_modules') + +env_net_gdnative = env_modules.Clone() + +env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) +env_net_gdnative.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 new file mode 100644 index 0000000000..e2d710b5ad --- /dev/null +++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* multiplayer_peer_gdnative.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 "multiplayer_peer_gdnative.h" + +MultiplayerPeerGDNative::MultiplayerPeerGDNative() { + interface = NULL; +} + +MultiplayerPeerGDNative::~MultiplayerPeerGDNative() { +} + +void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) { + interface = p_interface; +} + +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); +} + +Error MultiplayerPeerGDNative::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 MultiplayerPeerGDNative::get_max_packet_size() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_max_packet_size(interface->data); +} + +int MultiplayerPeerGDNative::get_available_packet_count() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_packet_count(interface->data); +} + +/* NetworkedMultiplayerPeer */ +void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) { + ERR_FAIL_COND(interface == NULL); + interface->set_transfer_mode(interface->data, (godot_int)p_mode); +} + +NetworkedMultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const { + ERR_FAIL_COND_V(interface == NULL, TRANSFER_MODE_UNRELIABLE); + return (TransferMode)interface->get_transfer_mode(interface->data); +} + +void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) { + ERR_FAIL_COND(interface == NULL); + interface->set_target_peer(interface->data, p_peer_id); +} + +int MultiplayerPeerGDNative::get_packet_peer() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_packet_peer(interface->data); +} + +bool MultiplayerPeerGDNative::is_server() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->is_server(interface->data); +} + +void MultiplayerPeerGDNative::poll() { + ERR_FAIL_COND(interface == NULL); + interface->poll(interface->data); +} + +int MultiplayerPeerGDNative::get_unique_id() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_unique_id(interface->data); +} + +void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) { + ERR_FAIL_COND(interface == NULL); + interface->set_refuse_new_connections(interface->data, p_enable); +} + +bool MultiplayerPeerGDNative::is_refusing_new_connections() const { + ERR_FAIL_COND_V(interface == NULL, true); + return interface->is_refusing_new_connections(interface->data); +} + +NetworkedMultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const { + ERR_FAIL_COND_V(interface == NULL, CONNECTION_DISCONNECTED); + return (ConnectionStatus)interface->get_connection_status(interface->data); +} + +void MultiplayerPeerGDNative::_bind_methods() { +} + +extern "C" { + +void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) { + + ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl); +} +} diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h new file mode 100644 index 0000000000..c8c95b3dd7 --- /dev/null +++ b/modules/gdnative/net/multiplayer_peer_gdnative.h @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* multiplayer_peer_gdnative.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 MULTIPLAYER_PEER_GDNATIVE_H +#define MULTIPLAYER_PEER_GDNATIVE_H + +#include "core/io/networked_multiplayer_peer.h" +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" + +class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer { + GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer) + +protected: + static void _bind_methods(); + const godot_net_multiplayer_peer *interface; + +public: + MultiplayerPeerGDNative(); + ~MultiplayerPeerGDNative(); + + /* Sets the interface implementation from GDNative */ + void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl); + + /* Specific to PacketPeer */ + 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; + virtual int get_available_packet_count() const; + + /* Specific to NetworkedMultiplayerPeer */ + virtual void set_transfer_mode(TransferMode p_mode); + virtual TransferMode get_transfer_mode() const; + virtual void set_target_peer(int p_peer_id); + + virtual int get_packet_peer() const; + + virtual bool is_server() const; + + virtual void poll(); + + virtual int get_unique_id() const; + + virtual void set_refuse_new_connections(bool p_enable); + virtual bool is_refusing_new_connections() const; + + virtual ConnectionStatus get_connection_status() const; +}; + +#endif // MULTIPLAYER_PEER_GDNATIVE_H diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp new file mode 100644 index 0000000000..ceae79edc0 --- /dev/null +++ b/modules/gdnative/net/packet_peer_gdnative.cpp @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* packet_peer_gdnative.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 "packet_peer_gdnative.h" + +PacketPeerGDNative::PacketPeerGDNative() { + interface = NULL; +} + +PacketPeerGDNative::~PacketPeerGDNative() { +} + +void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) { + interface = p_impl; +} + +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); +} + +Error PacketPeerGDNative::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 PacketPeerGDNative::get_max_packet_size() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_max_packet_size(interface->data); +} + +int PacketPeerGDNative::get_available_packet_count() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_packet_count(interface->data); +} + +extern "C" { + +void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) { + + ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl); +} +} diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h new file mode 100644 index 0000000000..71814177ed --- /dev/null +++ b/modules/gdnative/net/packet_peer_gdnative.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* packet_peer_gdnative.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 PACKET_PEER_GDNATIVE_H +#define PACKET_PEER_GDNATIVE_H + +#include "core/io/packet_peer.h" +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" + +class PacketPeerGDNative : public PacketPeer { + GDCLASS(PacketPeerGDNative, PacketPeer) + +protected: + static void _bind_methods(); + const godot_net_packet_peer *interface; + +public: + PacketPeerGDNative(); + ~PacketPeerGDNative(); + + /* Sets the interface implementation from GDNative */ + void set_native_packet_peer(const godot_net_packet_peer *p_impl); + + /* Specific to PacketPeer */ + 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; + virtual int get_available_packet_count() const; +}; + +#endif // PACKET_PEER_GDNATIVE_H diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp new file mode 100644 index 0000000000..c3fb4d8008 --- /dev/null +++ b/modules/gdnative/net/register_types.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* 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 "multiplayer_peer_gdnative.h" +#include "packet_peer_gdnative.h" +#include "stream_peer_gdnative.h" + +void register_net_types() { + ClassDB::register_class<MultiplayerPeerGDNative>(); + ClassDB::register_class<PacketPeerGDNative>(); + ClassDB::register_class<StreamPeerGDNative>(); +} + +void unregister_net_types() { +} diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h new file mode 100644 index 0000000000..9545a2ba8f --- /dev/null +++ b/modules/gdnative/net/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.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. */ +/*************************************************************************/ + +void register_net_types(); +void unregister_net_types(); diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp new file mode 100644 index 0000000000..4d1f2ec9a5 --- /dev/null +++ b/modules/gdnative/net/stream_peer_gdnative.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* stream_peer_gdnative.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 "stream_peer_gdnative.h" + +StreamPeerGDNative::StreamPeerGDNative() { + interface = NULL; +} + +StreamPeerGDNative::~StreamPeerGDNative() { +} + +void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) { + interface = p_interface; +} + +void StreamPeerGDNative::_bind_methods() { +} + +Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)(interface->put_data(interface->data, p_data, 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)); +} + +Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)(interface->get_data(interface->data, p_buffer, 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)); +} + +int StreamPeerGDNative::get_available_bytes() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_bytes(interface->data); +} + +extern "C" { + +void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, 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 new file mode 100644 index 0000000000..654234e6ab --- /dev/null +++ b/modules/gdnative/net/stream_peer_gdnative.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* stream_peer_gdnative.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 STREAM_PEER_GDNATIVE_H +#define STREAM_PEER_GDNATIVE_H + +#include "core/io/stream_peer.h" +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" + +class StreamPeerGDNative : public StreamPeer { + + GDCLASS(StreamPeerGDNative, StreamPeer); + +protected: + static void _bind_methods(); + 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); + + /* Specific to StreamPeer */ + Error put_data(const uint8_t *p_data, int p_bytes); + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error get_data(uint8_t *p_buffer, int p_bytes); + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); + int get_available_bytes() const; +}; + +#endif // STREAM_PEER_GDNATIVE_H diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp index 931ab0bfe4..13026e8e7a 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.cpp +++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp @@ -138,11 +138,11 @@ void PluginScriptInstance::notification(int p_notification) { _desc->notification(_data, p_notification); } -ScriptInstance::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const { return _script->get_rpc_mode(p_method); } -ScriptInstance::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const { return _script->get_rset_mode(p_variable); } diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index 3c7b360ad9..8be6a4ccaa 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -76,8 +76,8 @@ public: void set_path(const String &p_path); - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; virtual void refcount_incremented(); virtual bool refcount_decremented(); diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 5ae7926f1b..c3a623e9a1 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -84,35 +84,20 @@ StringName PluginScript::get_instance_base_type() const { } void PluginScript::update_exports() { -// TODO #ifdef TOOLS_ENABLED -#if 0 ASSERT_SCRIPT_VALID(); - if (/*changed &&*/ placeholders.size()) { //hm :( + if (placeholders.size()) { //update placeholders if any Map<StringName, Variant> propdefvalues; List<PropertyInfo> propinfos; - const String *props = (const String *)pybind_get_prop_list(_py_exposed_class); - for (int i = 0; props[i] != ""; ++i) { - const String propname = props[i]; - pybind_get_prop_default_value(_py_exposed_class, propname.c_str(), (godot_variant *)&propdefvalues[propname]); - pybind_prop_info raw_info; - pybind_get_prop_info(_py_exposed_class, propname.c_str(), &raw_info); - PropertyInfo info; - info.type = (Variant::Type)raw_info.type; - info.name = propname; - info.hint = (PropertyHint)raw_info.hint; - info.hint_string = *(String *)&raw_info.hint_string; - info.usage = raw_info.usage; - propinfos.push_back(info); - } + + get_script_property_list(&propinfos); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { - E->get()->update(propinfos, propdefvalues); + E->get()->update(propinfos, _properties_default_values); } } #endif -#endif } // TODO: rename p_this "p_owner" ? @@ -245,9 +230,9 @@ Error PluginScript::reload(bool p_keep_state) { // rpc_mode is passed as an optional field and is not part of MethodInfo Variant var = v["rpc_mode"]; if (var == Variant()) { - _methods_rpc_mode[mi.name] = ScriptInstance::RPC_MODE_DISABLED; + _methods_rpc_mode[mi.name] = MultiplayerAPI::RPC_MODE_DISABLED; } else { - _methods_rpc_mode[mi.name] = ScriptInstance::RPCMode(int(var)); + _methods_rpc_mode[mi.name] = MultiplayerAPI::RPCMode(int(var)); } } Array *signals = (Array *)&manifest.signals; @@ -265,9 +250,9 @@ Error PluginScript::reload(bool p_keep_state) { // rset_mode is passed as an optional field and is not part of PropertyInfo Variant var = v["rset_mode"]; if (var == Variant()) { - _methods_rpc_mode[pi.name] = ScriptInstance::RPC_MODE_DISABLED; + _methods_rpc_mode[pi.name] = MultiplayerAPI::RPC_MODE_DISABLED; } else { - _methods_rpc_mode[pi.name] = ScriptInstance::RPCMode(int(var)); + _methods_rpc_mode[pi.name] = MultiplayerAPI::RPCMode(int(var)); } } // Manifest's attributes must be explicitly freed @@ -402,23 +387,23 @@ int PluginScript::get_member_line(const StringName &p_member) const { return -1; } -ScriptInstance::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const { - ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED); - const Map<StringName, ScriptInstance::RPCMode>::Element *e = _methods_rpc_mode.find(p_method); +MultiplayerAPI::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const { + ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED); + const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _methods_rpc_mode.find(p_method); if (e != NULL) { return e->get(); } else { - return ScriptInstance::RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } } -ScriptInstance::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const { - ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED); - const Map<StringName, ScriptInstance::RPCMode>::Element *e = _variables_rset_mode.find(p_variable); +MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const { + ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED); + const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _variables_rset_mode.find(p_variable); if (e != NULL) { return e->get(); } else { - return ScriptInstance::RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } } diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 1be9e907c2..31c6c4d67f 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -62,8 +62,8 @@ private: Map<StringName, PropertyInfo> _properties_info; Map<StringName, MethodInfo> _signals_info; Map<StringName, MethodInfo> _methods_info; - Map<StringName, ScriptInstance::RPCMode> _variables_rset_mode; - Map<StringName, ScriptInstance::RPCMode> _methods_rpc_mode; + Map<StringName, MultiplayerAPI::RPCMode> _variables_rset_mode; + Map<StringName, MultiplayerAPI::RPCMode> _methods_rpc_mode; Set<Object *> _instances; //exported members @@ -116,8 +116,8 @@ public: virtual int get_member_line(const StringName &p_member) const; - ScriptInstance::RPCMode get_rpc_mode(const StringName &p_method) const; - ScriptInstance::RPCMode get_rset_mode(const StringName &p_variable) const; + MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; PluginScript(); void init(PluginScriptLanguage *language); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index a0b6fbeb75..d18297f2f8 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -38,6 +38,7 @@ #include "arvr/register_types.h" #include "nativescript/register_types.h" +#include "net/register_types.h" #include "pluginscript/register_types.h" #include "core/engine.h" @@ -321,6 +322,7 @@ void register_gdnative_types() { GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); + register_net_types(); register_arvr_types(); register_nativescript_types(); register_pluginscript_types(); @@ -379,6 +381,7 @@ void unregister_gdnative_types() { unregister_pluginscript_types(); unregister_nativescript_types(); unregister_arvr_types(); + unregister_net_types(); memdelete(GDNativeCallRegistry::singleton); diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index 6496b59d75..95b40d90af 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 40a435f459..632970f8c0 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html + <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link> </tutorials> <demos> </demos> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index ea3efff9cf..fedc510f01 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -312,8 +312,24 @@ void GDScriptSyntaxHighlighter::_update_cache() { number_color = text_editor->get_color("number_color"); member_color = text_editor->get_color("member_variable_color"); - function_definition_color = EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", Color::html("#01e1ff")); - node_path_color = EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", Color::html("#64c15a")); + EditorSettings *settings = EditorSettings::get_singleton(); + String text_editor_color_theme = settings->get("text_editor/theme/color_theme"); + + 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"); + + EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color); + EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color); + if (text_editor_color_theme == "Adaptive" || default_theme) { + settings->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", function_definition_color, true); + settings->set_initial_value("text_editor/highlighting/gdscript/node_path_color", node_path_color, true); + } + + function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color"); + node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color"); } SyntaxHighlighter *GDScriptSyntaxHighlighter::create() { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 14bdce50ec..f23e7854a5 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1220,7 +1220,7 @@ ScriptLanguage *GDScriptInstance::get_language() { return GDScriptLanguage::get_singleton(); } -GDScriptInstance::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const { const GDScript *cscript = script.ptr(); @@ -1228,17 +1228,17 @@ GDScriptInstance::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_met const Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.find(p_method); if (E) { - if (E->get()->get_rpc_mode() != RPC_MODE_DISABLED) { + if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) { return E->get()->get_rpc_mode(); } } cscript = cscript->_base; } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -GDScriptInstance::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const { const GDScript *cscript = script.ptr(); @@ -1253,7 +1253,7 @@ GDScriptInstance::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_va cscript = cscript->_base; } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } void GDScriptInstance::reload_members() { @@ -1769,6 +1769,9 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "sync", "master", "slave", + "remotesync", + "mastersync", + "slavesync", 0 }; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 6885fbb7fe..a35b0a10d5 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -63,7 +63,7 @@ class GDScript : public Script { int index; StringName setter; StringName getter; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; }; friend class GDScriptInstance; @@ -248,8 +248,8 @@ public: void reload_members(); - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; GDScriptInstance(); ~GDScriptInstance(); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 4286412c14..c0c3bd7b06 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -54,18 +54,18 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - String _template = String() + - "extends %BASE%\n\n" + - "# class member variables go here, for example:\n" + - "# var a = 2\n" + - "# var b = \"textvar\"\n\n" + - "func _ready():\n" + - "%TS%# Called when the node is added to the scene for the first time.\n" + - "%TS%# Initialization here.\n" + - "%TS%pass\n\n" + - "#func _process(delta):\n" + - "#%TS%# Called every frame. Delta is time since last frame.\n" + - "#%TS%# Update game logic here.\n" + + String _template = "extends %BASE%\n" + "\n" + "# Declare member variables here. Examples:\n" + "# var a = 2\n" + "# var b = \"text\"\n" + "\n" + "# Called when the node enters the scene tree for the first time.\n" + "func _ready():\n" + "%TS%pass # Replace with function body.\n" + "\n" + "# Called every frame. 'delta' is the elapsed time since the previous frame.\n" + "#func _process(delta):\n" "#%TS%pass\n"; _template = _template.replace("%BASE%", p_base_class_name); @@ -118,6 +118,13 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int & funcs[cl->static_functions[i]->line] = cl->static_functions[i]->name; } + 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); + } + } + for (Map<int, String>::Element *E = funcs.front(); E; E = E->next()) { r_functions->push_back(E->get() + ":" + itos(E->key())); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index dac7da3a28..61130cb58f 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1455,7 +1455,7 @@ GDScriptFunction::GDScriptFunction() : _stack_size = 0; _call_size = 0; - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; name = "<anonymous>"; #ifdef DEBUG_ENABLED _func_cname = NULL; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index ea009dcd96..836325f0fe 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -96,14 +96,6 @@ public: ADDR_TYPE_NIL = 9 }; - enum RPCMode { - RPC_DISABLED, - RPC_ENABLED, - RPC_SYNC, - RPC_SYNC_MASTER, - RPC_SYNC_SLAVE - }; - struct StackDebug { int line; @@ -135,7 +127,7 @@ private: int _call_size; int _initial_line; bool _static; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; GDScript *_script; @@ -230,7 +222,7 @@ public: Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError &r_err, CallState *p_state = NULL); - _FORCE_INLINE_ ScriptInstance::RPCMode get_rpc_mode() const { return rpc_mode; } + _FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; } GDScriptFunction(); ~GDScriptFunction(); }; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index e7b0700e76..9650563ee6 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -716,6 +716,14 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = constant; bfn = true; } + + if (!bfn && GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) { + //check from singletons + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = GDScriptLanguage::get_singleton()->get_named_globals_map()[identifier]; + expr = constant; + bfn = true; + } } if (!bfn) { @@ -3365,7 +3373,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { function->line = fnline; function->rpc_mode = rpc_mode; - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; if (_static) p_class->static_functions.push_back(function); @@ -3923,10 +3931,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); } - if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC) { + if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVESYNC) { current_export = PropertyInfo(); - _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'."); + _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave', 'sync', 'remotesync', 'mastersync', 'slavesync'."); return; } @@ -3959,7 +3967,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } - rpc_mode = ScriptInstance::RPC_MODE_REMOTE; + rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE; continue; } break; @@ -3980,7 +3988,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } - rpc_mode = ScriptInstance::RPC_MODE_MASTER; + rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; continue; } break; case GDScriptTokenizer::TK_PR_SLAVE: { @@ -4000,9 +4008,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } - rpc_mode = ScriptInstance::RPC_MODE_SLAVE; + rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE; continue; } break; + case GDScriptTokenizer::TK_PR_REMOTESYNC: case GDScriptTokenizer::TK_PR_SYNC: { //may be fallthrough from export, ignore if so @@ -4015,7 +4024,37 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - rpc_mode = ScriptInstance::RPC_MODE_SYNC; + rpc_mode = MultiplayerAPI::RPC_MODE_SYNC; + continue; + } break; + case GDScriptTokenizer::TK_PR_MASTERSYNC: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) { + if (current_export.type) + _set_error("Expected 'var'."); + else + _set_error("Expected 'var' or 'func'."); + return; + } + + rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC; + continue; + } break; + case GDScriptTokenizer::TK_PR_SLAVESYNC: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) { + if (current_export.type) + _set_error("Expected 'var'."); + else + _set_error("Expected 'var' or 'func'."); + return; + } + + rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC; continue; } break; case GDScriptTokenizer::TK_PR_VAR: { @@ -4045,7 +4084,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { @@ -4054,7 +4093,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { #endif tokenizer->advance(); - Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport); + Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL); if (!subexpr) { if (_recover_from_completion()) { break; @@ -4470,7 +4509,7 @@ void GDScriptParser::clear() { current_class = NULL; completion_found = false; - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; current_function = NULL; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 485ba1263d..b88a59537c 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -89,7 +89,7 @@ public: StringName getter; int line; Node *expression; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; }; struct Constant { StringName identifier; @@ -125,7 +125,7 @@ public: struct FunctionNode : public Node { bool _static; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; StringName name; Vector<StringName> arguments; Vector<Node *> default_values; @@ -134,7 +134,7 @@ public: FunctionNode() { type = TYPE_FUNCTION; _static = false; - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; } }; @@ -493,7 +493,7 @@ private: PropertyInfo current_export; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; void _set_error(const String &p_error, int p_line = -1, int p_column = -1); bool _recover_from_completion(); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 6a844cd651..3c8e1ddbe4 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -110,6 +110,9 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "sync", "master", "slave", + "remotesync", + "mastersync", + "slavesync", "'['", "']'", "'{'", @@ -201,6 +204,9 @@ static const _kws _keyword_list[] = { { GDScriptTokenizer::TK_PR_MASTER, "master" }, { GDScriptTokenizer::TK_PR_SLAVE, "slave" }, { GDScriptTokenizer::TK_PR_SYNC, "sync" }, + { GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" }, + { GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" }, + { GDScriptTokenizer::TK_PR_SLAVESYNC, "slavesync" }, { GDScriptTokenizer::TK_PR_CONST, "const" }, { GDScriptTokenizer::TK_PR_ENUM, "enum" }, //controlflow @@ -247,6 +253,9 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const case TK_PR_MASTER: case TK_PR_SLAVE: case TK_PR_SYNC: + case TK_PR_REMOTESYNC: + case TK_PR_MASTERSYNC: + case TK_PR_SLAVESYNC: return true; // Literal for non-variables only: diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index b020c85199..c4f1f9fd94 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -115,6 +115,9 @@ public: TK_PR_SYNC, TK_PR_MASTER, TK_PR_SLAVE, + TK_PR_REMOTESYNC, + TK_PR_MASTERSYNC, + TK_PR_SLAVESYNC, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py index a93f4edb81..5022116c9b 100644 --- a/modules/gridmap/config.py +++ b/modules/gridmap/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index bb652f3bdf..d5f9563600 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -10,7 +10,7 @@ A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. </description> <tutorials> - http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html + <link>http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html</link> </tutorials> <demos> </demos> diff --git a/modules/hdr/config.py b/modules/hdr/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/hdr/config.py +++ b/modules/hdr/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/jpg/config.py b/modules/jpg/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/jpg/config.py +++ b/modules/jpg/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/mbedtls/config.py b/modules/mbedtls/config.py index 5f133eba90..1c8cd12a2d 100755 --- a/modules/mbedtls/config.py +++ b/modules/mbedtls/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py index aa8ef111d3..4912457e2b 100644 --- a/modules/mobile_vr/config.py +++ b/modules/mobile_vr/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): # should probably change this to only be true on iOS and Android return False diff --git a/modules/mono/SCsub b/modules/mono/SCsub index a1dfcf6377..03e187e5b0 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -127,15 +127,24 @@ def find_msbuild_windows(): if not mono_root: raise RuntimeError('Cannot find mono root directory') + 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') + + if os.path.isfile(msbuild_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'), os.path.join(mono_root, 'lib', 'mono', '4.5')) - else: - msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat') - - if os.path.isfile(msbuild_mono): - return (msbuild_mono, '') + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) return None @@ -145,14 +154,21 @@ def mono_build_solution(source, target, env): import mono_reg_utils as monoreg from shutil import copyfile - framework_path_override = '' + framework_path = '' + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] 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_override = msbuild_info[1] + framework_path = msbuild_info[1] + msbuild_env.update(msbuild_info[2]) else: msbuild_path = find_msbuild_unix('msbuild') if msbuild_path is None: @@ -183,14 +199,8 @@ def mono_build_solution(source, target, env): '/p:Configuration=' + build_config, ] - if framework_path_override: - msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override] - - msbuild_env = os.environ.copy() - - # Needed when running from Developer Command Prompt for VS - if 'PLATFORM' in msbuild_env: - del msbuild_env['PLATFORM'] + if framework_path: + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] try: subprocess.check_call(msbuild_args, env=msbuild_env) diff --git a/modules/mono/config.py b/modules/mono/config.py index 18d9c67795..ebf8512fb6 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -4,7 +4,7 @@ import os import sys import subprocess -from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables +from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') @@ -19,7 +19,7 @@ def find_file_in_dir(directory, files, prefix='', extension=''): return '' -def can_build(platform): +def can_build(env, platform): if platform in ["javascript"]: return False # Not yet supported return True @@ -42,13 +42,24 @@ def copy_file(src_dir, dst_dir, name): copyfile(src_path, dst_path) +def custom_path_is_dir_create(key, val, env): + """Validator to check if Path is a directory, creating it if it does not exist. + Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and + SCons.Script.File() in order to support the '#' top level directory token. + """ + # Dir constructor will throw an error if the path points to a file + fsDir = Dir(val) + if not fsDir.exists: + os.makedirs(fsDir.abspath) + + def configure(env): env.use_ptrcall = True env.add_module_version_string("mono") envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) - envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', PathVariable.PathIsDirCreate)) + envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', custom_path_is_dir_create)) envvars.Update(env) bits = env['bits'] @@ -80,7 +91,11 @@ def configure(env): if mono_static: lib_suffix = Environment()['LIBSUFFIX'] - mono_static_lib_name = 'libmono-static-sgen' + + 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) @@ -93,7 +108,10 @@ def configure(env): env.Append(LINKFLAGS='LIBCMT' + lib_suffix) env.Append(LINKFLAGS='Psapi' + lib_suffix) else: - env.Append(LIBS=mono_static_lib_name) + 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') @@ -128,6 +146,22 @@ def configure(env): if os.getenv('MONO64_PREFIX'): mono_root = os.getenv('MONO64_PREFIX') + # 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: + def pkgconfig_try_find_mono_root(): + 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 '' + mono_root = pkgconfig_try_find_mono_root() + 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: mono_lib_path = os.path.join(mono_root, 'lib') @@ -168,8 +202,7 @@ def configure(env): copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: - if mono_static: - raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') + assert not mono_static env.ParseConfig('pkg-config monosgen-2 --cflags --libs') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 24292b77ed..2420cdb4af 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -298,22 +298,20 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "\n" "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n" "{\n" - " // Member variables here, example:\n" + " // Declare member variables here. Examples:\n" " // private int a = 2;\n" - " // private string b = \"textvar\";\n" + " // private string b = \"text\";\n" "\n" + " // Called when the node enters the scene tree for the first time." " public override void _Ready()\n" " {\n" - " // Called every time the node is added to the scene.\n" - " // Initialization here.\n" - " \n" + " " " }\n" "\n" + "// // Called every frame. 'delta' is the elapsed time since the previous frame." "// public override void _Process(float delta)\n" "// {\n" - "// // Called every frame. Delta is time since last frame.\n" - "// // Update game logic here.\n" - "// \n" + "// " "// }\n" "}\n"; @@ -1310,21 +1308,27 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -ScriptInstance::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { +MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; + return MultiplayerAPI::RPC_MODE_REMOTE; if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; + return MultiplayerAPI::RPC_MODE_SYNC; if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; + return MultiplayerAPI::RPC_MODE_MASTER; if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; + return MultiplayerAPI::RPC_MODE_SLAVE; + if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute))) + return MultiplayerAPI::RPC_MODE_REMOTESYNC; + if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) + return MultiplayerAPI::RPC_MODE_MASTERSYNC; + if (p_member->has_attribute(CACHED_CLASS(SlaveSyncAttribute))) + return MultiplayerAPI::RPC_MODE_SLAVESYNC; - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { GDMonoClass *top = script->script_class; @@ -1337,10 +1341,10 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) top = top->get_parent_class(); } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const { GDMonoClass *top = script->script_class; @@ -1358,7 +1362,7 @@ ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab top = top->get_parent_class(); } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } void CSharpInstance::notification(int p_notification) { @@ -1726,6 +1730,12 @@ void CSharpScript::_clear() { Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + if (unlikely(GDMono::get_singleton() == NULL)) { + // Probably not the best error but eh. + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return Variant(); + } + GDMonoClass *top = script_class; while (top && top != native) { @@ -1967,15 +1977,15 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { return NULL; #endif } - + if (!script_class) { if (GDMono::get_singleton()->get_project_assembly() == NULL) { // The project assembly is not loaded ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); ERR_FAIL_V(NULL); } - - // The project assembly is loaded, but the class could not found + + // The project assembly is loaded, but the class could not found ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); ERR_FAIL_V(NULL); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 8666149111..cae2bbf40a 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -192,7 +192,7 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); - RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; public: MonoObject *get_mono_object() const; @@ -213,8 +213,8 @@ public: virtual void refcount_incremented(); virtual bool refcount_decremented(); - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; virtual void notification(int p_notification); void call_notification_no_check(MonoObject *p_mono_object, int p_notification); diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs index f3b4b66663..16beacb45c 100644 --- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs @@ -78,6 +78,8 @@ namespace GodotSharpTools.Build public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) { + bool debugMSBuild = IsDebugMSBuildRequested(); + List<string> customPropertiesList = new List<string>(); if (customProperties != null) @@ -92,9 +94,10 @@ namespace GodotSharpTools.Build ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); - // No console output, thanks - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; + bool redirectOutput = !debugMSBuild; + + startInfo.RedirectStandardOutput = redirectOutput; + startInfo.RedirectStandardError = redirectOutput; startInfo.UseShellExecute = false; if (UsingMonoMSBuildOnWindows) @@ -116,8 +119,11 @@ namespace GodotSharpTools.Build process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + if (redirectOutput) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } process.WaitForExit(); @@ -129,6 +135,8 @@ namespace GodotSharpTools.Build public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) { + bool debugMSBuild = IsDebugMSBuildRequested(); + if (process != null) throw new InvalidOperationException("Already in use"); @@ -146,9 +154,10 @@ namespace GodotSharpTools.Build ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); - // No console output, thanks - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; + bool redirectOutput = !debugMSBuild; + + startInfo.RedirectStandardOutput = redirectOutput; + startInfo.RedirectStandardError = redirectOutput; startInfo.UseShellExecute = false; if (UsingMonoMSBuildOnWindows) @@ -171,8 +180,11 @@ namespace GodotSharpTools.Build process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + if (redirectOutput) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } return true; } @@ -220,6 +232,11 @@ namespace GodotSharpTools.Build Dispose(); } + private static bool IsDebugMSBuildRequested() + { + return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1"; + } + public void Dispose() { if (process != null) diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index e29384aabf..43d58caad2 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -461,12 +461,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { exit_code = -1; - String logs_dir = GodotSharpDirs::get_build_logs_dir().plus_file(build_info.solution.md5_text() + "_" + build_info.configuration); + String log_dirpath = build_info.get_log_dirpath(); if (build_tab) { build_tab->on_build_start(); } else { - build_tab = memnew(MonoBuildTab(build_info, logs_dir)); + build_tab = memnew(MonoBuildTab(build_info, log_dirpath)); MonoBottomPanel::get_singleton()->add_build_tab(build_tab); } @@ -488,12 +488,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Remove old issues file String issues_file = "msbuild_issues.csv"; - DirAccessRef d = DirAccess::create_for_path(logs_dir); + 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(logs_dir).plus_file(issues_file); + 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); @@ -527,8 +527,9 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Call Build - Variant logger_assembly = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(EDITOR_TOOLS_ASSEMBLY_NAME) + ".dll"; - Variant logger_output_dir = logs_dir; + 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 }; diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 1b5a303835..9317550d28 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -73,7 +73,7 @@ void MonoBottomPanel::_update_build_tabs_list() { if (no_current_tab || current_tab == i) { build_tabs_list->select(i); - _build_tab_item_selected(i); + _build_tabs_item_selected(i); } } } @@ -105,21 +105,27 @@ void MonoBottomPanel::show_build_tab() { ERR_PRINT("Builds tab not found"); } -void MonoBottomPanel::_build_tab_item_selected(int p_idx) { +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_tab_changed(int p_idx) { +void MonoBottomPanel::_build_tabs_nothing_selected() { - if (p_idx < 0 || p_idx >= build_tabs->get_tab_count()) { - warnings_btn->set_visible(false); - errors_btn->set_visible(false); - } else { - warnings_btn->set_visible(true); - errors_btn->set_visible(true); - } + 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) { @@ -148,6 +154,22 @@ void MonoBottomPanel::_build_project_pressed() { CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); } +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) { @@ -163,10 +185,11 @@ void MonoBottomPanel::_notification(int p_what) { 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_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected); - ClassDB::bind_method(D_METHOD("_build_tab_changed", "idx"), &MonoBottomPanel::_build_tab_changed); + 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) { @@ -223,6 +246,15 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { 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); @@ -230,14 +262,14 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { build_tabs_list = memnew(ItemList); build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL); - build_tabs_list->connect("item_selected", this, "_build_tab_item_selected"); + 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); - build_tabs->connect("tab_changed", this, "_build_tab_changed"); hsc->add_child(build_tabs); } } diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h index a44d3a9af8..03240e9a13 100644 --- a/modules/mono/editor/mono_bottom_panel.h +++ b/modules/mono/editor/mono_bottom_panel.h @@ -53,16 +53,18 @@ class MonoBottomPanel : public VBoxContainer { Button *warnings_btn; Button *errors_btn; + Button *view_log_btn; void _update_build_tabs_list(); - void _build_tab_item_selected(int p_idx); - void _build_tab_changed(int p_idx); + 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; diff --git a/modules/mono/editor/mono_build_info.cpp b/modules/mono/editor/mono_build_info.cpp new file mode 100644 index 0000000000..e4af2aac4f --- /dev/null +++ b/modules/mono/editor/mono_build_info.cpp @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* mono_build_info.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_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); +} + +MonoBuildInfo::MonoBuildInfo() {} + +MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) { + + solution = p_solution; + configuration = p_config; +} diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/editor/mono_build_info.h index 4806764a61..64ba0f4037 100644 --- a/modules/mono/editor/mono_build_info.h +++ b/modules/mono/editor/mono_build_info.h @@ -31,35 +31,25 @@ #ifndef MONO_BUILD_INFO_H #define MONO_BUILD_INFO_H -#include "../mono_gd/gd_mono_utils.h" +#include "core/ustring.h" +#include "core/vector.h" struct MonoBuildInfo { struct Hasher { - static _FORCE_INLINE_ uint32_t 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; - } + static uint32_t hash(const MonoBuildInfo &p_key); }; String solution; String configuration; Vector<String> custom_props; - MonoBuildInfo() {} + bool operator==(const MonoBuildInfo &p_b) const; - MonoBuildInfo(const String &p_solution, const String &p_config) { - solution = p_solution; - configuration = p_config; - } + String get_log_dirpath(); - bool operator==(const MonoBuildInfo &p_b) const { - return p_b.solution == solution && p_b.configuration == configuration; - } + MonoBuildInfo(); + MonoBuildInfo(const String &p_solution, const String &p_config); }; #endif // MONO_BUILD_INFO_H diff --git a/modules/mono/glue/cs_files/RPCAttributes.cs b/modules/mono/glue/cs_files/RPCAttributes.cs index 08841ffd76..6bf9560bfa 100644 --- a/modules/mono/glue/cs_files/RPCAttributes.cs +++ b/modules/mono/glue/cs_files/RPCAttributes.cs @@ -13,4 +13,13 @@ namespace Godot [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] public class SlaveAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class RemoteSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class MasterSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SlaveSyncAttribute : Attribute {} } diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index e6baea3089..c04fcca962 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -606,6 +606,8 @@ MonoArray *Array_to_mono_array(const Array &p_array) { Array mono_array_to_Array(MonoArray *p_array) { Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -631,6 +633,8 @@ MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) { PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) { PoolIntArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); for (int i = 0; i < length; i++) { @@ -653,6 +657,8 @@ MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) { PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) { PoolByteArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -676,6 +682,8 @@ MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) { PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) { PoolRealArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -700,6 +708,8 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) { PoolStringArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -732,6 +742,8 @@ MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) { PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) { PoolColorArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -763,6 +775,8 @@ MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) { PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) { PoolVector2Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -795,6 +809,8 @@ MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) { PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { PoolVector3Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); ret.resize(length); @@ -835,6 +851,9 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { Dictionary ret; + if (!p_dict) + return ret; + GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays); MonoArray *keys = NULL; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index db136a1313..fbdbeaa3f7 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -120,6 +120,9 @@ void MonoCache::clear_members() { class_SyncAttribute = NULL; class_MasterAttribute = NULL; class_SlaveAttribute = NULL; + class_RemoteSyncAttribute = NULL; + class_MasterSyncAttribute = NULL; + class_SlaveSyncAttribute = NULL; class_GodotMethodAttribute = NULL; field_GodotMethodAttribute_methodName = NULL; @@ -208,6 +211,9 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); + CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute)); + CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute)); + CACHE_CLASS_AND_CHECK(SlaveSyncAttribute, GODOT_API_CLASS(SlaveSyncAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 1a34180d15..fc13a00e85 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -112,6 +112,9 @@ struct MonoCache { GDMonoClass *class_ToolAttribute; GDMonoClass *class_RemoteAttribute; GDMonoClass *class_SyncAttribute; + GDMonoClass *class_RemoteSyncAttribute; + GDMonoClass *class_MasterSyncAttribute; + GDMonoClass *class_SlaveSyncAttribute; GDMonoClass *class_MasterAttribute; GDMonoClass *class_SlaveAttribute; GDMonoClass *class_GodotMethodAttribute; diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index 9c188d07a7..c8ebb54ded 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/mono_reg_utils.py @@ -60,10 +60,10 @@ def _find_mono_in_reg_old(subkey, bits): def find_mono_root_dir(bits): root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits) if root_dir is not None: - return root_dir + return str(root_dir) root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits) if root_dir is not None: - return root_dir + return str(root_dir) return '' diff --git a/modules/ogg/config.py b/modules/ogg/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/ogg/config.py +++ b/modules/ogg/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/opus/config.py b/modules/opus/config.py index 60f8d838d6..a1cde10ea0 100644 --- a/modules/opus/config.py +++ b/modules/opus/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/pvr/config.py b/modules/pvr/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/pvr/config.py +++ b/modules/pvr/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/recast/SCsub b/modules/recast/SCsub index 530df9a37c..f56be72b24 100644 --- a/modules/recast/SCsub +++ b/modules/recast/SCsub @@ -1,8 +1,9 @@ #!/usr/bin/env python Import('env') +Import('env_modules') -# Not building in a separate env as core needs it +env_recast = env_modules.Clone() # Thirdparty source files if env['builtin_recast']: @@ -22,13 +23,10 @@ if env['builtin_recast']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/Include"]) - - lib = env.add_library("recast_builtin", thirdparty_sources) - env.Append(LIBS=[lib]) + env_recast.add_source_files(env.modules_sources, thirdparty_sources) + env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"]) # Godot source files -env.add_source_files(env.modules_sources, "*.cpp") -env.Append(CCFLAGS=['-DRECAST_ENABLED']) +env_recast.add_source_files(env.modules_sources, "*.cpp") Export('env') diff --git a/modules/recast/config.py b/modules/recast/config.py index fc074cf661..098f1eafa9 100644 --- a/modules/recast/config.py +++ b/modules/recast/config.py @@ -1,5 +1,5 @@ -def can_build(platform): - return platform != "android" +def can_build(env, platform): + return env['tools'] def configure(env): pass diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp new file mode 100644 index 0000000000..8556b7aa0a --- /dev/null +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -0,0 +1,163 @@ +/*************************************************************************/ +/* navigation_mesh_editor_plugin.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 "navigation_mesh_editor_plugin.h" + +#include "io/marshalls.h" +#include "io/resource_saver.h" +#include "scene/3d/mesh_instance.h" +#include "scene/gui/box_container.h" + +void NavigationMeshEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + + hide(); + } +} + +void NavigationMeshEditor::_notification(int p_option) { + + if (p_option == NOTIFICATION_ENTER_TREE) { + + button_bake->set_icon(get_icon("Bake", "EditorIcons")); + button_reset->set_icon(get_icon("Reload", "EditorIcons")); + } +} + +void NavigationMeshEditor::_bake_pressed() { + + 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); + + if (node) { + node->update_gizmo(); + } +} + +void NavigationMeshEditor::_clear_pressed() { + + if (node) + NavigationMeshGenerator::clear(node->get_navigation_mesh()); + + button_bake->set_pressed(false); + bake_info->set_text(""); + + if (node) { + node->update_gizmo(); + } +} + +void NavigationMeshEditor::edit(NavigationMeshInstance *p_nav_mesh_instance) { + + if (p_nav_mesh_instance == NULL || node == p_nav_mesh_instance) { + return; + } + + node = p_nav_mesh_instance; +} + +void NavigationMeshEditor::_bind_methods() { + + ClassDB::bind_method("_bake_pressed", &NavigationMeshEditor::_bake_pressed); + ClassDB::bind_method("_clear_pressed", &NavigationMeshEditor::_clear_pressed); +} + +NavigationMeshEditor::NavigationMeshEditor() { + + bake_hbox = memnew(HBoxContainer); + button_bake = memnew(ToolButton); + button_bake->set_text(TTR("Bake!")); + button_bake->set_toggle_mode(true); + button_reset = memnew(Button); + button_bake->set_tooltip(TTR("Bake the navigation mesh.") + "\n"); + + bake_info = memnew(Label); + bake_hbox->add_child(button_bake); + bake_hbox->add_child(button_reset); + bake_hbox->add_child(bake_info); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); + node = NULL; + + button_bake->connect("pressed", this, "_bake_pressed"); + button_reset->connect("pressed", this, "_clear_pressed"); + button_reset->set_tooltip(TTR("Clear the navigation mesh.")); +} + +NavigationMeshEditor::~NavigationMeshEditor() { +} + +void NavigationMeshEditorPlugin::edit(Object *p_object) { + + navigation_mesh_editor->edit(Object::cast_to<NavigationMeshInstance>(p_object)); +} + +bool NavigationMeshEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("NavigationMeshInstance"); +} + +void NavigationMeshEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + navigation_mesh_editor->show(); + navigation_mesh_editor->bake_hbox->show(); + } else { + + navigation_mesh_editor->hide(); + navigation_mesh_editor->bake_hbox->hide(); + navigation_mesh_editor->edit(NULL); + } +} + +NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) { + + editor = p_node; + navigation_mesh_editor = memnew(NavigationMeshEditor); + editor->get_viewport()->add_child(navigation_mesh_editor); + add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox); + navigation_mesh_editor->hide(); + navigation_mesh_editor->bake_hbox->hide(); +} + +NavigationMeshEditorPlugin::~NavigationMeshEditorPlugin() { +} diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h new file mode 100644 index 0000000000..4f3e222002 --- /dev/null +++ b/modules/recast/navigation_mesh_editor_plugin.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* navigation_mesh_editor_plugin.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 NAVIGATION_MESH_GENERATOR_PLUGIN_H +#define NAVIGATION_MESH_GENERATOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "navigation_mesh_generator.h" + +class NavigationMeshEditor : public Control { + friend class NavigationMeshEditorPlugin; + + GDCLASS(NavigationMeshEditor, Control); + + AcceptDialog *err_dialog; + + HBoxContainer *bake_hbox; + Button *button_bake; + Button *button_reset; + Label *bake_info; + + NavigationMeshInstance *node; + + void _bake_pressed(); + void _clear_pressed(); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + void _notification(int p_option); + +public: + void edit(NavigationMeshInstance *p_nav_mesh_instance); + NavigationMeshEditor(); + ~NavigationMeshEditor(); +}; + +class NavigationMeshEditorPlugin : public EditorPlugin { + + GDCLASS(NavigationMeshEditorPlugin, EditorPlugin); + + NavigationMeshEditor *navigation_mesh_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "NavigationMesh"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + NavigationMeshEditorPlugin(EditorNode *p_node); + ~NavigationMeshEditorPlugin(); +}; + +#endif // NAVIGATION_MESH_GENERATOR_PLUGIN_H diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp new file mode 100644 index 0000000000..64c4b85269 --- /dev/null +++ b/modules/recast/navigation_mesh_generator.cpp @@ -0,0 +1,304 @@ +/*************************************************************************/ +/* navigation_mesh_generator.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 "navigation_mesh_generator.h" + +void NavigationMeshGenerator::_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) { + int current_vertex_count = 0; + + for (int i = 0; i < p_mesh->get_surface_count(); i++) { + current_vertex_count = p_verticies.size() / 3; + + if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) + continue; + + int index_count = 0; + if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { + index_count = p_mesh->surface_get_array_index_len(i); + } else { + index_count = p_mesh->surface_get_array_len(i); + } + + ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0)); + + int face_count = index_count / 3; + + Array a = p_mesh->surface_get_arrays(i); + + PoolVector<Vector3> mesh_vertices = a[Mesh::ARRAY_VERTEX]; + PoolVector<Vector3>::Read vr = mesh_vertices.read(); + + if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { + + 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 i = 0; i < face_count; i++) { + // 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])); + } + } 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)); + } + } + } +} + +void NavigationMeshGenerator::_parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices) { + + if (Object::cast_to<MeshInstance>(p_node)) { + + 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); + } + } + + 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); + } +} + +void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) { + + PoolVector<Vector3> nav_vertices; + + for (int i = 0; i < p_detail_mesh->nverts; i++) { + const float *v = &p_detail_mesh->verts[i * 3]; + nav_vertices.append(Vector3(v[0], v[1], v[2])); + } + p_nav_mesh->set_vertices(nav_vertices); + + for (int i = 0; i < p_detail_mesh->nmeshes; i++) { + const unsigned int *m = &p_detail_mesh->meshes[i * 4]; + const unsigned int bverts = m[0]; + const unsigned int btris = m[2]; + const unsigned int ntris = m[3]; + const unsigned char *tris = &p_detail_mesh->tris[btris * 4]; + for (unsigned int j = 0; j < ntris; j++) { + Vector<int> nav_indices; + nav_indices.resize(3); + nav_indices[0] = ((int)(bverts + tris[j * 4 + 0])); + nav_indices[1] = ((int)(bverts + tris[j * 4 + 1])); + nav_indices[2] = ((int)(bverts + tris[j * 4 + 2])); + p_nav_mesh->add_polygon(nav_indices); + } + } +} + +void NavigationMeshGenerator::_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; + ep->step(TTR("Setting up Configuration..."), 1); + + const float *verts = vertices.ptr(); + const int nverts = vertices.size() / 3; + const int *tris = indices.ptr(); + const int ntris = indices.size() / 3; + + float bmin[3], bmax[3]; + rcCalcBounds(verts, nverts, bmin, bmax); + + rcConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + + cfg.cs = p_nav_mesh->get_cell_size(); + cfg.ch = p_nav_mesh->get_cell_height(); + cfg.walkableSlopeAngle = p_nav_mesh->get_agent_max_slope(); + cfg.walkableHeight = (int)Math::ceil(p_nav_mesh->get_agent_height() / cfg.ch); + cfg.walkableClimb = (int)Math::floor(p_nav_mesh->get_agent_max_climb() / cfg.ch); + cfg.walkableRadius = (int)Math::ceil(p_nav_mesh->get_agent_radius() / cfg.cs); + cfg.maxEdgeLen = (int)(p_nav_mesh->get_edge_max_length() / p_nav_mesh->get_cell_size()); + cfg.maxSimplificationError = p_nav_mesh->get_edge_max_error(); + cfg.minRegionArea = (int)(p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size()); + cfg.mergeRegionArea = (int)(p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size()); + cfg.maxVertsPerPoly = (int)p_nav_mesh->get_verts_per_poly(); + cfg.detailSampleDist = p_nav_mesh->get_detail_sample_distance() < 0.9f ? 0 : p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance(); + cfg.detailSampleMaxError = p_nav_mesh->get_cell_height() * p_nav_mesh->get_detail_sample_max_error(); + + cfg.bmin[0] = bmin[0]; + cfg.bmin[1] = bmin[1]; + cfg.bmin[2] = bmin[2]; + cfg.bmax[0] = bmax[0]; + cfg.bmax[1] = bmax[1]; + cfg.bmax[2] = bmax[2]; + + ep->step(TTR("Calculating grid size..."), 2); + rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); + + ep->step(TTR("Creating heightfield..."), 3); + hf = rcAllocHeightfield(); + + ERR_FAIL_COND(!hf); + ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)); + + ep->step(TTR("Marking walkable triangles..."), 4); + { + Vector<unsigned char> tri_areas; + tri_areas.resize(ntris); + + ERR_FAIL_COND(tri_areas.size() == 0); + + memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char)); + rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw()); + + ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb)); + } + + if (p_nav_mesh->get_filter_low_hanging_obstacles()) + rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf); + if (p_nav_mesh->get_filter_ledge_spans()) + rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); + if (p_nav_mesh->get_filter_walkable_low_height_spans()) + rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf); + + ep->step(TTR("Constructing compact heightfield..."), 5); + + chf = rcAllocCompactHeightfield(); + + ERR_FAIL_COND(!chf); + ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf)); + + rcFreeHeightField(hf); + hf = 0; + + ep->step(TTR("Eroding walkable area..."), 6); + ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)); + + ep->step(TTR("Partitioning..."), 7); + if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { + ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf)); + ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); + } else if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) { + ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); + } else { + ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea)); + } + + ep->step(TTR("Creating contours..."), 8); + + cset = rcAllocContourSet(); + + ERR_FAIL_COND(!cset); + ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset)); + + ep->step(TTR("Creating polymesh..."), 9); + + poly_mesh = rcAllocPolyMesh(); + ERR_FAIL_COND(!poly_mesh); + ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh)); + + detail_mesh = rcAllocPolyMeshDetail(); + ERR_FAIL_COND(!detail_mesh); + ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh)); + + rcFreeCompactHeightfield(chf); + chf = 0; + rcFreeContourSet(cset); + cset = 0; + + ep->step(TTR("Converting to native navigation mesh..."), 10); + + _convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_nav_mesh); + + rcFreePolyMesh(poly_mesh); + poly_mesh = 0; + rcFreePolyMeshDetail(detail_mesh); + detail_mesh = 0; +} + +void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) { + + ERR_FAIL_COND(!p_nav_mesh.is_valid()); + + EditorProgress ep("bake", TTR("Navigation Mesh Generator Setup:"), 11); + ep.step(TTR("Parsing Geometry..."), 0); + + Vector<float> vertices; + Vector<int> indices; + + _parse_geometry(Object::cast_to<Spatial>(p_node)->get_global_transform().affine_inverse(), p_node, vertices, indices); + + if (vertices.size() > 0 && indices.size() > 0) { + + rcHeightfield *hf = NULL; + rcCompactHeightfield *chf = NULL; + rcContourSet *cset = NULL; + rcPolyMesh *poly_mesh = NULL; + rcPolyMeshDetail *detail_mesh = NULL; + + _build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices); + + rcFreeHeightField(hf); + hf = 0; + + rcFreeCompactHeightfield(chf); + chf = 0; + + rcFreeContourSet(cset); + cset = 0; + + rcFreePolyMesh(poly_mesh); + poly_mesh = 0; + + rcFreePolyMeshDetail(detail_mesh); + detail_mesh = 0; + } + ep.step(TTR("Done!"), 11); +} + +void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) { + if (p_nav_mesh.is_valid()) { + p_nav_mesh->clear_polygons(); + p_nav_mesh->set_vertices(PoolVector<Vector3>()); + } +} diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h new file mode 100644 index 0000000000..3588539ef1 --- /dev/null +++ b/modules/recast/navigation_mesh_generator.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* navigation_mesh_generator.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 NAVIGATION_MESH_GENERATOR_H +#define NAVIGATION_MESH_GENERATOR_H + +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "os/thread.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/navigation_mesh.h" +#include "scene/resources/shape.h" + +#include <Recast.h> + +class NavigationMeshGenerator { +protected: + 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 _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, + rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, + 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); +}; + +#endif // NAVIGATION_MESH_GENERATOR_H diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp index 913857c591..f2f18fc86f 100644 --- a/modules/recast/register_types.cpp +++ b/modules/recast/register_types.cpp @@ -30,5 +30,10 @@ #include "register_types.h" -void register_recast_types() {} +#include "navigation_mesh_editor_plugin.h" + +void register_recast_types() { + EditorPlugins::add_by_type<NavigationMeshEditorPlugin>(); +} + void unregister_recast_types() {} diff --git a/modules/regex/config.py b/modules/regex/config.py index cb2da26738..42cfe3b43c 100644 --- a/modules/regex/config.py +++ b/modules/regex/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/squish/config.py b/modules/squish/config.py index 97c95999c8..098f1eafa9 100644 --- a/modules/squish/config.py +++ b/modules/squish/config.py @@ -1,9 +1,5 @@ -def can_build(platform): - return True +def can_build(env, platform): + return env['tools'] def configure(env): - # Tools only, disabled for non-tools - # TODO: Find a cleaner way to achieve that - if not env['tools']: - env['module_squish_enabled'] = False - env.disabled_modules.append("squish") + pass diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py index defe8d0c94..d75e41797a 100644 --- a/modules/stb_vorbis/config.py +++ b/modules/stb_vorbis/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/svg/SCsub b/modules/svg/SCsub index e12abac8c1..a41e0703bd 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -1,7 +1,6 @@ #!/usr/bin/env python Import('env') -from compat import isbasestring # Thirdparty source files thirdparty_dir = "#thirdparty/nanosvg/" @@ -10,23 +9,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -# env.add_source_files(env.modules_sources, thirdparty_sources) - -lib = env.add_library("svg_builtin", thirdparty_sources) - -# Needs to be appended to arrive after libscene in the linker call, -# but we don't want it to arrive *after* system libs, so manual hack -# LIBS contains first SCons Library objects ("SCons.Node.FS.File object") -# and then plain strings for system library. We insert between the two. -inserted = False -for idx, linklib in enumerate(env["LIBS"]): - if isbasestring(linklib): # first system lib such as "X11", otherwise SCons lib object - env["LIBS"].insert(idx, lib) - inserted = True - break -if not inserted: - env.Append(LIBS=[lib]) - +env.add_source_files(env.modules_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) env.Append(CCFLAGS=["-DSVG_ENABLED"]) diff --git a/modules/svg/config.py b/modules/svg/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/svg/config.py +++ b/modules/svg/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/tga/config.py b/modules/tga/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/tga/config.py +++ b/modules/tga/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py index b1ce7d4b91..bd092bdc16 100644 --- a/modules/thekla_unwrap/config.py +++ b/modules/thekla_unwrap/config.py @@ -1,7 +1,5 @@ -def can_build(platform): - return platform != "android" and platform != "ios" +def can_build(env, platform): + return (env['tools'] and platform not in ["android", "ios"]) def configure(env): - if not env['tools']: - env['builtin_thekla_atlas'] = False - env.disabled_modules.append("thekla_unwrap") + pass diff --git a/modules/theora/config.py b/modules/theora/config.py index 34d34f8be2..7504166237 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/tinyexr/config.py b/modules/tinyexr/config.py index e12bb398ce..098f1eafa9 100644 --- a/modules/tinyexr/config.py +++ b/modules/tinyexr/config.py @@ -1,9 +1,5 @@ -def can_build(platform): - return True +def can_build(env, platform): + return env['tools'] def configure(env): - # Tools only, disabled for non-tools - # TODO: Find a cleaner way to achieve that - if not env['tools']: - env['module_tinyexr_enabled'] = False - env.disabled_modules.append("tinyexr") + pass diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub new file mode 100644 index 0000000000..cde231867f --- /dev/null +++ b/modules/upnp/SCsub @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_upnp = env_modules.Clone() + +# Thirdparty source files + +if env['builtin_miniupnpc']: + thirdparty_dir = "#thirdparty/miniupnpc/" + thirdparty_sources = [ + "miniupnpc.c", + "upnpcommands.c", + "miniwget.c", + "upnpdev.c", + "igd_desc_parse.c", + "minissdpc.c", + "minisoap.c", + "minixml.c", + "connecthostport.c", + "receivedata.c", + "portlistingparse.c", + "upnpreplyparse.c", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_upnp.add_source_files(env.modules_sources, thirdparty_sources) + env_upnp.Append(CPPPATH=[thirdparty_dir]) + env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"]) + +env_upnp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/upnp/config.py b/modules/upnp/config.py new file mode 100644 index 0000000000..8724ff1a51 --- /dev/null +++ b/modules/upnp/config.py @@ -0,0 +1,14 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "UPNP", + "UPNPDevice" + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml new file mode 100644 index 0000000000..30be9c836b --- /dev/null +++ b/modules/upnp/doc_classes/UPNP.xml @@ -0,0 +1,227 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="UPNP" inherits="Reference" category="Core" version="3.1"> + <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. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_device"> + <return type="void"> + </return> + <argument index="0" name="device" type="UPNPDevice"> + </argument> + <description> + Adds the given [UPNPDevice] to the list of discovered devices. + </description> + </method> + <method name="add_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="port_internal" type="int" default="0"> + </argument> + <argument index="2" name="desc" type="String" default=""""> + </argument> + <argument index="3" name="proto" type="String" default=""UDP""> + </argument> + <argument index="4" name="duration" type="int" default="0"> + </argument> + <description> + Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. + If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value). + The description ([code]desc[/code]) is shown in some router UIs and can be used to point out which application added the mapping, and the lifetime of the mapping can be limited by [code]duration[/code]. However, some routers are incompatible with one or both of these, so use with caution and add fallback logic in case of errors to retry without them if in doubt. + See [enum UPNPResult] for possible return values. + </description> + </method> + <method name="clear_devices"> + <return type="void"> + </return> + <description> + Clears the list of discovered devices. + </description> + </method> + <method name="delete_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="proto" type="String" default=""UDP""> + </argument> + <description> + Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values. + </description> + </method> + <method name="discover"> + <return type="int"> + </return> + <argument index="0" name="timeout" type="int" default="2000"> + </argument> + <argument index="1" name="ttl" type="int" default="2"> + </argument> + <argument index="2" name="device_filter" type="String" default=""InternetGatewayDevice""> + </argument> + <description> + Discovers local [UPNPDevice]s. Clears the list of previously discovered devices. + Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in miliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing. + See [enum UPNPResult] for possible return values. + </description> + </method> + <method name="get_device" qualifiers="const"> + <return type="UPNPDevice"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + Returns the [UPNPDevice] at the given [code]index[/code]. + </description> + </method> + <method name="get_device_count" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the number of discovered [UPNPDevice]s. + </description> + </method> + <method name="get_gateway" qualifiers="const"> + <return type="UPNPDevice"> + </return> + <description> + Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice). + </description> + </method> + <method name="query_external_address" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error. + </description> + </method> + <method name="remove_device"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + Removes the device at [code]index[/code] from the list of discovered devices. + </description> + </method> + <method name="set_device"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="device" type="UPNPDevice"> + </argument> + <description> + Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code]. + </description> + </method> + </methods> + <members> + <member name="discover_ipv6" type="bool" setter="set_discover_ipv6" getter="is_discover_ipv6"> + 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"> + 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"> + Multicast interface to use for discovery. Uses the default multicast interface if empty. + </member> + </members> + <constants> + <constant name="UPNP_RESULT_SUCCESS" value="0" enum="UPNPResult"> + UPNP command or discovery was successful. + </constant> + <constant name="UPNP_RESULT_NOT_AUTHORIZED" value="1" enum="UPNPResult"> + Not authorized to use the command on the [UPNPDevice]. May be returned when the user disabled UPNP on their router. + </constant> + <constant name="UPNP_RESULT_PORT_MAPPING_NOT_FOUND" value="2" enum="UPNPResult"> + No port mapping was found for the given port, protocol combination on the given [UPNPDevice]. + </constant> + <constant name="UPNP_RESULT_INCONSISTENT_PARAMETERS" value="3" enum="UPNPResult"> + Inconsistent parameters. + </constant> + <constant name="UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY" value="4" enum="UPNPResult"> + No such entry in array. May be returned if a given port, protocol combination is not found on an [UPNPDevice]. + </constant> + <constant name="UPNP_RESULT_ACTION_FAILED" value="5" enum="UPNPResult"> + The action failed. + </constant> + <constant name="UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED" value="6" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the source IP address. + </constant> + <constant name="UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED" value="7" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the external port. + </constant> + <constant name="UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED" value="8" enum="UPNPResult"> + The [UPNPDevice] does not allow wildcard values for the internal port. + </constant> + <constant name="UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD" value="9" enum="UPNPResult"> + The remote host value must be a wildcard. + </constant> + <constant name="UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD" value="10" enum="UPNPResult"> + The external port value must be a wildcard. + </constant> + <constant name="UPNP_RESULT_NO_PORT_MAPS_AVAILABLE" value="11" enum="UPNPResult"> + 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. + </constant> + <constant name="UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING" value="13" enum="UPNPResult"> + Conflict with an existing port mapping. + </constant> + <constant name="UPNP_RESULT_SAME_PORT_VALUES_REQUIRED" value="14" enum="UPNPResult"> + External and internal port values must be the same. + </constant> + <constant name="UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED" value="15" enum="UPNPResult"> + Only permanent leases are supported. Do not use the [code]duration[/code] parameter when adding port mappings. + </constant> + <constant name="UPNP_RESULT_INVALID_GATEWAY" value="16" enum="UPNPResult"> + Invalid gateway. + </constant> + <constant name="UPNP_RESULT_INVALID_PORT" value="17" enum="UPNPResult"> + Invalid port. + </constant> + <constant name="UPNP_RESULT_INVALID_PROTOCOL" value="18" enum="UPNPResult"> + Invalid protocol. + </constant> + <constant name="UPNP_RESULT_INVALID_DURATION" value="19" enum="UPNPResult"> + Invalid duration. + </constant> + <constant name="UPNP_RESULT_INVALID_ARGS" value="20" enum="UPNPResult"> + Invalid arguments. + </constant> + <constant name="UPNP_RESULT_INVALID_RESPONSE" value="21" enum="UPNPResult"> + Invalid response. + </constant> + <constant name="UPNP_RESULT_INVALID_PARAM" value="22" enum="UPNPResult"> + Invalid parameter. + </constant> + <constant name="UPNP_RESULT_HTTP_ERROR" value="23" enum="UPNPResult"> + HTTP error. + </constant> + <constant name="UPNP_RESULT_SOCKET_ERROR" value="24" enum="UPNPResult"> + Socket error. + </constant> + <constant name="UPNP_RESULT_MEM_ALLOC_ERROR" value="25" enum="UPNPResult"> + Error allocating memory. + </constant> + <constant name="UPNP_RESULT_NO_GATEWAY" value="26" enum="UPNPResult"> + No gateway available. You may need to call [method discover] first, or discovery didn't detect any valid IGDs (InternetGatewayDevices). + </constant> + <constant name="UPNP_RESULT_NO_DEVICES" value="27" enum="UPNPResult"> + No devices available. You may need to call [method discover] first, or discovery didn't detect any valid [UPNPDevice]s. + </constant> + <constant name="UPNP_RESULT_UNKNOWN_ERROR" value="28" enum="UPNPResult"> + Unknown error. + </constant> + </constants> +</class> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml new file mode 100644 index 0000000000..9de8042daf --- /dev/null +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="UPNPDevice" inherits="Reference" category="Core" version="3.1"> + <brief_description> + UPNP device. + </brief_description> + <description> + UPNP device. See [UPNP] for UPNP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="port_internal" type="int" default="0"> + </argument> + <argument index="2" name="desc" type="String" default=""""> + </argument> + <argument index="3" name="proto" type="String" default=""UDP""> + </argument> + <argument index="4" name="duration" type="int" default="0"> + </argument> + <description> + Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping]. + </description> + </method> + <method name="delete_port_mapping" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="proto" type="String" default=""UDP""> + </argument> + <description> + Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping]. + </description> + </method> + <method name="is_valid_gateway" qualifiers="const"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding. + </description> + </method> + <method name="query_external_address" qualifiers="const"> + <return type="String"> + </return> + <description> + Returns the external IP address of this [UPNPDevice] or an empty string. + </description> + </method> + </methods> + <members> + <member name="description_url" type="String" setter="set_description_url" getter="get_description_url"> + URL to the device description. + </member> + <member name="igd_control_url" type="String" setter="set_igd_control_url" getter="get_igd_control_url"> + IDG control URL. + </member> + <member name="igd_our_addr" type="String" setter="set_igd_our_addr" getter="get_igd_our_addr"> + 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"> + IGD service type. + </member> + <member name="igd_status" type="int" setter="set_igd_status" getter="get_igd_status" enum="UPNPDevice.IGDStatus"> + IGD status. See [enum IGDStatus]. + </member> + <member name="service_type" type="String" setter="set_service_type" getter="get_service_type"> + Service type. + </member> + </members> + <constants> + <constant name="IGD_STATUS_OK" value="0" enum="IGDStatus"> + OK. + </constant> + <constant name="IGD_STATUS_HTTP_ERROR" value="1" enum="IGDStatus"> + HTTP error. + </constant> + <constant name="IGD_STATUS_HTTP_EMPTY" value="2" enum="IGDStatus"> + Empty HTTP response. + </constant> + <constant name="IGD_STATUS_NO_URLS" value="3" enum="IGDStatus"> + Returned response contained no URLs. + </constant> + <constant name="IGD_STATUS_NO_IGD" value="4" enum="IGDStatus"> + Not a valid IGD. + </constant> + <constant name="IGD_STATUS_DISCONNECTED" value="5" enum="IGDStatus"> + Disconnected. + </constant> + <constant name="IGD_STATUS_UNKNOWN_DEVICE" value="6" enum="IGDStatus"> + Unknown device. + </constant> + <constant name="IGD_STATUS_INVALID_CONTROL" value="7" enum="IGDStatus"> + Invalid control. + </constant> + <constant name="IGD_STATUS_MALLOC_ERROR" value="8" enum="IGDStatus"> + Memory allocation error. + </constant> + <constant name="IGD_STATUS_UNKNOWN_ERROR" value="9" enum="IGDStatus"> + Unknown error. + </constant> + </constants> +</class> diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp new file mode 100644 index 0000000000..c79155c4d2 --- /dev/null +++ b/modules/upnp/register_types.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* 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 "error_macros.h" +#include "upnp.h" +#include "upnpdevice.h" + +void register_upnp_types() { + + ClassDB::register_class<UPNP>(); + ClassDB::register_class<UPNPDevice>(); +} + +void unregister_upnp_types() { +} diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h new file mode 100644 index 0000000000..2aeb06abc7 --- /dev/null +++ b/modules/upnp/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.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. */ +/*************************************************************************/ + +void register_upnp_types(); +void unregister_upnp_types(); diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp new file mode 100644 index 0000000000..32fdfe22f8 --- /dev/null +++ b/modules/upnp/upnp.cpp @@ -0,0 +1,401 @@ +/*************************************************************************/ +/* upnp.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 "upnp.h" +#include "miniupnpc/miniwget.h" +#include "upnpcommands.h" +#include <stdlib.h> + +bool UPNP::is_common_device(const String &dev) const { + return dev.empty() || + dev.find("InternetGatewayDevice") >= 0 || + dev.find("WANIPConnection") >= 0 || + dev.find("WANPPPConnection") >= 0 || + dev.find("rootdevice") >= 0; +} + +int UPNP::discover(int timeout, int ttl, const String &device_filter) { + ERR_FAIL_COND_V(timeout < 0, UPNP_RESULT_INVALID_PARAM); + ERR_FAIL_COND_V(ttl < 0, UPNP_RESULT_INVALID_PARAM); + ERR_FAIL_COND_V(ttl > 255, UPNP_RESULT_INVALID_PARAM); + + devices.clear(); + + int error = 0; + struct UPNPDev *devlist; + + if (is_common_device(device_filter)) { + devlist = upnpDiscover(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error); + } else { + devlist = upnpDiscoverAll(timeout, discover_multicast_if.utf8().get_data(), NULL, discover_local_port, discover_ipv6, ttl, &error); + } + + if (error != UPNPDISCOVER_SUCCESS) { + switch (error) { + case UPNPDISCOVER_SOCKET_ERROR: + return UPNP_RESULT_SOCKET_ERROR; + case UPNPDISCOVER_MEMORY_ERROR: + return UPNP_RESULT_MEM_ALLOC_ERROR; + default: + return UPNP_RESULT_UNKNOWN_ERROR; + } + } + + if (!devlist) { + return UPNP_RESULT_NO_DEVICES; + } + + struct UPNPDev *dev = devlist; + + while (dev) { + if (device_filter.empty() || strstr(dev->st, device_filter.utf8().get_data())) { + add_device_to_list(dev, devlist); + } + + dev = dev->pNext; + } + + freeUPNPDevlist(devlist); + + return UPNP_RESULT_SUCCESS; +} + +void UPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) { + Ref<UPNPDevice> new_device; + new_device.instance(); + + new_device->set_description_url(dev->descURL); + new_device->set_service_type(dev->st); + + parse_igd(new_device, devlist); + + devices.push_back(new_device); +} + +char *UPNP::load_description(const String &url, int *size, int *status_code) const { + return (char *)miniwget(url.utf8().get_data(), size, 0, status_code); +} + +void UPNP::parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist) { + int size = 0; + int status_code = -1; + char *xml = load_description(dev->get_description_url(), &size, &status_code); + + if (status_code != 200) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR); + return; + } + + if (!xml || size < 1) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY); + return; + } + + struct UPNPUrls *urls = (UPNPUrls *)malloc(sizeof(struct UPNPUrls)); + + if (!urls) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_MALLOC_ERROR); + return; + } + + struct IGDdatas data; + + memset(urls, 0, sizeof(struct UPNPUrls)); + + parserootdesc(xml, size, &data); + free(xml); + xml = 0; + + GetUPNPUrls(urls, &data, dev->get_description_url().utf8().get_data(), 0); + + if (!urls) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_URLS); + return; + } + + char addr[16]; + int i = UPNP_GetValidIGD(devlist, urls, &data, (char *)&addr, 16); + + if (i != 1) { + FreeUPNPUrls(urls); + + switch (i) { + case 0: + dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD); + return; + case 2: + dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED); + return; + case 3: + dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE); + return; + default: + dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR); + return; + } + } + + if (urls->controlURL[0] == '\0') { + FreeUPNPUrls(urls); + dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL); + return; + } + + dev->set_igd_control_url(urls->controlURL); + dev->set_igd_service_type(data.first.servicetype); + dev->set_igd_our_addr(addr); + dev->set_igd_status(UPNPDevice::IGD_STATUS_OK); + + FreeUPNPUrls(urls); +} + +int UPNP::upnp_result(int in) { + switch (in) { + case UPNPCOMMAND_SUCCESS: + return UPNP_RESULT_SUCCESS; + case UPNPCOMMAND_UNKNOWN_ERROR: + return UPNP_RESULT_UNKNOWN_ERROR; + case UPNPCOMMAND_INVALID_ARGS: + return UPNP_RESULT_INVALID_ARGS; + case UPNPCOMMAND_HTTP_ERROR: + return UPNP_RESULT_HTTP_ERROR; + case UPNPCOMMAND_INVALID_RESPONSE: + return UPNP_RESULT_INVALID_RESPONSE; + case UPNPCOMMAND_MEM_ALLOC_ERROR: + return UPNP_RESULT_MEM_ALLOC_ERROR; + + case 402: + return UPNP_RESULT_INVALID_ARGS; + case 403: + return UPNP_RESULT_NOT_AUTHORIZED; + case 501: + return UPNP_RESULT_ACTION_FAILED; + case 606: + return UPNP_RESULT_NOT_AUTHORIZED; + case 714: + return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY; + case 715: + return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED; + case 716: + return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED; + case 718: + return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING; + case 724: + return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED; + case 725: + return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED; + case 726: + return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD; + case 727: + return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD; + case 728: + return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE; + case 729: + return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM; + case 732: + return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED; + case 733: + return UPNP_RESULT_INCONSISTENT_PARAMETERS; + } + + return UPNP_RESULT_UNKNOWN_ERROR; +} + +int UPNP::get_device_count() const { + return devices.size(); +} + +Ref<UPNPDevice> UPNP::get_device(int index) const { + ERR_FAIL_INDEX_V(index, devices.size(), NULL); + + return devices.get(index); +} + +void UPNP::add_device(Ref<UPNPDevice> device) { + ERR_FAIL_COND(device == NULL); + + devices.push_back(device); +} + +void UPNP::set_device(int index, Ref<UPNPDevice> device) { + ERR_FAIL_INDEX(index, devices.size()); + ERR_FAIL_COND(device == NULL); + + devices.set(index, device); +} + +void UPNP::remove_device(int index) { + ERR_FAIL_INDEX(index, devices.size()); + + devices.remove(index); +} + +void UPNP::clear_devices() { + devices.clear(); +} + +Ref<UPNPDevice> UPNP::get_gateway() const { + ERR_FAIL_COND_V(devices.size() < 1, NULL); + + for (int i = 0; i < devices.size(); i++) { + Ref<UPNPDevice> dev = get_device(i); + + if (dev != NULL && dev->is_valid_gateway()) { + return dev; + } + } + + return NULL; +} + +void UPNP::set_discover_multicast_if(const String &m_if) { + discover_multicast_if = m_if; +} + +String UPNP::get_discover_multicast_if() const { + return discover_multicast_if; +} + +void UPNP::set_discover_local_port(int port) { + discover_local_port = port; +} + +int UPNP::get_discover_local_port() const { + return discover_local_port; +} + +void UPNP::set_discover_ipv6(bool ipv6) { + discover_ipv6 = ipv6; +} + +bool UPNP::is_discover_ipv6() const { + return discover_ipv6; +} + +String UPNP::query_external_address() const { + Ref<UPNPDevice> dev = get_gateway(); + + if (dev == NULL) { + return ""; + } + + return dev->query_external_address(); +} + +int UPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { + Ref<UPNPDevice> dev = get_gateway(); + + if (dev == NULL) { + return UPNP_RESULT_NO_GATEWAY; + } + + dev->delete_port_mapping(port, proto); + + return dev->add_port_mapping(port, port_internal, desc, proto, duration); +} + +int UPNP::delete_port_mapping(int port, String proto) const { + Ref<UPNPDevice> dev = get_gateway(); + + if (dev == NULL) { + return UPNP_RESULT_NO_GATEWAY; + } + + return dev->delete_port_mapping(port, proto); +} + +void UPNP::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_device_count"), &UPNP::get_device_count); + ClassDB::bind_method(D_METHOD("get_device", "index"), &UPNP::get_device); + ClassDB::bind_method(D_METHOD("add_device", "device"), &UPNP::add_device); + ClassDB::bind_method(D_METHOD("set_device", "index", "device"), &UPNP::set_device); + ClassDB::bind_method(D_METHOD("remove_device", "index"), &UPNP::remove_device); + ClassDB::bind_method(D_METHOD("clear_devices"), &UPNP::clear_devices); + + ClassDB::bind_method(D_METHOD("get_gateway"), &UPNP::get_gateway); + + ClassDB::bind_method(D_METHOD("discover", "timeout", "ttl", "device_filter"), &UPNP::discover, DEFVAL(2000), DEFVAL(2), DEFVAL("InternetGatewayDevice")); + + ClassDB::bind_method(D_METHOD("query_external_address"), &UPNP::query_external_address); + + ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNP::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNP::delete_port_mapping, DEFVAL("UDP")); + + ClassDB::bind_method(D_METHOD("set_discover_multicast_if", "m_if"), &UPNP::set_discover_multicast_if); + ClassDB::bind_method(D_METHOD("get_discover_multicast_if"), &UPNP::get_discover_multicast_if); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "discover_multicast_if"), "set_discover_multicast_if", "get_discover_multicast_if"); + + ClassDB::bind_method(D_METHOD("set_discover_local_port", "port"), &UPNP::set_discover_local_port); + ClassDB::bind_method(D_METHOD("get_discover_local_port"), &UPNP::get_discover_local_port); + ADD_PROPERTY(PropertyInfo(Variant::INT, "discover_local_port", PROPERTY_HINT_RANGE, "0,65535"), "set_discover_local_port", "get_discover_local_port"); + + ClassDB::bind_method(D_METHOD("set_discover_ipv6", "ipv6"), &UPNP::set_discover_ipv6); + ClassDB::bind_method(D_METHOD("is_discover_ipv6"), &UPNP::is_discover_ipv6); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "discover_ipv6"), "set_discover_ipv6", "is_discover_ipv6"); + + BIND_ENUM_CONSTANT(UPNP_RESULT_SUCCESS); + BIND_ENUM_CONSTANT(UPNP_RESULT_NOT_AUTHORIZED); + BIND_ENUM_CONSTANT(UPNP_RESULT_PORT_MAPPING_NOT_FOUND); + BIND_ENUM_CONSTANT(UPNP_RESULT_INCONSISTENT_PARAMETERS); + BIND_ENUM_CONSTANT(UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY); + BIND_ENUM_CONSTANT(UPNP_RESULT_ACTION_FAILED); + BIND_ENUM_CONSTANT(UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED); + BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED); + BIND_ENUM_CONSTANT(UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED); + BIND_ENUM_CONSTANT(UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD); + BIND_ENUM_CONSTANT(UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD); + BIND_ENUM_CONSTANT(UPNP_RESULT_NO_PORT_MAPS_AVAILABLE); + BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM); + BIND_ENUM_CONSTANT(UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING); + BIND_ENUM_CONSTANT(UPNP_RESULT_SAME_PORT_VALUES_REQUIRED); + BIND_ENUM_CONSTANT(UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_GATEWAY); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PORT); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PROTOCOL); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_DURATION); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_ARGS); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_RESPONSE); + BIND_ENUM_CONSTANT(UPNP_RESULT_INVALID_PARAM); + BIND_ENUM_CONSTANT(UPNP_RESULT_HTTP_ERROR); + BIND_ENUM_CONSTANT(UPNP_RESULT_SOCKET_ERROR); + BIND_ENUM_CONSTANT(UPNP_RESULT_MEM_ALLOC_ERROR); + BIND_ENUM_CONSTANT(UPNP_RESULT_NO_GATEWAY); + BIND_ENUM_CONSTANT(UPNP_RESULT_NO_DEVICES); + BIND_ENUM_CONSTANT(UPNP_RESULT_UNKNOWN_ERROR); +} + +UPNP::UPNP() { + discover_multicast_if = ""; + discover_local_port = 0; + discover_ipv6 = false; +} + +UPNP::~UPNP() { +} diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h new file mode 100644 index 0000000000..fb0c0f30a0 --- /dev/null +++ b/modules/upnp/upnp.h @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* upnp.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 GODOT_UPNP_H +#define GODOT_UPNP_H + +#include "miniupnpc/miniupnpc.h" +#include "upnpdevice.h" +#include <reference.h> + +class UPNP : public Reference { + + GDCLASS(UPNP, Reference); + +private: + String discover_multicast_if; + int discover_local_port; + bool discover_ipv6; + + Vector<Ref<UPNPDevice> > devices; + + bool is_common_device(const String &dev) const; + void add_device_to_list(UPNPDev *dev, UPNPDev *devlist); + void parse_igd(Ref<UPNPDevice> dev, UPNPDev *devlist); + char *load_description(const String &url, int *size, int *status_code) const; + +protected: + static void _bind_methods(); + +public: + enum UPNPResult { + + UPNP_RESULT_SUCCESS, + UPNP_RESULT_NOT_AUTHORIZED, + UPNP_RESULT_PORT_MAPPING_NOT_FOUND, + UPNP_RESULT_INCONSISTENT_PARAMETERS, + UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY, + UPNP_RESULT_ACTION_FAILED, + UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED, + UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED, + UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED, + UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD, + UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD, + UPNP_RESULT_NO_PORT_MAPS_AVAILABLE, + UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM, + UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING, + UPNP_RESULT_SAME_PORT_VALUES_REQUIRED, + UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED, + UPNP_RESULT_INVALID_GATEWAY, + UPNP_RESULT_INVALID_PORT, + UPNP_RESULT_INVALID_PROTOCOL, + UPNP_RESULT_INVALID_DURATION, + UPNP_RESULT_INVALID_ARGS, + UPNP_RESULT_INVALID_RESPONSE, + UPNP_RESULT_INVALID_PARAM, + UPNP_RESULT_HTTP_ERROR, + UPNP_RESULT_SOCKET_ERROR, + UPNP_RESULT_MEM_ALLOC_ERROR, + UPNP_RESULT_NO_GATEWAY, + UPNP_RESULT_NO_DEVICES, + UPNP_RESULT_UNKNOWN_ERROR, + }; + + static int upnp_result(int in); + + int get_device_count() const; + Ref<UPNPDevice> get_device(int index) const; + void add_device(Ref<UPNPDevice> device); + void set_device(int index, Ref<UPNPDevice> device); + void remove_device(int index); + void clear_devices(); + + Ref<UPNPDevice> get_gateway() const; + + int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice"); + + String query_external_address() const; + + int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const; + int delete_port_mapping(int port, String proto = "UDP") const; + + void set_discover_multicast_if(const String &m_if); + String get_discover_multicast_if() const; + + void set_discover_local_port(int port); + int get_discover_local_port() const; + + void set_discover_ipv6(bool ipv6); + bool is_discover_ipv6() const; + + UPNP(); + ~UPNP(); +}; + +VARIANT_ENUM_CAST(UPNP::UPNPResult) + +#endif // GODOT_UPNP_H diff --git a/modules/upnp/upnpdevice.cpp b/modules/upnp/upnpdevice.cpp new file mode 100644 index 0000000000..a5959cf649 --- /dev/null +++ b/modules/upnp/upnpdevice.cpp @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* upnpdevice.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 "upnpdevice.h" +#include "upnp.h" +#include "upnpcommands.h" + +String UPNPDevice::query_external_address() const { + ERR_FAIL_COND_V(!is_valid_gateway(), ""); + + char addr[16]; + int i = UPNP_GetExternalIPAddress( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + (char *)&addr); + + ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, ""); + + return String(addr); +} + +int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { + ERR_FAIL_COND_V(!is_valid_gateway(), UPNP::UPNP_RESULT_INVALID_GATEWAY); + ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT); + ERR_FAIL_COND_V(port_internal < 0 || port_internal > 65535, UPNP::UPNP_RESULT_INVALID_PORT); // Needs to allow 0 because 0 signifies "use external port as internal port" + ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL); + ERR_FAIL_COND_V(duration < 0, UPNP::UPNP_RESULT_INVALID_DURATION); + + if (port_internal < 1) { + port_internal = port; + } + + int i = UPNP_AddPortMapping( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + itos(port).utf8().get_data(), + itos(port_internal).utf8().get_data(), + igd_our_addr.utf8().get_data(), + desc.empty() ? 0 : desc.utf8().get_data(), + proto.utf8().get_data(), + NULL, // Remote host, always NULL as IGDs don't support it + duration > 0 ? itos(duration).utf8().get_data() : 0); + + ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i)); + + return UPNP::UPNP_RESULT_SUCCESS; +} + +int UPNPDevice::delete_port_mapping(int port, String proto) const { + ERR_FAIL_COND_V(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT); + ERR_FAIL_COND_V(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL); + + int i = UPNP_DeletePortMapping( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + itos(port).utf8().get_data(), + proto.utf8().get_data(), + NULL // Remote host, always NULL as IGDs don't support it + ); + + ERR_FAIL_COND_V(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i)); + + return UPNP::UPNP_RESULT_SUCCESS; +} + +void UPNPDevice::set_description_url(const String &url) { + description_url = url; +} + +String UPNPDevice::get_description_url() const { + return description_url; +} + +void UPNPDevice::set_service_type(const String &type) { + service_type = type; +} + +String UPNPDevice::get_service_type() const { + return service_type; +} + +void UPNPDevice::set_igd_control_url(const String &url) { + igd_control_url = url; +} + +String UPNPDevice::get_igd_control_url() const { + return igd_control_url; +} + +void UPNPDevice::set_igd_service_type(const String &type) { + igd_service_type = type; +} + +String UPNPDevice::get_igd_service_type() const { + return igd_service_type; +} + +void UPNPDevice::set_igd_our_addr(const String &addr) { + igd_our_addr = addr; +} + +String UPNPDevice::get_igd_our_addr() const { + return igd_our_addr; +} + +void UPNPDevice::set_igd_status(IGDStatus status) { + igd_status = status; +} + +UPNPDevice::IGDStatus UPNPDevice::get_igd_status() const { + return igd_status; +} + +bool UPNPDevice::is_valid_gateway() const { + return igd_status == IGD_STATUS_OK; +} + +void UPNPDevice::_bind_methods() { + ClassDB::bind_method(D_METHOD("is_valid_gateway"), &UPNPDevice::is_valid_gateway); + ClassDB::bind_method(D_METHOD("query_external_address"), &UPNPDevice::query_external_address); + ClassDB::bind_method(D_METHOD("add_port_mapping", "port", "port_internal", "desc", "proto", "duration"), &UPNPDevice::add_port_mapping, DEFVAL(0), DEFVAL(""), DEFVAL("UDP"), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("delete_port_mapping", "port", "proto"), &UPNPDevice::delete_port_mapping, DEFVAL("UDP")); + + ClassDB::bind_method(D_METHOD("set_description_url", "url"), &UPNPDevice::set_description_url); + ClassDB::bind_method(D_METHOD("get_description_url"), &UPNPDevice::get_description_url); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "description_url"), "set_description_url", "get_description_url"); + + ClassDB::bind_method(D_METHOD("set_service_type", "type"), &UPNPDevice::set_service_type); + ClassDB::bind_method(D_METHOD("get_service_type"), &UPNPDevice::get_service_type); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "service_type"), "set_service_type", "get_service_type"); + + ClassDB::bind_method(D_METHOD("set_igd_control_url", "url"), &UPNPDevice::set_igd_control_url); + ClassDB::bind_method(D_METHOD("get_igd_control_url"), &UPNPDevice::get_igd_control_url); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_control_url"), "set_igd_control_url", "get_igd_control_url"); + + ClassDB::bind_method(D_METHOD("set_igd_service_type", "type"), &UPNPDevice::set_igd_service_type); + ClassDB::bind_method(D_METHOD("get_igd_service_type"), &UPNPDevice::get_igd_service_type); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_service_type"), "set_igd_service_type", "get_igd_service_type"); + + ClassDB::bind_method(D_METHOD("set_igd_our_addr", "addr"), &UPNPDevice::set_igd_our_addr); + ClassDB::bind_method(D_METHOD("get_igd_our_addr"), &UPNPDevice::get_igd_our_addr); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "igd_our_addr"), "set_igd_our_addr", "get_igd_our_addr"); + + ClassDB::bind_method(D_METHOD("set_igd_status", "status"), &UPNPDevice::set_igd_status); + ClassDB::bind_method(D_METHOD("get_igd_status"), &UPNPDevice::get_igd_status); + ADD_PROPERTY(PropertyInfo(Variant::INT, "igd_status", PROPERTY_HINT_ENUM), "set_igd_status", "get_igd_status"); + + BIND_ENUM_CONSTANT(IGD_STATUS_OK); + BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_ERROR); + BIND_ENUM_CONSTANT(IGD_STATUS_HTTP_EMPTY); + BIND_ENUM_CONSTANT(IGD_STATUS_NO_URLS); + BIND_ENUM_CONSTANT(IGD_STATUS_NO_IGD); + BIND_ENUM_CONSTANT(IGD_STATUS_DISCONNECTED); + BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_DEVICE); + BIND_ENUM_CONSTANT(IGD_STATUS_INVALID_CONTROL); + BIND_ENUM_CONSTANT(IGD_STATUS_MALLOC_ERROR); + BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_ERROR); +} + +UPNPDevice::UPNPDevice() { + description_url = ""; + service_type = ""; + igd_control_url = ""; + igd_service_type = ""; + igd_our_addr = ""; + igd_status = IGD_STATUS_UNKNOWN_ERROR; +} + +UPNPDevice::~UPNPDevice() { +} diff --git a/modules/upnp/upnpdevice.h b/modules/upnp/upnpdevice.h new file mode 100644 index 0000000000..25c9fe1ba3 --- /dev/null +++ b/modules/upnp/upnpdevice.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* upnpdevice.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 GODOT_UPNPDEVICE_H +#define GODOT_UPNPDEVICE_H + +#include <reference.h> + +class UPNPDevice : public Reference { + + GDCLASS(UPNPDevice, Reference); + +public: + enum IGDStatus { + + IGD_STATUS_OK, + IGD_STATUS_HTTP_ERROR, + IGD_STATUS_HTTP_EMPTY, + IGD_STATUS_NO_URLS, + IGD_STATUS_NO_IGD, + IGD_STATUS_DISCONNECTED, + IGD_STATUS_UNKNOWN_DEVICE, + IGD_STATUS_INVALID_CONTROL, + IGD_STATUS_MALLOC_ERROR, + IGD_STATUS_UNKNOWN_ERROR, + }; + + void set_description_url(const String &url); + String get_description_url() const; + + void set_service_type(const String &type); + String get_service_type() const; + + void set_igd_control_url(const String &url); + String get_igd_control_url() const; + + void set_igd_service_type(const String &type); + String get_igd_service_type() const; + + void set_igd_our_addr(const String &addr); + String get_igd_our_addr() const; + + void set_igd_status(IGDStatus status); + IGDStatus get_igd_status() const; + + bool is_valid_gateway() const; + String query_external_address() const; + int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const; + int delete_port_mapping(int port, String proto = "UDP") const; + + UPNPDevice(); + ~UPNPDevice(); + +protected: + static void _bind_methods(); + +private: + String description_url; + String service_type; + String igd_control_url; + String igd_service_type; + String igd_our_addr; + IGDStatus igd_status; +}; + +VARIANT_ENUM_CAST(UPNPDevice::IGDStatus) + +#endif // GODOT_UPNPDEVICE_H diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index 6b1ce41014..07a0450734 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index dab186fb57..28764aca40 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html + <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html</link> </tutorials> <demos> </demos> diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 318fb7fb9c..9331092171 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -1971,11 +1971,11 @@ Ref<Script> VisualScriptInstance::get_script() const { return script; } -ScriptInstance::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const { const Map<StringName, VisualScript::Function>::Element *E = script->functions.find(p_method); if (!E) { - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) { @@ -1987,12 +1987,12 @@ ScriptInstance::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_m } } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -ScriptInstance::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const { - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index a15360ad39..aaa6dfea11 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -433,8 +433,8 @@ public: virtual ScriptLanguage *get_language(); - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; VisualScriptInstance(); ~VisualScriptInstance(); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 72b5e09222..0bd64d6a1d 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -137,7 +137,7 @@ class VisualScriptEditor : public ScriptEditorBase { Vector<Pair<Variant::Type, String> > args; }; - HashMap<StringName, Ref<StyleBox>, StringNameHasher> node_styles; + HashMap<StringName, Ref<StyleBox> > node_styles; StringName edited_func; void _update_graph_connections(); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 4803f29519..8b7b809ec0 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -93,7 +93,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value } if (p_name == "rpc/mode") { - rpc_mode = ScriptInstance::RPCMode(int(p_value)); + rpc_mode = MultiplayerAPI::RPCMode(int(p_value)); return true; } @@ -267,11 +267,11 @@ int VisualScriptFunction::get_argument_count() const { return arguments.size(); } -void VisualScriptFunction::set_rpc_mode(ScriptInstance::RPCMode p_mode) { +void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) { rpc_mode = p_mode; } -ScriptInstance::RPCMode VisualScriptFunction::get_rpc_mode() const { +MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const { return rpc_mode; } @@ -319,7 +319,7 @@ VisualScriptFunction::VisualScriptFunction() { stack_size = 256; stack_less = false; sequenced = true; - rpc_mode = ScriptInstance::RPC_MODE_DISABLED; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; } void VisualScriptFunction::set_stack_less(bool p_enable) { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index a0bc35dd92..9bfbd46e47 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -46,7 +46,7 @@ class VisualScriptFunction : public VisualScriptNode { bool stack_less; int stack_size; - ScriptInstance::RPCMode rpc_mode; + MultiplayerAPI::RPCMode rpc_mode; bool sequenced; protected: @@ -93,8 +93,8 @@ public: void set_return_type(Variant::Type p_type); Variant::Type get_return_type() const; - void set_rpc_mode(ScriptInstance::RPCMode p_mode); - ScriptInstance::RPCMode get_rpc_mode() const; + void set_rpc_mode(MultiplayerAPI::RPCMode p_mode); + MultiplayerAPI::RPCMode get_rpc_mode() const; virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance); diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/vorbis/config.py +++ b/modules/vorbis/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/webm/config.py b/modules/webm/config.py index dcae4447d5..72a4073423 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -1,5 +1,5 @@ -def can_build(platform): - return platform != 'iphone' +def can_build(env, platform): + return platform not in ['iphone'] def configure(env): pass diff --git a/modules/webp/config.py b/modules/webp/config.py index 5f133eba90..1c8cd12a2d 100644 --- a/modules/webp/config.py +++ b/modules/webp/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index b36f1beacd..15a88773e7 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -7,47 +7,57 @@ Import('env_modules') env_lws = env_modules.Clone() -thirdparty_dir = "#thirdparty/lws/" +thirdparty_dir = "#thirdparty/libwebsockets/" helper_dir = "win32helpers/" thirdparty_sources = [ - "client/client.c", - "client/client-handshake.c", - "client/client-parser.c", - "client/ssl-client.c", - - "ext/extension.c", - "ext/extension-permessage-deflate.c", - - "server/fops-zip.c", - "server/lejp-conf.c", - "server/parsers.c", - "server/ranges.c", - "server/server.c", - "server/server-handshake.c", - "server/ssl-server.c", + + "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", - "alloc.c", - "context.c", - "handshake.c", - "header.c", - "libwebsockets.c", - "output.c", - "pollfd.c", - "service.c", - "ssl.c", - - "mbedtls_wrapper/library/ssl_cert.c", - "mbedtls_wrapper/library/ssl_pkey.c", - "mbedtls_wrapper/library/ssl_stack.c", - "mbedtls_wrapper/library/ssl_methods.c", - "mbedtls_wrapper/library/ssl_lib.c", - "mbedtls_wrapper/library/ssl_x509.c", - "mbedtls_wrapper/platform/ssl_port.c", - "mbedtls_wrapper/platform/ssl_pm.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" ] if env_lws["platform"] == "android": # Builtin getifaddrs @@ -67,7 +77,7 @@ else: env_lws.add_source_files(env.modules_sources, thirdparty_sources) env_lws.Append(CPPPATH=[thirdparty_dir]) - wrapper_includes = ["#thirdparty/lws/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] + wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] env_lws.Prepend(CPPPATH=wrapper_includes) if env['builtin_mbedtls']: diff --git a/modules/websocket/config.py b/modules/websocket/config.py index 399ca88fc1..f59ef432b4 100644 --- a/modules/websocket/config.py +++ b/modules/websocket/config.py @@ -1,8 +1,6 @@ - -def can_build(platform): +def can_build(env, platform): return True - def configure(env): pass diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index 2220c9adf2..06f97aaf05 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -32,6 +32,7 @@ #include "lws_client.h" #include "core/io/ip.h" #include "core/io/stream_peer_ssl.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) { @@ -140,7 +141,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi destroy_context(); return -1; // we should close the connection (would probably happen anyway) - case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CLOSED: peer_data->in_count = 0; peer_data->out_count = 0; peer_data->rbw.resize(0); diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index a850a545d3..a4920c3d54 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.h @@ -30,6 +30,9 @@ #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" @@ -124,6 +127,7 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, /* 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(); @@ -145,13 +149,15 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, structs_ptr[0].name = "http-only"; structs_ptr[0].callback = p_callback; structs_ptr[0].per_session_data_size = data_size; - structs_ptr[0].rx_buffer_size = 0; + 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 = 0; + 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'; } diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 3855a39aef..96acb99cc4 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -191,8 +191,8 @@ IP_Address LWSPeer::get_connected_host() const { IP_Address ip; int port = 0; - socklen_t len = 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()); @@ -212,8 +212,8 @@ uint16_t LWSPeer::get_connected_port() const { IP_Address ip; int port = 0; - socklen_t len = 0; struct sockaddr_storage addr; + socklen_t len = sizeof(addr); int fd = lws_get_socket_fd(wsi); ERR_FAIL_COND_V(fd == -1, 0); |