summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/bmp/SCsub9
-rw-r--r--modules/bmp/config.py (renamed from modules/openssl/config.py)2
-rw-r--r--modules/bmp/image_loader_bmp.cpp194
-rw-r--r--modules/bmp/image_loader_bmp.h84
-rw-r--r--modules/bmp/register_types.cpp44
-rw-r--r--modules/bmp/register_types.h (renamed from modules/openssl/register_types.h)4
-rw-r--r--modules/bullet/area_bullet.cpp24
-rw-r--r--modules/bullet/area_bullet.h4
-rw-r--r--modules/bullet/btRayShape.cpp5
-rw-r--r--modules/bullet/btRayShape.h4
-rw-r--r--modules/bullet/bullet_physics_server.cpp63
-rw-r--r--modules/bullet/bullet_physics_server.h11
-rw-r--r--modules/bullet/collision_object_bullet.cpp10
-rw-r--r--modules/bullet/config.py2
-rw-r--r--modules/bullet/constraint_bullet.cpp12
-rw-r--r--modules/bullet/constraint_bullet.h4
-rw-r--r--modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml2
-rw-r--r--modules/bullet/doc_classes/BulletPhysicsServer.xml2
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp13
-rw-r--r--modules/bullet/godot_motion_state.h2
-rw-r--r--modules/bullet/godot_ray_world_algorithm.cpp8
-rw-r--r--modules/bullet/godot_ray_world_algorithm.h2
-rw-r--r--modules/bullet/godot_result_callbacks.cpp52
-rw-r--r--modules/bullet/godot_result_callbacks.h20
-rw-r--r--modules/bullet/rigid_body_bullet.cpp19
-rw-r--r--modules/bullet/rigid_body_bullet.h10
-rw-r--r--modules/bullet/shape_bullet.cpp82
-rw-r--r--modules/bullet/shape_bullet.h28
-rw-r--r--modules/bullet/space_bullet.cpp149
-rw-r--r--modules/bullet/space_bullet.h8
-rw-r--r--modules/csg/SCsub9
-rw-r--r--modules/csg/config.py21
-rw-r--r--modules/csg/csg.cpp1518
-rw-r--r--modules/csg/csg.h236
-rw-r--r--modules/csg/csg_gizmos.cpp345
-rw-r--r--modules/csg/csg_gizmos.h60
-rw-r--r--modules/csg/csg_shape.cpp2164
-rw-r--r--modules/csg/csg_shape.h390
-rw-r--r--modules/csg/doc_classes/CSGBox.xml25
-rw-r--r--modules/csg/doc_classes/CSGCombiner.xml15
-rw-r--r--modules/csg/doc_classes/CSGCylinder.xml29
-rw-r--r--modules/csg/doc_classes/CSGMesh.xml19
-rw-r--r--modules/csg/doc_classes/CSGPolygon.xml49
-rw-r--r--modules/csg/doc_classes/CSGPrimitive.xml19
-rw-r--r--modules/csg/doc_classes/CSGShape.xml35
-rw-r--r--modules/csg/doc_classes/CSGSphere.xml27
-rw-r--r--modules/csg/doc_classes/CSGTorus.xml29
-rw-r--r--modules/csg/register_types.cpp57
-rw-r--r--modules/csg/register_types.h32
-rw-r--r--modules/dds/config.py2
-rw-r--r--modules/enet/config.py2
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml76
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp321
-rw-r--r--modules/enet/networked_multiplayer_enet.h22
-rw-r--r--modules/etc/config.py10
-rw-r--r--modules/freetype/SCsub21
-rw-r--r--modules/freetype/config.py2
-rw-r--r--modules/gdnative/SCsub98
-rw-r--r--modules/gdnative/android/android_gdn.cpp73
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.cpp4
-rw-r--r--modules/gdnative/config.py3
-rw-r--r--modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml2
-rw-r--r--modules/gdnative/doc_classes/GDNative.xml2
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml10
-rw-r--r--modules/gdnative/doc_classes/NativeScript.xml38
-rw-r--r--modules/gdnative/doc_classes/PluginScript.xml2
-rw-r--r--modules/gdnative/gdnative.cpp235
-rw-r--r--modules/gdnative/gdnative.h6
-rw-r--r--modules/gdnative/gdnative_api.json158
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.h2
-rw-r--r--modules/gdnative/include/android/godot_android.h54
-rw-r--r--modules/gdnative/include/arvr/godot_arvr.h8
-rw-r--r--modules/gdnative/include/gdnative/variant.h2
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h52
-rw-r--r--modules/gdnative/include/net/godot_net.h118
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h2
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp170
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp318
-rw-r--r--modules/gdnative/nativescript/nativescript.h42
-rw-r--r--modules/gdnative/nativescript/register_types.cpp1
-rw-r--r--modules/gdnative/net/SCsub12
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp124
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h77
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.cpp73
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.h59
-rw-r--r--modules/gdnative/net/register_types.cpp43
-rw-r--r--modules/gdnative/net/register_types.h32
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.cpp77
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.h61
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp47
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h10
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp4
-rw-r--r--modules/gdnative/register_types.cpp3
-rw-r--r--modules/gdscript/SCsub3
-rw-r--r--modules/gdscript/config.py2
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml4
-rw-r--r--modules/gdscript/doc_classes/GDScriptFunctionState.xml2
-rw-r--r--modules/gdscript/doc_classes/GDScriptNativeClass.xml2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp337
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h70
-rw-r--r--modules/gdscript/gdscript.cpp22
-rw-r--r--modules/gdscript/gdscript.h12
-rw-r--r--modules/gdscript/gdscript_compiler.cpp29
-rw-r--r--modules/gdscript/gdscript_compiler.h3
-rw-r--r--modules/gdscript/gdscript_editor.cpp113
-rw-r--r--modules/gdscript/gdscript_function.cpp41
-rw-r--r--modules/gdscript/gdscript_function.h23
-rw-r--r--modules/gdscript/gdscript_functions.cpp74
-rw-r--r--modules/gdscript/gdscript_functions.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp222
-rw-r--r--modules/gdscript/gdscript_parser.h8
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp9
-rw-r--r--modules/gdscript/gdscript_tokenizer.h3
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gridmap/config.py2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml4
-rw-r--r--modules/gridmap/grid_map.h2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp90
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h4
-rw-r--r--modules/hdr/config.py2
-rw-r--r--modules/hdr/image_loader_hdr.cpp16
-rw-r--r--modules/jpg/config.py2
-rwxr-xr-xmodules/mbedtls/SCsub91
-rwxr-xr-xmodules/mbedtls/config.py5
-rwxr-xr-x[-rw-r--r--]modules/mbedtls/register_types.cpp (renamed from modules/openssl/register_types.cpp)12
-rwxr-xr-xmodules/mbedtls/register_types.h32
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp307
-rwxr-xr-x[-rw-r--r--]modules/mbedtls/stream_peer_mbed_tls.h (renamed from modules/openssl/stream_peer_openssl.h)65
-rw-r--r--modules/mobile_vr/config.py4
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml2
-rw-r--r--modules/mono/SCsub79
-rw-r--r--modules/mono/config.py106
-rw-r--r--modules/mono/csharp_script.cpp268
-rw-r--r--modules/mono/csharp_script.h26
-rw-r--r--modules/mono/doc_classes/@C#.xml2
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml2
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml2
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs119
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj4
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/bindings_generator.cpp259
-rw-r--r--modules/mono/editor/bindings_generator.h54
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp204
-rw-r--r--modules/mono/editor/godotsharp_builds.h21
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp17
-rw-r--r--modules/mono/editor/godotsharp_editor.h1
-rw-r--r--modules/mono/editor/godotsharp_export.cpp166
-rw-r--r--modules/mono/editor/godotsharp_export.h57
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp104
-rw-r--r--modules/mono/editor/mono_bottom_panel.h6
-rw-r--r--modules/mono/editor/mono_build_info.cpp62
-rw-r--r--modules/mono/editor/mono_build_info.h24
-rw-r--r--modules/mono/editor/monodevelop_instance.h2
-rw-r--r--modules/mono/glue/builtin_types_glue.h30
-rw-r--r--modules/mono/glue/cs_files/AABB.cs147
-rw-r--r--modules/mono/glue/cs_files/Basis.cs294
-rw-r--r--modules/mono/glue/cs_files/Color.cs123
-rw-r--r--modules/mono/glue/cs_files/DebuggingUtils.cs4
-rw-r--r--modules/mono/glue/cs_files/GD.cs14
-rw-r--r--modules/mono/glue/cs_files/GodotMethodAttribute.cs2
-rw-r--r--modules/mono/glue/cs_files/GodotSynchronizationContext.cs1
-rw-r--r--modules/mono/glue/cs_files/GodotTaskScheduler.cs4
-rw-r--r--modules/mono/glue/cs_files/IAwaiter.cs1
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs6
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs217
-rw-r--r--modules/mono/glue/cs_files/MathfEx.cs39
-rw-r--r--modules/mono/glue/cs_files/Plane.cs107
-rw-r--r--modules/mono/glue/cs_files/Quat.cs111
-rw-r--r--modules/mono/glue/cs_files/RPCAttributes.cs9
-rw-r--r--modules/mono/glue/cs_files/Rect2.cs79
-rw-r--r--modules/mono/glue/cs_files/SignalAttribute.cs9
-rw-r--r--modules/mono/glue/cs_files/SignalAwaiter.cs12
-rw-r--r--modules/mono/glue/cs_files/StringExtensions.cs110
-rw-r--r--modules/mono/glue/cs_files/Transform.cs34
-rw-r--r--modules/mono/glue/cs_files/Transform2D.cs93
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt1
-rw-r--r--modules/mono/glue/cs_files/Vector2.cs167
-rw-r--r--modules/mono/glue/cs_files/Vector3.cs165
-rw-r--r--modules/mono/godotsharp_defs.h3
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp357
-rw-r--r--modules/mono/mono_gd/gd_mono.h63
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp136
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h19
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp28
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp44
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp17
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp75
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h4
-rw-r--r--modules/mono/mono_reg_utils.py6
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp2
-rw-r--r--modules/ogg/config.py2
-rw-r--r--modules/openssl/SCsub696
-rw-r--r--modules/openssl/stream_peer_openssl.cpp632
-rw-r--r--modules/opus/config.py2
-rw-r--r--modules/pvr/config.py2
-rw-r--r--modules/pvr/texture_loader_pvr.cpp4
-rw-r--r--modules/recast/SCsub12
-rw-r--r--modules/recast/config.py4
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp163
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.h84
-rw-r--r--modules/recast/navigation_mesh_generator.cpp304
-rw-r--r--modules/recast/navigation_mesh_generator.h59
-rw-r--r--modules/recast/register_types.cpp7
-rw-r--r--modules/regex/SCsub3
-rw-r--r--modules/regex/config.py2
-rw-r--r--modules/regex/doc_classes/RegEx.xml6
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml2
-rw-r--r--modules/squish/config.py10
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp6
-rw-r--r--modules/stb_vorbis/config.py2
-rw-r--r--modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml5
-rw-r--r--modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml2
-rw-r--r--modules/stb_vorbis/resource_importer_ogg_vorbis.cpp1
-rw-r--r--modules/svg/SCsub19
-rw-r--r--modules/svg/config.py2
-rw-r--r--modules/tga/config.py2
-rw-r--r--modules/thekla_unwrap/config.py8
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/theora/doc_classes/ResourceImporterTheora.xml2
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml2
-rw-r--r--modules/theora/video_stream_theora.cpp20
-rw-r--r--modules/tinyexr/config.py10
-rw-r--r--modules/upnp/SCsub32
-rw-r--r--modules/upnp/config.py14
-rw-r--r--modules/upnp/doc_classes/UPNP.xml227
-rw-r--r--modules/upnp/doc_classes/UPNPDevice.xml109
-rw-r--r--modules/upnp/register_types.cpp43
-rw-r--r--modules/upnp/register_types.h32
-rw-r--r--modules/upnp/upnp.cpp401
-rw-r--r--modules/upnp/upnp.h124
-rw-r--r--modules/upnp/upnpdevice.cpp197
-rw-r--r--modules/upnp/upnpdevice.h95
-rw-r--r--modules/visual_script/config.py2
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptClassConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComment.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCondition.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstructor.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptDeconstruct.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEditor.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEmitSignal.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptExpression.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunction.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionCall.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionState.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexGet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexSet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptInputAction.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIterator.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVar.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptNode.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptOperator.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPreload.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertyGet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertySet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptResourcePath.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptReturn.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneNode.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneTree.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelect.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelf.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSequence.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSubCall.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSwitch.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptTypeCast.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableGet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableSet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptWhile.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYield.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYieldSignal.xml2
-rw-r--r--modules/visual_script/register_types.cpp2
-rw-r--r--modules/visual_script/visual_script.cpp19
-rw-r--r--modules/visual_script/visual_script.h8
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp4
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp85
-rw-r--r--modules/visual_script/visual_script_editor.h6
-rw-r--r--modules/visual_script/visual_script_expression.cpp13
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp2
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp102
-rw-r--r--modules/visual_script/visual_script_func_nodes.h2
-rw-r--r--modules/visual_script/visual_script_nodes.cpp227
-rw-r--r--modules/visual_script/visual_script_nodes.h25
-rw-r--r--modules/vorbis/config.py2
-rw-r--r--modules/webm/SCsub4
-rw-r--r--modules/webm/config.py4
-rw-r--r--modules/webm/doc_classes/ResourceImporterWebm.xml2
-rw-r--r--modules/webm/doc_classes/VideoStreamWebm.xml2
-rw-r--r--modules/webm/libvpx/SCsub53
-rw-r--r--modules/webm/libvpx/yasm_osx_fat.py38
-rw-r--r--modules/webp/SCsub2
-rw-r--r--modules/webp/config.py2
-rw-r--r--modules/websocket/SCsub93
-rw-r--r--modules/websocket/config.py16
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml70
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml35
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml75
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml109
-rw-r--r--modules/websocket/emws_client.cpp224
-rw-r--r--modules/websocket/emws_client.h62
-rw-r--r--modules/websocket/emws_peer.cpp175
-rw-r--r--modules/websocket/emws_peer.h85
-rw-r--r--modules/websocket/emws_server.cpp80
-rw-r--r--modules/websocket/emws_server.h61
-rw-r--r--modules/websocket/lws_client.cpp222
-rw-r--r--modules/websocket/lws_client.h61
-rw-r--r--modules/websocket/lws_helper.h220
-rw-r--r--modules/websocket/lws_peer.cpp240
-rw-r--r--modules/websocket/lws_peer.h92
-rw-r--r--modules/websocket/lws_server.cpp195
-rw-r--r--modules/websocket/lws_server.h66
-rw-r--r--modules/websocket/register_types.cpp79
-rw-r--r--modules/websocket/register_types.h31
-rw-r--r--modules/websocket/websocket_client.cpp140
-rw-r--r--modules/websocket/websocket_client.h72
-rw-r--r--modules/websocket/websocket_macros.h63
-rw-r--r--modules/websocket/websocket_multiplayer.cpp361
-rw-r--r--modules/websocket/websocket_multiplayer.h110
-rw-r--r--modules/websocket/websocket_peer.cpp51
-rw-r--r--modules/websocket/websocket_peer.h73
-rw-r--r--modules/websocket/websocket_server.cpp97
-rw-r--r--modules/websocket/websocket_server.h67
334 files changed, 17699 insertions, 3879 deletions
diff --git a/modules/bmp/SCsub b/modules/bmp/SCsub
new file mode 100644
index 0000000000..e7da7cf108
--- /dev/null
+++ b/modules/bmp/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_bmp = env_modules.Clone()
+
+# Godot's own source files
+env_bmp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/openssl/config.py b/modules/bmp/config.py
index 5f133eba90..1c8cd12a2d 100644
--- a/modules/openssl/config.py
+++ b/modules/bmp/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
new file mode 100644
index 0000000000..769119a0dc
--- /dev/null
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -0,0 +1,194 @@
+/*************************************************************************/
+/* image_loader_bmp.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 "image_loader_bmp.h"
+
+Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
+ const uint8_t *p_buffer,
+ const uint8_t *p_color_buffer,
+ const bmp_header_s &p_header) {
+
+ Error err = OK;
+
+ if (p_buffer == NULL)
+ err = FAILED;
+
+ if (err == OK) {
+ size_t index = 0;
+ size_t width =
+ static_cast<size_t>(p_header.bmp_info_header.bmp_width < 0 ? -p_header.bmp_info_header.bmp_width : p_header.bmp_info_header.bmp_width);
+ size_t height =
+ static_cast<size_t>(p_header.bmp_info_header.bmp_height < 0 ? -p_header.bmp_info_header.bmp_height : p_header.bmp_info_header.bmp_height);
+ size_t bits_per_pixel =
+ static_cast<size_t>(p_header.bmp_info_header.bmp_bit_count);
+
+ if (p_header.bmp_info_header.bmp_compression != 0) {
+ err = FAILED;
+ }
+
+ if (bits_per_pixel != 24 || bits_per_pixel != 32) {
+ err = FAILED;
+ }
+
+ if (err == OK) {
+
+ uint32_t line_width = ((p_header.bmp_info_header.bmp_width *
+ p_header.bmp_info_header.bmp_bit_count / 8) +
+ 3) &
+ ~3;
+
+ PoolVector<uint8_t> image_data;
+ err = image_data.resize(width * height * 4);
+
+ PoolVector<uint8_t>::Write image_data_w = image_data.write();
+ uint8_t *write_buffer = image_data_w.ptr();
+
+ const uint8_t *line = p_buffer + (line_width * (height - 1));
+ for (unsigned int i = 0; i < height; i++) {
+ const uint8_t *line_ptr = line;
+ for (unsigned int j = 0; j < width; j++) {
+ switch (bits_per_pixel) {
+ case 24: {
+ uint32_t color = *((uint32_t *)line_ptr);
+
+ write_buffer[index + 2] = color & 0xff;
+ write_buffer[index + 1] = (color >> 8) & 0xff;
+ write_buffer[index + 0] = (color >> 16) & 0xff;
+ write_buffer[index + 3] = 0xff;
+ index += 4;
+ line_ptr += 3;
+ } break;
+ case 32: {
+ uint32_t color = *((uint32_t *)line_ptr);
+
+ write_buffer[index + 2] = color & 0xff;
+ write_buffer[index + 1] = (color >> 8) & 0xff;
+ write_buffer[index + 0] = (color >> 16) & 0xff;
+ write_buffer[index + 3] = color >> 24;
+ index += 4;
+ line_ptr += 4;
+ } break;
+ }
+ }
+ line -= line_width;
+ }
+ p_image->create(width, height, 0, Image::FORMAT_RGBA8, image_data);
+ }
+ }
+ return err;
+}
+
+Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
+ bool p_force_linear, float p_scale) {
+
+ bmp_header_s bmp_header;
+ Error err = ERR_INVALID_DATA;
+
+ if (f->get_len() > sizeof(bmp_header)) {
+ // File Header
+ bmp_header.bmp_file_header.bmp_signature = f->get_16();
+ if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) {
+ bmp_header.bmp_file_header.bmp_file_size = f->get_32();
+ bmp_header.bmp_file_header.bmp_file_padding = f->get_32();
+ bmp_header.bmp_file_header.bmp_file_offset = f->get_32();
+
+ // Info Header
+ bmp_header.bmp_info_header.bmp_header_size = f->get_32();
+ bmp_header.bmp_info_header.bmp_width = f->get_32();
+ bmp_header.bmp_info_header.bmp_height = f->get_32();
+ bmp_header.bmp_info_header.bmp_planes = f->get_16();
+ bmp_header.bmp_info_header.bmp_bit_count = f->get_16();
+ bmp_header.bmp_info_header.bmp_compression = f->get_32();
+ bmp_header.bmp_info_header.bmp_size_image = f->get_32();
+ bmp_header.bmp_info_header.bmp_pixels_per_meter_x = f->get_32();
+ bmp_header.bmp_info_header.bmp_pixels_per_meter_y = f->get_32();
+ bmp_header.bmp_info_header.bmp_colors_used = f->get_32();
+ bmp_header.bmp_info_header.bmp_important_colors = f->get_32();
+
+ bmp_header.bmp_info_header.bmp_red_mask = f->get_32();
+ bmp_header.bmp_info_header.bmp_green_mask = f->get_32();
+ bmp_header.bmp_info_header.bmp_blue_mask = f->get_32();
+ bmp_header.bmp_info_header.bmp_alpha_mask = f->get_32();
+ bmp_header.bmp_info_header.bmp_cs_type = f->get_32();
+ for (int i = 0; i < 9; i++)
+ bmp_header.bmp_info_header.bmp_endpoints[i] = f->get_32();
+
+ bmp_header.bmp_info_header.bmp_gamma_red = f->get_32();
+ bmp_header.bmp_info_header.bmp_gamma_green = f->get_32();
+ bmp_header.bmp_info_header.bmp_gamma_blue = f->get_32();
+
+ f->seek(sizeof(bmp_header.bmp_file_header) +
+ bmp_header.bmp_info_header.bmp_header_size);
+
+ uint32_t color_table_size = 0;
+ if (bmp_header.bmp_info_header.bmp_bit_count == 1)
+ color_table_size = 2;
+ else if (bmp_header.bmp_info_header.bmp_bit_count == 4)
+ color_table_size = 16;
+ else if (bmp_header.bmp_info_header.bmp_bit_count == 8)
+ color_table_size = 256;
+
+ PoolVector<uint8_t> bmp_color_table;
+ if (color_table_size > 0) {
+ err = bmp_color_table.resize(color_table_size * 4);
+ PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write();
+ f->get_buffer(bmp_color_table_w.ptr(),
+ bmp_header.bmp_info_header.bmp_colors_used * 4);
+ }
+
+ f->seek(bmp_header.bmp_file_header.bmp_file_offset);
+
+ uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size -
+ bmp_header.bmp_file_header.bmp_file_offset);
+
+ PoolVector<uint8_t> bmp_buffer;
+ err = bmp_buffer.resize(bmp_buffer_size);
+ if (err == OK) {
+ PoolVector<uint8_t>::Write bmp_buffer_w = bmp_buffer.write();
+ f->get_buffer(bmp_buffer_w.ptr(), bmp_buffer_size);
+
+ PoolVector<uint8_t>::Read bmp_buffer_r = bmp_buffer.read();
+ PoolVector<uint8_t>::Read bmp_color_table_r = bmp_color_table.read();
+ err = convert_to_image(p_image, bmp_buffer_r.ptr(),
+ bmp_color_table_r.ptr(), bmp_header);
+ }
+ f->close();
+ }
+ }
+ return err;
+}
+
+void ImageLoaderBMP::get_recognized_extensions(
+ List<String> *p_extensions) const {
+
+ p_extensions->push_back("bmp");
+}
+
+ImageLoaderBMP::ImageLoaderBMP() {}
diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h
new file mode 100644
index 0000000000..3fa7481287
--- /dev/null
+++ b/modules/bmp/image_loader_bmp.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* image_loader_bmp.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 IMAGE_LOADER_BMP_H
+#define IMAGE_LOADER_BMP_H
+
+#include "io/image_loader.h"
+
+class ImageLoaderBMP : public ImageFormatLoader {
+protected:
+ static const unsigned BITMAP_SIGNATURE = 0x4d42;
+
+ struct bmp_header_s {
+ struct bmp_file_header_s {
+ uint16_t bmp_signature;
+ uint32_t bmp_file_size;
+ uint32_t bmp_file_padding;
+ uint32_t bmp_file_offset;
+ } bmp_file_header;
+
+ struct bmp_info_header_s {
+ uint32_t bmp_header_size;
+ uint32_t bmp_width;
+ uint32_t bmp_height;
+ uint16_t bmp_planes;
+ uint16_t bmp_bit_count;
+ uint32_t bmp_compression;
+ uint32_t bmp_size_image;
+ uint32_t bmp_pixels_per_meter_x;
+ uint32_t bmp_pixels_per_meter_y;
+ uint32_t bmp_colors_used;
+ uint32_t bmp_important_colors;
+ uint32_t bmp_red_mask;
+ uint32_t bmp_green_mask;
+ uint32_t bmp_blue_mask;
+ uint32_t bmp_alpha_mask;
+ uint32_t bmp_cs_type;
+ uint32_t bmp_endpoints[9];
+ uint32_t bmp_gamma_red;
+ uint32_t bmp_gamma_green;
+ uint32_t bmp_gamma_blue;
+ } bmp_info_header;
+ };
+
+ static Error convert_to_image(Ref<Image> p_image,
+ const uint8_t *p_buffer,
+ const uint8_t *p_color_buffer,
+ const bmp_header_s &p_header);
+
+public:
+ virtual Error load_image(Ref<Image> p_image, FileAccess *f,
+ bool p_force_linear, float p_scale);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ ImageLoaderBMP();
+};
+
+#endif // IMAGE_LOADER_BMP_H
diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp
new file mode 100644
index 0000000000..1f68a03e85
--- /dev/null
+++ b/modules/bmp/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* 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 "image_loader_bmp.h"
+
+static ImageLoaderBMP *image_loader_bmp = NULL;
+
+void register_bmp_types() {
+ image_loader_bmp = memnew(ImageLoaderBMP);
+ ImageLoader::add_image_format_loader(image_loader_bmp);
+}
+
+void unregister_bmp_types() {
+ memdelete(image_loader_bmp);
+}
diff --git a/modules/openssl/register_types.h b/modules/bmp/register_types.h
index 94d917ca81..d8755db397 100644
--- a/modules/openssl/register_types.h
+++ b/modules/bmp/register_types.h
@@ -28,5 +28,5 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_openssl_types();
-void unregister_openssl_types();
+void register_bmp_types();
+void unregister_bmp_types();
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 648919e612..bfb452d109 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -68,7 +68,9 @@ AreaBullet::AreaBullet() :
}
AreaBullet::~AreaBullet() {
- remove_all_overlapping_instantly();
+ // signal are handled by godot, so just clear without notify
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i)
+ overlappingObjects[i].object->on_exit_area(this);
}
void AreaBullet::dispatch_callbacks() {
@@ -121,23 +123,21 @@ void AreaBullet::scratch() {
isScratched = true;
}
-void AreaBullet::remove_all_overlapping_instantly() {
- CollisionObjectBullet *supportObject;
+void AreaBullet::clear_overlaps(bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
}
overlappingObjects.clear();
}
-void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) {
- CollisionObjectBullet *supportObject;
+void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- if (supportObject == p_object) {
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (overlappingObjects[i].object == p_object) {
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
overlappingObjects.remove(i);
break;
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 78136d574b..b2046c684e 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -150,9 +150,9 @@ public:
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
- void remove_all_overlapping_instantly();
+ void clear_overlaps(bool p_notify);
// Dispatch the callbacks and removes from overlapping list
- void remove_overlapping_instantly(CollisionObjectBullet *p_object);
+ void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp
index 4164450cd2..8707096038 100644
--- a/modules/bullet/btRayShape.cpp
+++ b/modules/bullet/btRayShape.cpp
@@ -54,6 +54,11 @@ void btRayShape::setLength(btScalar p_length) {
reload_cache();
}
+void btRayShape::setSlipsOnSlope(bool p_slipsOnSlope) {
+
+ slipsOnSlope = p_slipsOnSlope;
+}
+
btVector3 btRayShape::localGetSupportingVertex(const btVector3 &vec) const {
return localGetSupportingVertexWithoutMargin(vec) + (m_shapeAxis * m_collisionMargin);
}
diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h
index 99a9412dbe..a44c30db4b 100644
--- a/modules/bullet/btRayShape.h
+++ b/modules/bullet/btRayShape.h
@@ -44,6 +44,7 @@ ATTRIBUTE_ALIGNED16(class)
btRayShape : public btConvexInternalShape {
btScalar m_length;
+ bool slipsOnSlope;
/// The default axis is the z
btVector3 m_shapeAxis;
@@ -59,6 +60,9 @@ public:
void setLength(btScalar p_length);
btScalar getLength() const { return m_length; }
+ void setSlipsOnSlope(bool p_slipOnSlope);
+ bool getSlipsOnSlope() const { return slipsOnSlope; }
+
const btTransform &getSupportPoint() const { return m_cacheSupportPoint; }
const btScalar &getScaledLength() const { return m_cacheScaledLength; }
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 51de4998fa..6246a295ec 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -70,8 +70,8 @@
return RID(); \
}
-#define AddJointToSpace(body, joint, disableCollisionsBetweenLinkedBodies) \
- body->get_space()->add_constraint(joint, disableCollisionsBetweenLinkedBodies);
+#define AddJointToSpace(body, joint) \
+ body->get_space()->add_constraint(joint, joint->is_disabled_collisions_between_bodies());
// <--------------- Joint creation asserts
btEmptyShape *BulletPhysicsServer::emptyShape(ShapeBullet::create_shape_empty());
@@ -89,7 +89,9 @@ BulletPhysicsServer::BulletPhysicsServer() :
active(true),
active_spaces_count(0) {}
-BulletPhysicsServer::~BulletPhysicsServer() {}
+BulletPhysicsServer::~BulletPhysicsServer() {
+ bulletdelete(emptyShape);
+}
RID BulletPhysicsServer::shape_create(ShapeType p_shape) {
ShapeBullet *shape = NULL;
@@ -619,11 +621,11 @@ uint32_t BulletPhysicsServer::body_get_collision_mask(RID p_body) const {
}
void BulletPhysicsServer::body_set_user_flags(RID p_body, uint32_t p_flags) {
- WARN_PRINT("This function si not currently supported by bullet and Godot");
+ // This function si not currently supported
}
uint32_t BulletPhysicsServer::body_get_user_flags(RID p_body) const {
- WARN_PRINT("This function si not currently supported by bullet and Godot");
+ // This function si not currently supported
return 0;
}
@@ -783,22 +785,27 @@ int BulletPhysicsServer::body_get_max_contacts_reported(RID p_body) const {
return body->get_max_collisions_detection();
}
-void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold) {
- WARN_PRINT("Not supported by bullet and even Godot");
+void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) {
+ // Not supported by bullet and even Godot
}
float BulletPhysicsServer::body_get_contacts_reported_depth_threshold(RID p_body) const {
- WARN_PRINT("Not supported by bullet and even Godot");
+ // Not supported by bullet and even Godot
return 0.;
}
void BulletPhysicsServer::body_set_omit_force_integration(RID p_body, bool p_omit) {
- WARN_PRINT("Not supported by bullet");
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_omit_forces_integration(p_omit);
}
bool BulletPhysicsServer::body_is_omitting_force_integration(RID p_body) const {
- WARN_PRINT("Not supported by bullet");
- return false;
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+
+ return body->get_omit_forces_integration();
}
void BulletPhysicsServer::body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata) {
@@ -825,12 +832,12 @@ PhysicsDirectBodyState *BulletPhysicsServer::body_get_direct_state(RID p_body) {
return BulletPhysicsDirectBodyState::get_singleton(body);
}
-bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, MotionResult *r_result) {
+bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result) {
RigidBodyBullet *body = rigid_body_owner.get(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
- return body->get_space()->test_body_motion(body, p_from, p_motion, r_result);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result);
}
RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
@@ -979,14 +986,28 @@ PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const
}
void BulletPhysicsServer::joint_set_solver_priority(RID p_joint, int p_priority) {
- //WARN_PRINTS("Joint priority not supported by bullet");
+ // Joint priority not supported by bullet
}
int BulletPhysicsServer::joint_get_solver_priority(RID p_joint) const {
- //WARN_PRINTS("Joint priority not supported by bullet");
+ // Joint priority not supported by bullet
return 0;
}
+void BulletPhysicsServer::joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable) {
+ JointBullet *joint = joint_owner.get(p_joint);
+ ERR_FAIL_COND(!joint);
+
+ joint->disable_collisions_between_bodies(p_disable);
+}
+
+bool BulletPhysicsServer::joint_is_disabled_collisions_between_bodies(RID p_joint) const {
+ JointBullet *joint(joint_owner.get(p_joint));
+ ERR_FAIL_COND_V(!joint, false);
+
+ return joint->is_disabled_collisions_between_bodies();
+}
+
RID BulletPhysicsServer::joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) {
RigidBodyBullet *body_A = rigid_body_owner.get(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
@@ -1003,7 +1024,7 @@ RID BulletPhysicsServer::joint_create_pin(RID p_body_A, const Vector3 &p_local_A
ERR_FAIL_COND_V(body_A == body_B, RID());
JointBullet *joint = bulletnew(PinJointBullet(body_A, p_local_A, body_B, p_local_B));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
@@ -1071,7 +1092,7 @@ RID BulletPhysicsServer::joint_create_hinge(RID p_body_A, const Transform &p_hin
ERR_FAIL_COND_V(body_A == body_B, RID());
JointBullet *joint = bulletnew(HingeJointBullet(body_A, body_B, p_hinge_A, p_hinge_B));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
@@ -1091,7 +1112,7 @@ RID BulletPhysicsServer::joint_create_hinge_simple(RID p_body_A, const Vector3 &
ERR_FAIL_COND_V(body_A == body_B, RID());
JointBullet *joint = bulletnew(HingeJointBullet(body_A, body_B, p_pivot_A, p_pivot_B, p_axis_A, p_axis_B));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
@@ -1143,7 +1164,7 @@ RID BulletPhysicsServer::joint_create_slider(RID p_body_A, const Transform &p_lo
ERR_FAIL_COND_V(body_A == body_B, RID());
JointBullet *joint = bulletnew(SliderJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
@@ -1177,7 +1198,7 @@ RID BulletPhysicsServer::joint_create_cone_twist(RID p_body_A, const Transform &
}
JointBullet *joint = bulletnew(ConeTwistJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
@@ -1213,7 +1234,7 @@ RID BulletPhysicsServer::joint_create_generic_6dof(RID p_body_A, const Transform
ERR_FAIL_COND_V(body_A == body_B, RID());
JointBullet *joint = bulletnew(Generic6DOFJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B, true));
- AddJointToSpace(body_A, joint, true);
+ AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
}
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 1c94428a2a..e931915bba 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -154,7 +154,7 @@ public:
/// AREA_PARAM_GRAVITY_VECTOR
/// Otherwise you can set area parameters
virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value);
- virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const;
+ virtual Variant area_get_param(RID p_area, AreaParameter p_param) const;
virtual void area_set_transform(RID p_area, const Transform &p_transform);
virtual Transform area_get_transform(RID p_area) const;
@@ -239,7 +239,7 @@ public:
virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
virtual int body_get_max_contacts_reported(RID p_body) const;
- virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_treshold);
+ virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold);
virtual float body_get_contacts_reported_depth_threshold(RID p_body) const;
virtual void body_set_omit_force_integration(RID p_body, bool p_omit);
@@ -253,7 +253,7 @@ public:
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body);
- virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, MotionResult *r_result = NULL);
+ virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL);
/* SOFT BODY API */
@@ -290,6 +290,9 @@ public:
virtual void joint_set_solver_priority(RID p_joint, int p_priority);
virtual int joint_get_solver_priority(RID p_joint) const;
+ virtual void joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable);
+ virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const;
+
virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B);
virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value);
@@ -301,7 +304,7 @@ public:
virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B);
virtual Vector3 pin_joint_get_local_b(RID p_joint) const;
- virtual RID joint_create_hinge(RID p_body_A, const Transform &p_frame_A, RID p_body_B, const Transform &p_frame_B);
+ virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B);
virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B);
virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value);
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 34aff68a4a..57e4db708e 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -49,7 +49,7 @@
CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {}
void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_transform) {
- G_TO_B(p_transform.get_basis().get_scale(), scale);
+ G_TO_B(p_transform.get_basis().get_scale_abs(), scale);
G_TO_B(p_transform, transform);
UNSCALE_BT_BASIS(transform);
}
@@ -68,12 +68,10 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
force_shape_reset(false) {}
CollisionObjectBullet::~CollisionObjectBullet() {
- // Remove all overlapping
+ // Remove all overlapping, notify is not required since godot take care of it
for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
- areasOverlapped[i]->remove_overlapping_instantly(this);
+ areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
}
- // not required
- // areasOverlapped.clear();
destroyBulletCollisionObject();
}
@@ -160,7 +158,7 @@ int CollisionObjectBullet::get_godot_object_flags() const {
void CollisionObjectBullet::set_transform(const Transform &p_global_transform) {
- set_body_scale(p_global_transform.basis.get_scale());
+ set_body_scale(p_global_transform.basis.get_scale_abs());
btTransform bt_transform;
G_TO_B(p_global_transform, bt_transform);
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/constraint_bullet.cpp b/modules/bullet/constraint_bullet.cpp
index b60e89b6fd..d15fb8de01 100644
--- a/modules/bullet/constraint_bullet.cpp
+++ b/modules/bullet/constraint_bullet.cpp
@@ -39,7 +39,8 @@
ConstraintBullet::ConstraintBullet() :
space(NULL),
- constraint(NULL) {}
+ constraint(NULL),
+ disabled_collisions_between_bodies(true) {}
void ConstraintBullet::setup(btTypedConstraint *p_constraint) {
constraint = p_constraint;
@@ -53,3 +54,12 @@ void ConstraintBullet::set_space(SpaceBullet *p_space) {
void ConstraintBullet::destroy_internal_constraint() {
space->remove_constraint(this);
}
+
+void ConstraintBullet::disable_collisions_between_bodies(const bool p_disabled) {
+ disabled_collisions_between_bodies = p_disabled;
+
+ if (space) {
+ space->remove_constraint(this);
+ space->add_constraint(this, disabled_collisions_between_bodies);
+ }
+}
diff --git a/modules/bullet/constraint_bullet.h b/modules/bullet/constraint_bullet.h
index 23be5a5063..ed3a318cbc 100644
--- a/modules/bullet/constraint_bullet.h
+++ b/modules/bullet/constraint_bullet.h
@@ -49,6 +49,7 @@ class ConstraintBullet : public RIDBullet {
protected:
SpaceBullet *space;
btTypedConstraint *constraint;
+ bool disabled_collisions_between_bodies;
public:
ConstraintBullet();
@@ -57,6 +58,9 @@ public:
virtual void set_space(SpaceBullet *p_space);
virtual void destroy_internal_constraint();
+ void disable_collisions_between_bodies(const bool p_disabled);
+ _FORCE_INLINE_ bool is_disabled_collisions_between_bodies() const { return disabled_collisions_between_bodies; }
+
public:
virtual ~ConstraintBullet() {
bulletdelete(constraint);
diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml
index c7909c7d72..a4dc61d0bc 100644
--- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml
+++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.0-stable">
+<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml
index a59abb0ebb..1486936cf4 100644
--- a/modules/bullet/doc_classes/BulletPhysicsServer.xml
+++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.0-stable">
+<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index 151a79a69f..adfad7803f 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -138,6 +138,12 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
sixDOFConstraint->getTranslationalLimitMotor()->m_damping = p_value;
break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis] = p_value;
+ break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
limits_lower[1][p_axis] = p_value;
set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter
@@ -185,6 +191,10 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6
return sixDOFConstraint->getTranslationalLimitMotor()->m_restitution;
case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
return sixDOFConstraint->getTranslationalLimitMotor()->m_damping;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis];
case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
return limits_lower[1][p_axis];
case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
@@ -232,6 +242,9 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF
case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR:
sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableMotor = flags[p_axis][p_flag];
break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag];
+ break;
default:
WARN_PRINT("This flag is not supported by Bullet engine");
return;
diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h
index fe5d8418b7..fa58e86589 100644
--- a/modules/bullet/godot_motion_state.h
+++ b/modules/bullet/godot_motion_state.h
@@ -87,7 +87,7 @@ public:
public:
/// Use this function to move kinematic body
- /// -- or set initial transfom before body creation.
+ /// -- or set initial transform before body creation.
void moveBody(const btTransform &newWorldTransform) {
bodyKinematicWorldTransf = newWorldTransform;
}
diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp
index 4a511b39a7..53d0ab7e3c 100644
--- a/modules/bullet/godot_ray_world_algorithm.cpp
+++ b/modules/bullet/godot_ray_world_algorithm.cpp
@@ -100,14 +100,16 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo
if (btResult.hasHit()) {
- btVector3 ray_normal(ray_transform.getOrigin() - to.getOrigin());
- ray_normal.normalize();
btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1));
if (depth >= -RAY_STABILITY_MARGIN)
depth = 0;
- resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, depth);
+ if (ray_shape->getSlipsOnSlope())
+ resultOut->addContactPoint(btResult.m_hitNormalWorld, btResult.m_hitPointWorld, depth);
+ else {
+ resultOut->addContactPoint((ray_transform.getOrigin() - to.getOrigin()).normalize(), btResult.m_hitPointWorld, depth);
+ }
}
}
diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h
index c716c1d88d..7383dad2bf 100644
--- a/modules/bullet/godot_ray_world_algorithm.h
+++ b/modules/bullet/godot_ray_world_algorithm.h
@@ -49,7 +49,7 @@ class GodotRayWorldAlgorithm : public btActivatingCollisionAlgorithm {
bool m_isSwapped;
public:
- GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *m_world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped);
+ GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped);
virtual ~GodotRayWorldAlgorithm();
virtual void processCollision(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut);
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 8d4ca6d6a7..197550d686 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -63,6 +63,9 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
}
bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -70,6 +73,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con
if (m_exclude->has(gObj->get_self())) {
return false;
}
+
return true;
} else {
return false;
@@ -87,7 +91,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
++count;
- return count < m_resultMax;
+ return 1; // not used by bullet
}
bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -98,11 +102,16 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
if (gObj == m_self_object) {
return false;
} else {
- if (m_ignore_areas && gObj->getType() == CollisionObjectBullet::TYPE_AREA) {
+
+ // A kinematic body can't be stopped by a rigid body since the mass of kinematic body is infinite
+ if (m_infinite_inertia && !btObj->isStaticOrKinematicObject())
return false;
- } else if (m_self_object->has_collision_exception(gObj)) {
+
+ if (gObj->getType() == CollisionObjectBullet::TYPE_AREA)
+ return false;
+
+ if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object))
return false;
- }
}
return true;
} else {
@@ -163,10 +172,7 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
result.shape = cp.m_index0;
}
- if (colObj)
- result.collider_id = colObj->get_instance_id();
- else
- result.collider_id = 0;
+ result.collider_id = colObj->get_instance_id();
result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
result.rid = colObj->get_self();
++m_count;
@@ -176,6 +182,9 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
}
bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (m_count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -201,7 +210,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint
++m_count;
- return m_count < m_resultMax;
+ return 1; // Not used by bullet
}
bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -238,32 +247,23 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp
m_rest_info_collision_object = colObj0Wrap->getCollisionObject();
}
- if (colObj)
- m_result->collider_id = colObj->get_instance_id();
- else
- m_result->collider_id = 0;
+ m_result->collider_id = colObj->get_instance_id();
m_result->rid = colObj->get_self();
m_collided = true;
}
- return cp.getDistance();
+ return 1; // Not used by bullet
}
void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) {
- if (depth < 0) {
- // Has penetration
- if (m_most_penetrated_distance > depth) {
-
- bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
+ if (m_penetration_distance > depth) { // Has penetration?
- m_most_penetrated_distance = depth;
- m_pointCollisionObject = (isSwapped ? m_body0Wrap : m_body1Wrap)->getCollisionObject();
- m_other_compound_shape_index = isSwapped ? m_index1 : m_index0;
- m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
- m_pointWorld = isSwapped ? (pointInWorldOnB + normalOnBInWorld * depth) : pointInWorldOnB;
- m_penetration_distance = depth;
- }
+ bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
+ m_penetration_distance = depth;
+ m_other_compound_shape_index = isSwapped ? m_index0 : m_index1;
+ m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
+ m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB;
}
}
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index b18965a5b8..363051f24c 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -93,12 +93,12 @@ public:
struct GodotKinClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback {
public:
const RigidBodyBullet *m_self_object;
- const bool m_ignore_areas;
+ const bool m_infinite_inertia;
- GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_ignore_areas) :
+ GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
m_self_object(p_self_object),
- m_ignore_areas(p_ignore_areas) {}
+ m_infinite_inertia(p_infinite_inertia) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
};
@@ -185,26 +185,20 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult {
btVector3 m_pointWorld;
btScalar m_penetration_distance;
int m_other_compound_shape_index;
- const btCollisionObject *m_pointCollisionObject;
-
- btScalar m_most_penetrated_distance;
GodotDeepPenetrationContactResultCallback(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) :
btManifoldResult(body0Wrap, body1Wrap),
- m_pointCollisionObject(NULL),
m_penetration_distance(0),
- m_other_compound_shape_index(0),
- m_most_penetrated_distance(1e20) {}
+ m_other_compound_shape_index(0) {}
void reset() {
- m_pointCollisionObject = NULL;
- m_most_penetrated_distance = 1e20;
+ m_penetration_distance = 0;
}
bool hasHit() {
- return m_pointCollisionObject;
+ return m_penetration_distance < 0;
}
- virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorld, btScalar depth);
+ virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth);
};
#endif // GODOT_RESULT_CALLBACKS_H
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 96a53f9f8b..2494063c22 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -114,10 +114,18 @@ Transform BulletPhysicsDirectBodyState::get_transform() const {
return body->get_transform();
}
+void BulletPhysicsDirectBodyState::add_central_force(const Vector3 &p_force) {
+ body->apply_central_force(p_force);
+}
+
void BulletPhysicsDirectBodyState::add_force(const Vector3 &p_force, const Vector3 &p_pos) {
body->apply_force(p_force, p_pos);
}
+void BulletPhysicsDirectBodyState::add_torque(const Vector3 &p_torque) {
+ body->apply_torque(p_torque);
+}
+
void BulletPhysicsDirectBodyState::apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) {
body->apply_impulse(p_pos, p_j);
}
@@ -247,6 +255,7 @@ RigidBodyBullet::RigidBodyBullet() :
linearDamp(0),
angularDamp(0),
can_sleep(true),
+ omit_forces_integration(false),
force_integration_callback(NULL),
isTransformChanged(false),
previousActiveState(true),
@@ -326,6 +335,9 @@ void RigidBodyBullet::dispatch_callbacks() {
/// The check isTransformChanged is necessary in order to call integrated forces only when the first transform is sent
if ((btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && isTransformChanged) {
+ if (omit_forces_integration)
+ btBody->clearForces();
+
BulletPhysicsDirectBodyState *bodyDirect = BulletPhysicsDirectBodyState::get_singleton(this);
Variant variantBodyDirect = bodyDirect;
@@ -429,6 +441,10 @@ bool RigidBodyBullet::is_active() const {
return btBody->isActive();
}
+void RigidBodyBullet::set_omit_forces_integration(bool p_omit) {
+ omit_forces_integration = p_omit;
+}
+
void RigidBodyBullet::set_param(PhysicsServer::BodyParameter p_param, real_t p_value) {
switch (p_param) {
case PhysicsServer::BODY_PARAM_BOUNCE:
@@ -832,7 +848,8 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
void RigidBodyBullet::reload_space_override_modificator() {
- if (!is_active())
+ // Make sure that kinematic bodies have their total gravity calculated
+ if (!is_active() && PhysicsServer::BODY_MODE_KINEMATIC != mode)
return;
Vector3 newGravity(space->get_gravity_direction() * space->get_gravity_magnitude());
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index aff6056ad9..b9511243c7 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -110,7 +110,9 @@ public:
virtual void set_transform(const Transform &p_transform);
virtual Transform get_transform() const;
+ virtual void add_central_force(const Vector3 &p_force);
virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos);
+ virtual void add_torque(const Vector3 &p_torque);
virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j);
virtual void apply_torque_impulse(const Vector3 &p_j);
@@ -196,6 +198,7 @@ private:
real_t linearDamp;
real_t angularDamp;
bool can_sleep;
+ bool omit_forces_integration;
Vector<CollisionData> collisions;
// these parameters are used to avoid vector resize
@@ -252,6 +255,9 @@ public:
void set_activation_state(bool p_active);
bool is_active() const;
+ void set_omit_forces_integration(bool p_omit);
+ _FORCE_INLINE_ bool get_omit_forces_integration() const { return omit_forces_integration; }
+
void set_param(PhysicsServer::BodyParameter p_param, real_t);
real_t get_param(PhysicsServer::BodyParameter p_param) const;
@@ -262,12 +268,12 @@ public:
Variant get_state(PhysicsServer::BodyState p_state) const;
void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse);
- void apply_central_impulse(const Vector3 &p_force);
+ void apply_central_impulse(const Vector3 &p_impulse);
void apply_torque_impulse(const Vector3 &p_impulse);
void apply_force(const Vector3 &p_force, const Vector3 &p_pos);
void apply_central_force(const Vector3 &p_force);
- void apply_torque(const Vector3 &p_force);
+ void apply_torque(const Vector3 &p_torque);
void set_applied_force(const Vector3 &p_force);
Vector3 get_applied_force() const;
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index c6b9695d96..76d9614465 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -125,18 +125,19 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes
}
}
-btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
const btScalar ignoredHeightScale(1);
- const btScalar fieldHeight(500); // Meters
const int YAxis = 1; // 0=X, 1=Y, 2=Z
const bool flipQuadEdges = false;
const void *heightsPtr = p_heights.read().ptr();
- return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges));
+ return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
}
-btRayShape *ShapeBullet::create_shape_ray(real_t p_length) {
- return bulletnew(btRayShape(p_length));
+btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) {
+ btRayShape *r(bulletnew(btRayShape(p_length)));
+ r->setSlipsOnSlope(p_slips_on_slope);
+ return r;
}
/* PLANE */
@@ -335,10 +336,10 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) {
int src_face_count = faces.size();
if (0 < src_face_count) {
- btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh);
-
// It counts the faces and assert the array contains the correct number of vertices.
ERR_FAIL_COND(src_face_count % 3);
+
+ btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh);
src_face_count /= 3;
PoolVector<Vector3>::Read r = p_faces.read();
const Vector3 *facesr = r.ptr();
@@ -385,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
Dictionary d = p_data;
ERR_FAIL_COND(!d.has("width"));
ERR_FAIL_COND(!d.has("depth"));
- ERR_FAIL_COND(!d.has("cell_size"));
ERR_FAIL_COND(!d.has("heights"));
+ real_t l_min_height = 0.0;
+ real_t l_max_height = 0.0;
+
+ // If specified, min and max height will be used as precomputed values
+ if (d.has("min_height"))
+ l_min_height = d["min_height"];
+ if (d.has("max_height"))
+ l_max_height = d["max_height"];
+
+ ERR_FAIL_COND(l_min_height > l_max_height);
+
int l_width = d["width"];
int l_depth = d["depth"];
- real_t l_cell_size = d["cell_size"];
PoolVector<real_t> l_heights = d["heights"];
ERR_FAIL_COND(l_width <= 0);
ERR_FAIL_COND(l_depth <= 0);
- ERR_FAIL_COND(l_cell_size <= CMP_EPSILON);
- ERR_FAIL_COND(l_heights.size() != (width * depth));
- setup(heights, width, depth, cell_size);
+ ERR_FAIL_COND(l_heights.size() != (l_width * l_depth));
+
+ // Compute min and max heights if not specified.
+ if (!d.has("min_height") && !d.has("max_height")) {
+
+ PoolVector<real_t>::Read r = heights.read();
+ int heights_size = heights.size();
+
+ for (int i = 0; i < heights_size; ++i) {
+ real_t h = r[i];
+
+ if (h < l_min_height)
+ l_min_height = h;
+ else if (h > l_max_height)
+ l_max_height = h;
+ }
+ }
+
+ setup(l_heights, l_width, l_depth, l_min_height, l_max_height);
}
Variant HeightMapShapeBullet::get_data() const {
@@ -408,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const {
return PhysicsServer::SHAPE_HEIGHTMAP;
}
-void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
+ // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes
+
{ // Copy
+
+ // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields
+ // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data).
+
const int heights_size = p_heights.size();
heights.resize(heights_size);
PoolVector<real_t>::Read p_heights_r = p_heights.read();
@@ -418,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
heights_w[i] = p_heights_r[i];
}
}
+
width = p_width;
depth = p_depth;
- cell_size = p_cell_size;
+ min_height = p_min_height;
+ max_height = p_max_height;
notifyShapeChanged();
}
btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size));
+ btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
cs->setMargin(p_margin);
@@ -435,25 +469,33 @@ btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_impli
/* Ray shape */
RayShapeBullet::RayShapeBullet() :
ShapeBullet(),
- length(1) {}
+ length(1),
+ slips_on_slope(false) {}
void RayShapeBullet::set_data(const Variant &p_data) {
- setup(p_data);
+
+ Dictionary d = p_data;
+ setup(d["length"], d["slips_on_slope"]);
}
Variant RayShapeBullet::get_data() const {
- return length;
+
+ Dictionary d;
+ d["length"] = length;
+ d["slips_on_slope"] = slips_on_slope;
+ return d;
}
PhysicsServer::ShapeType RayShapeBullet::get_type() const {
return PhysicsServer::SHAPE_RAY;
}
-void RayShapeBullet::setup(real_t p_length) {
+void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) {
length = p_length;
+ slips_on_slope = p_slips_on_slope;
notifyShapeChanged();
}
btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin));
+ return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin, slips_on_slope));
}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 4a03c0f014..abeea0f9ce 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -85,8 +85,8 @@ public:
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
- static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
- static class btRayShape *create_shape_ray(real_t p_length);
+ static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
+ static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope);
};
class PlaneShapeBullet : public ShapeBullet {
@@ -99,7 +99,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(const Plane &p_plane);
@@ -116,7 +116,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(real_t p_radius);
@@ -133,7 +133,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(const Vector3 &p_half_extents);
@@ -152,7 +152,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -169,7 +169,7 @@ public:
void get_vertices(Vector<Vector3> &out_vertices);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(const Vector<Vector3> &p_vertices);
@@ -187,7 +187,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(PoolVector<Vector3> p_faces);
@@ -199,32 +199,34 @@ public:
PoolVector<real_t> heights;
int width;
int depth;
- real_t cell_size;
+ real_t min_height;
+ real_t max_height;
HeightMapShapeBullet();
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
- void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
};
class RayShapeBullet : public ShapeBullet {
public:
real_t length;
+ bool slips_on_slope;
RayShapeBullet();
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
- void setup(real_t p_length);
+ void setup(real_t p_length, bool p_slips_on_slope);
};
#endif
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index d60d8ba0e2..971fd39509 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -116,13 +116,13 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
}
}
-int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *p_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
if (p_result_max <= 0)
return 0;
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -138,7 +138,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotAllContactResultCallback btQuery(&collision_object, p_results, p_result_max, &p_exclude);
+ GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -202,7 +202,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -234,7 +234,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -628,7 +628,7 @@ void SpaceBullet::destroy_world() {
void SpaceBullet::check_ghost_overlaps() {
- /// Algorith support variables
+ /// Algorithm support variables
btConvexShape *other_body_shape;
btConvexShape *area_shape;
btGjkPairDetector::ClosestPointInput gjk_input;
@@ -660,7 +660,10 @@ void SpaceBullet::check_ghost_overlaps() {
// For each overlapping
for (i = ghostOverlaps.size() - 1; 0 <= i; --i) {
- if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA))
+ if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) {
+ if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable())
+ continue;
+ } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY)
continue;
otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer());
@@ -790,7 +793,7 @@ void SpaceBullet::update_gravity() {
/// I'm leaving this here just for future tests.
/// Debug motion and normal vector drawing
#define debug_test_motion 0
-#define PERFORM_INITIAL_UNSTACK 0
+
#define RECOVERING_MOVEMENT_SCALE 0.4
#define RECOVERING_MOVEMENT_CYCLES 4
@@ -804,8 +807,7 @@ static Ref<SpatialMaterial> red_mat;
static Ref<SpatialMaterial> blue_mat;
#endif
-#define IGNORE_AREAS_TRUE true
-bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, PhysicsServer::MotionResult *r_result) {
+bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result) {
#if debug_test_motion
/// Yes I know this is not good, but I've used it as fast debugging hack.
@@ -839,33 +841,20 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
#endif
- ///// Release all generated manifolds
- //{
- // if(p_body->get_kinematic_utilities()){
- // for(int i= p_body->get_kinematic_utilities()->m_generatedManifold.size()-1; 0<=i; --i){
- // dispatcher->releaseManifold( p_body->get_kinematic_utilities()->m_generatedManifold[i] );
- // }
- // p_body->get_kinematic_utilities()->m_generatedManifold.clear();
- // }
- //}
-
- btTransform body_safe_position;
- G_TO_B(p_from, body_safe_position);
- UNSCALE_BT_BASIS(body_safe_position);
-
-#if PERFORM_INITIAL_UNSTACK
- btVector3 recover_initial_position(0, 0, 0);
+ btTransform body_transform;
+ G_TO_B(p_from, body_transform);
+ UNSCALE_BT_BASIS(body_transform);
+
+ 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, 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;
}
-#endif
btVector3 motion;
G_TO_B(p_motion, motion);
@@ -895,16 +884,16 @@ 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;
- GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, IGNORE_AREAS_TRUE);
+ GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia);
btResult.m_collisionFilterGroup = p_body->get_collision_layer();
btResult.m_collisionFilterMask = p_body->get_collision_mask();
- dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, 0.002);
+ dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration);
if (btResult.hasHit()) {
/// Since for each sweep test I fix the motion of new shapes in base the recover result,
@@ -913,72 +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, 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) {
-#if PERFORM_INITIAL_UNSTACK
- B_TO_G(motion + delta_recover_movement + recover_initial_position, r_result->motion);
-#else
- B_TO_G(motion + delta_recover_movement, r_result->motion);
-#endif
- if (l_has_penetration) {
- has_penetration = true;
- if (l_penetration_distance <= r_recover_result.penetration_distance) {
- continue;
- }
+ // Parse results
+ if (r_result) {
+ B_TO_G(motion + initial_recover_motion, r_result->motion);
+
+ 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());
-
- 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;
-
- //{ /// Add manifold point to manage collisions
- // btPersistentManifold* manifold = dynamicsWorld->getDispatcher()->getNewManifold(p_body->getBtBody(), btRigid);
- // btManifoldPoint manifoldPoint(result_callabck.m_pointWorld, result_callabck.m_pointWorld, result_callabck.m_pointNormalWorld, result_callabck.m_penetration_distance);
- // manifoldPoint.m_index0 = r_result->collision_local_shape;
- // manifoldPoint.m_index1 = r_result->collider_shape;
- // manifold->addManifoldPoint(manifoldPoint);
- // p_body->get_kinematic_utilities()->m_generatedManifold.push_back(manifold);
- //}
+ 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(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;
+ r_result->remainder = Vector3();
}
}
}
@@ -1016,11 +982,11 @@ public:
}
void reset() {
- result_collision_objects.empty();
+ result_collision_objects.clear();
}
};
-bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) {
+bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) {
RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask());
@@ -1051,7 +1017,10 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
- if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
+ if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
+ otherObject->activate(); // Force activation of hitten rigid, soft body
+ continue;
+ } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
continue;
if (otherObject->getCollisionShape()->isCompound()) {
@@ -1130,7 +1099,7 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
btCollisionObjectWrapper obA(NULL, p_shapeA, p_objectA, tA, -1, p_shapeId_A);
btCollisionObjectWrapper obB(NULL, p_shapeB, p_objectB, p_transformB, -1, p_shapeId_B);
- btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CLOSEST_POINT_ALGORITHMS);
+ btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS);
if (algorithm) {
GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB);
//discrete collision detection query
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 0aeb407dcc..a6c2786878 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -172,7 +172,7 @@ public:
void update_gravity();
- bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, PhysicsServer::MotionResult *r_result);
+ bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result);
private:
void create_empty_world(bool p_create_soft_world);
@@ -199,12 +199,12 @@ private:
local_shape_most_recovered(0) {}
};
- bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
+ bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
/// This is an API that recover a kinematic object from penetration
/// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions
- bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
+ bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
/// This is an API that recover a kinematic object from penetration
/// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm
- bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
+ bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
};
#endif
diff --git a/modules/csg/SCsub b/modules/csg/SCsub
new file mode 100644
index 0000000000..57c504efd8
--- /dev/null
+++ b/modules/csg/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_csg = env_modules.Clone()
+
+# Godot's own source files
+env_csg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/csg/config.py b/modules/csg/config.py
new file mode 100644
index 0000000000..38ccc66d91
--- /dev/null
+++ b/modules/csg/config.py
@@ -0,0 +1,21 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
+
+def get_doc_classes():
+ return [
+ "CSGBox",
+ "CSGCombiner",
+ "CSGCylinder",
+ "CSGMesh",
+ "CSGPolygon",
+ "CSGPrimitive",
+ "CSGShape",
+ "CSGSphere",
+ "CSGTorus",
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
new file mode 100644
index 0000000000..4e6e701bfd
--- /dev/null
+++ b/modules/csg/csg.cpp
@@ -0,0 +1,1518 @@
+/*************************************************************************/
+/* csg.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 "csg.h"
+#include "face3.h"
+#include "geometry.h"
+#include "os/os.h"
+#include "sort.h"
+#include "thirdparty/misc/triangulator.h"
+
+void CSGBrush::clear() {
+ faces.clear();
+}
+
+void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces) {
+
+ clear();
+
+ int vc = p_vertices.size();
+
+ ERR_FAIL_COND((vc % 3) != 0)
+
+ PoolVector<Vector3>::Read rv = p_vertices.read();
+ int uvc = p_uvs.size();
+ PoolVector<Vector2>::Read ruv = p_uvs.read();
+ int sc = p_smooth.size();
+ PoolVector<bool>::Read rs = p_smooth.read();
+ int mc = p_materials.size();
+ PoolVector<Ref<Material> >::Read rm = p_materials.read();
+ int ic = p_invert_faces.size();
+ PoolVector<bool>::Read ri = p_invert_faces.read();
+
+ Map<Ref<Material>, int> material_map;
+
+ faces.resize(p_vertices.size() / 3);
+
+ for (int i = 0; i < faces.size(); i++) {
+ Face &f = faces[i];
+ f.vertices[0] = rv[i * 3 + 0];
+ f.vertices[1] = rv[i * 3 + 1];
+ f.vertices[2] = rv[i * 3 + 2];
+ if (uvc == vc) {
+ f.uvs[0] = ruv[i * 3 + 0];
+ f.uvs[1] = ruv[i * 3 + 1];
+ f.uvs[2] = ruv[i * 3 + 2];
+ }
+ if (sc == vc / 3) {
+ f.smooth = rs[i];
+ } else {
+ f.smooth = false;
+ }
+
+ if (ic == vc / 3) {
+ f.invert = ri[i];
+ } else {
+ f.invert = false;
+ }
+
+ if (mc == vc / 3) {
+ Ref<Material> mat = rm[i];
+ if (mat.is_valid()) {
+ const Map<Ref<Material>, int>::Element *E = material_map.find(mat);
+ if (E) {
+ f.material = E->get();
+ } else {
+ f.material = material_map.size();
+ material_map[mat] = f.material;
+ }
+ } else {
+ f.material = -1;
+ }
+ }
+ }
+
+ materials.resize(material_map.size());
+ for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) {
+ materials[E->get()] = E->key();
+ }
+
+ _regen_face_aabbs();
+}
+
+void CSGBrush::_regen_face_aabbs() {
+
+ for (int i = 0; i < faces.size(); i++) {
+
+ faces[i].aabb.position = faces[i].vertices[0];
+ faces[i].aabb.expand_to(faces[i].vertices[1]);
+ faces[i].aabb.expand_to(faces[i].vertices[2]);
+ faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros
+ }
+}
+
+void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
+
+ faces = p_brush.faces;
+ materials = p_brush.materials;
+
+ for (int i = 0; i < faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]);
+ }
+ }
+
+ _regen_face_aabbs();
+}
+
+////////////////////////
+
+void CSGBrushOperation::BuildPoly::create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //creates the initial face that will be used for clipping against the other faces
+
+ Vector3 va[3] = {
+ p_brush->faces[p_face].vertices[0],
+ p_brush->faces[p_face].vertices[1],
+ p_brush->faces[p_face].vertices[2],
+ };
+
+ plane = Plane(va[0], va[1], va[2]);
+
+ to_world.origin = va[0];
+
+ to_world.basis.set_axis(2, plane.normal);
+ to_world.basis.set_axis(0, (va[1] - va[2]).normalized());
+ to_world.basis.set_axis(1, to_world.basis.get_axis(0).cross(to_world.basis.get_axis(2)).normalized());
+
+ to_poly = to_world.affine_inverse();
+
+ face_index = p_face;
+
+ for (int i = 0; i < 3; i++) {
+
+ Point p;
+ Vector3 localp = to_poly.xform(va[i]);
+ p.point.x = localp.x;
+ p.point.y = localp.y;
+ p.uv = p_brush->faces[p_face].uvs[i];
+
+ points.push_back(p);
+
+ ///edge
+
+ Edge e;
+ e.points[0] = i;
+ e.points[1] = (i + 1) % 3;
+ e.outer = true;
+ edges.push_back(e);
+ }
+
+ smooth = p_brush->faces[p_face].smooth;
+ invert = p_brush->faces[p_face].invert;
+
+ if (p_brush->faces[p_face].material != -1) {
+ material = p_brush->materials[p_brush->faces[p_face].material];
+ }
+
+ base_edges = 3;
+}
+
+static Vector2 interpolate_uv(const Vector2 &p_vertex_a, const Vector2 &p_vertex_b, const Vector2 &p_vertex_c, const Vector2 &p_uv_a, const Vector2 &p_uv_c) {
+
+ float len_a_c = (p_vertex_c - p_vertex_a).length();
+ if (len_a_c < CMP_EPSILON) {
+ return p_uv_a;
+ }
+
+ float len_a_b = (p_vertex_b - p_vertex_a).length();
+
+ float c = len_a_b / len_a_c;
+
+ return p_uv_a.linear_interpolate(p_uv_c, c);
+}
+
+static Vector2 interpolate_triangle_uv(const Vector2 &p_pos, const Vector2 *p_vtx, const Vector2 *p_uv) {
+
+ if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) {
+ return p_uv[0];
+ }
+ if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) {
+ return p_uv[1];
+ }
+ if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) {
+ return p_uv[2];
+ }
+
+ Vector2 v0 = p_vtx[1] - p_vtx[0];
+ Vector2 v1 = p_vtx[2] - p_vtx[0];
+ Vector2 v2 = p_pos - p_vtx[0];
+
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom == 0) {
+ return p_uv[0];
+ }
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
+}
+
+void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //keep track of what was inserted
+ Vector<int> inserted_points;
+
+ //keep track of point indices for what was inserted, allowing reuse of points.
+ int segment_idx[2] = { -1, -1 };
+
+ //check if edge and poly share a vertex, of so, assign it to segment_idx
+ for (int i = 0; i < points.size(); i++) {
+ for (int j = 0; j < 2; j++) {
+ if (segment[j].distance_to(points[i].point) < CMP_EPSILON) {
+ segment_idx[j] = i;
+ inserted_points.push_back(i);
+ break;
+ }
+ }
+ }
+
+ //check if both segment points are shared with other vertices
+ if (segment_idx[0] != -1 && segment_idx[1] != -1) {
+
+ if (segment_idx[0] == segment_idx[1]) {
+ return; //segment was too tiny, both mapped to same point
+ }
+
+ bool found = false;
+
+ //check if the segment already exists
+ for (int i = 0; i < edges.size(); i++) {
+
+ if (
+ (edges[i].points[0] == segment_idx[0] && edges[i].points[1] == segment_idx[1]) ||
+ (edges[i].points[0] == segment_idx[1] && edges[i].points[1] == segment_idx[0])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ //it does already exist, do nothing
+ return;
+ }
+
+ //directly add the new segment
+ Edge new_edge;
+ new_edge.points[0] = segment_idx[0];
+ new_edge.points[1] = segment_idx[1];
+ edges.push_back(new_edge);
+ return;
+ }
+
+ //check edge by edge against the segment points to see if intersects
+
+ for (int i = 0; i < base_edges; i++) {
+
+ //if a point is shared with one of the edge points, then this edge must not be tested, as it will result in a numerical precision error.
+ bool edge_valid = true;
+ for (int j = 0; j < 2; j++) {
+
+ if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) {
+ edge_valid = false; //segment has this point, cant check against this
+ break;
+ }
+ }
+
+ if (!edge_valid) //already hit a point in this edge, so dont test it
+ continue;
+
+ //see if either points are within the edge isntead of crossing it
+ Vector2 res;
+ bool found = false;
+ int assign_segment_id = -1;
+
+ for (int j = 0; j < 2; j++) {
+
+ Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point };
+ Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg);
+
+ if (closest.distance_to(segment[j]) < CMP_EPSILON) {
+ //point rest of this edge
+ res = closest;
+ found = true;
+ assign_segment_id = j;
+ }
+ }
+
+ //test if the point crosses the edge
+ if (!found && Geometry::segment_intersects_segment_2d(segment[0], segment[1], points[edges[i].points[0]].point, points[edges[i].points[1]].point, &res)) {
+ //point does cross the edge
+ found = true;
+ }
+
+ //check whether an intersection against the segment happened
+ if (found) {
+
+ //It did! so first, must slice the segment
+ Point new_point;
+ new_point.point = res;
+ //make sure to interpolate UV too
+ new_point.uv = interpolate_uv(points[edges[i].points[0]].point, new_point.point, points[edges[i].points[1]].point, points[edges[i].points[0]].uv, points[edges[i].points[1]].uv);
+
+ int point_idx = points.size();
+ points.push_back(new_point);
+
+ //split the edge in 2
+ Edge new_edge;
+ new_edge.points[0] = edges[i].points[0];
+ new_edge.points[1] = point_idx;
+ new_edge.outer = edges[i].outer;
+ edges[i].points[0] = point_idx;
+ edges.insert(i, new_edge);
+ i++; //skip newly inserted edge
+ base_edges++; //will need an extra one in the base triangle
+ if (assign_segment_id >= 0) {
+ //point did split a segment, so make sure to remember this
+ segment_idx[assign_segment_id] = point_idx;
+ }
+ inserted_points.push_back(point_idx);
+ }
+ }
+
+ //final step: after cutting the original triangle, try to see if we can still insert
+ //this segment
+
+ //if already inserted two points, just use them for a segment
+
+ if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
+ //two points were inserted, create the new edge
+ Edge new_edge;
+ new_edge.points[0] = inserted_points[0];
+ new_edge.points[1] = inserted_points[1];
+ edges.push_back(new_edge);
+ return;
+ }
+
+ // One or no points were inserted (besides splitting), so try to see if extra points can be placed inside the triangle.
+ // This needs to be done here, after the previous tests were exhausted
+ for (int i = 0; i < 2; i++) {
+
+ if (segment_idx[i] != -1)
+ continue; //already assigned to something, so skip
+
+ //check whether one of the segment endpoints is inside the triangle. If it is, this points needs to be inserted
+ if (Geometry::is_point_in_triangle(segment[i], points[0].point, points[1].point, points[2].point)) {
+
+ Point new_point;
+ new_point.point = segment[i];
+
+ Vector2 point3[3] = { points[0].point, points[1].point, points[2].point };
+ Vector2 uv3[3] = { points[0].uv, points[1].uv, points[2].uv };
+
+ new_point.uv = interpolate_triangle_uv(new_point.point, point3, uv3);
+
+ int point_idx = points.size();
+ points.push_back(new_point);
+ inserted_points.push_back(point_idx);
+ }
+ }
+
+ //check again whether two points were inserted, if so then create the new edge
+ if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
+ Edge new_edge;
+ new_edge.points[0] = inserted_points[0];
+ new_edge.points[1] = inserted_points[1];
+ edges.push_back(new_edge);
+ }
+}
+
+void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //Clip function.. find triangle points that will be mapped to the plane and form a segment
+
+ Vector2 segment[3]; //2D
+
+ int src_points = 0;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 p = p_brush->faces[p_face].vertices[i];
+ if (plane.has_point(p)) {
+ Vector3 pp = plane.project(p);
+ pp = to_poly.xform(pp);
+ segment[src_points++] = Vector2(pp.x, pp.y);
+ } else {
+ Vector3 q = p_brush->faces[p_face].vertices[(i + 1) % 3];
+ if (plane.has_point(q))
+ continue; //next point is in plane, will be added eventually
+ if (plane.is_point_over(p) == plane.is_point_over(q))
+ continue; // both on same side of the plane, don't add
+
+ Vector3 res;
+ if (plane.intersects_segment(p, q, &res)) {
+ res = to_poly.xform(res);
+ segment[src_points++] = Vector2(res.x, res.y);
+ }
+ }
+ }
+
+ //all above or all below, nothing to do. Should not happen though since a precheck was done before.
+ if (src_points == 0)
+ return;
+
+ //just one point in plane is not worth doing anything
+ if (src_points == 1)
+ return;
+
+ //transform A points to 2D
+
+ if (segment[0].distance_to(segment[1]) < CMP_EPSILON)
+ return; //too small
+
+ _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B);
+}
+
+void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge) {
+
+ //construct a frame of reference for both transforms, in order to do intersection test
+ Vector3 va[3] = {
+ A->faces[p_face_a].vertices[0],
+ A->faces[p_face_a].vertices[1],
+ A->faces[p_face_a].vertices[2],
+ };
+ Vector3 vb[3] = {
+ B->faces[p_face_b].vertices[0],
+ B->faces[p_face_b].vertices[1],
+ B->faces[p_face_b].vertices[2],
+ };
+
+ {
+ //check if either is a degenerate
+ if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON)
+ return;
+
+ if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON)
+ return;
+ }
+
+ {
+ //check if points are the same
+ int equal_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ for (int j = 0; j < 3; j++) {
+ if (va[i].distance_to(vb[j]) < mesh_merge.vertex_snap) {
+ equal_count++;
+ break;
+ }
+ }
+ }
+
+ //if 2 or 3 points are the same, there is no point in doing anything. They can't
+ //be clipped either, so add both.
+ if (equal_count == 2 || equal_count == 3) {
+ return;
+ }
+ }
+
+ // do a quick pre-check for no-intersection using the SAT theorem
+
+ {
+
+ //b under or over a plane
+ int over_count = 0, in_plane_count = 0, under_count = 0;
+ Plane plane_a(va[0], va[1], va[2]);
+ if (plane_a.normal == Vector3()) {
+ return; //degenerate
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_a.has_point(vb[i]))
+ in_plane_count++;
+ else if (plane_a.is_point_over(vb[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+
+ if (over_count == 0 || under_count == 0)
+ return; //no intersection, something needs to be under AND over
+
+ //a under or over b plane
+ over_count = 0;
+ under_count = 0;
+ in_plane_count = 0;
+
+ Plane plane_b(vb[0], vb[1], vb[2]);
+ if (plane_b.normal == Vector3())
+ return; //degenerate
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_b.has_point(va[i]))
+ in_plane_count++;
+ else if (plane_b.is_point_over(va[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+
+ if (over_count == 0 || under_count == 0)
+ return; //no intersection, something needs to be under AND over
+
+ //edge pairs (cross product combinations), see SAT theorem
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 axis_a = (va[i] - va[(i + 1) % 3]).normalized();
+
+ for (int j = 0; j < 3; j++) {
+
+ Vector3 axis_b = (vb[j] - vb[(j + 1) % 3]).normalized();
+
+ Vector3 sep_axis = axis_a.cross(axis_b);
+ if (sep_axis == Vector3())
+ continue; //colineal
+ sep_axis.normalize();
+
+ real_t min_a = 1e20, max_a = -1e20;
+ real_t min_b = 1e20, max_b = -1e20;
+
+ for (int k = 0; k < 3; k++) {
+ real_t d = sep_axis.dot(va[k]);
+ min_a = MIN(min_a, d);
+ max_a = MAX(max_a, d);
+ d = sep_axis.dot(vb[k]);
+ min_b = MIN(min_b, d);
+ max_b = MAX(max_b, d);
+ }
+
+ min_b -= (max_a - min_a) * 0.5;
+ max_b += (max_a - min_a) * 0.5;
+
+ real_t dmin = min_b - (min_a + max_a) * 0.5;
+ real_t dmax = max_b - (min_a + max_a) * 0.5;
+
+ if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) {
+ return; //does not contain zero, so they don't overlap
+ }
+ }
+ }
+ }
+
+ //if we are still here, it means they most likely intersect, so create BuildPolys if they dont existy
+
+ BuildPoly *poly_a = NULL;
+
+ if (!build_polys_a.has(p_face_a)) {
+
+ BuildPoly bp;
+ bp.create(A, p_face_a, mesh_merge, false);
+ build_polys_a[p_face_a] = bp;
+ }
+
+ poly_a = &build_polys_a[p_face_a];
+
+ BuildPoly *poly_b = NULL;
+
+ if (!build_polys_b.has(p_face_b)) {
+
+ BuildPoly bp;
+ bp.create(B, p_face_b, mesh_merge, true);
+ build_polys_b[p_face_b] = bp;
+ }
+
+ poly_b = &build_polys_b[p_face_b];
+
+ //clip each other, this could be improved by using vertex unique IDs (more vertices may be shared instead of using snap)
+ poly_a->clip(B, p_face_b, mesh_merge, false);
+ poly_b->clip(A, p_face_a, mesh_merge, true);
+}
+
+void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly) {
+
+ //this function follows the polygon points counter clockwise and adds them. It creates lists of unique polygons
+ //every time an unused edge is found, it's pushed to a stack and continues from there.
+
+ List<EdgeSort> edge_stack;
+
+ {
+ EdgeSort es;
+ es.angle = 0; //wont be checked here
+ es.edge = p_edge;
+ es.prev_point = p_from_point;
+ es.edge_point = p_to_point;
+
+ edge_stack.push_back(es);
+ }
+
+ //attempt to empty the stack.
+ while (edge_stack.size()) {
+
+ EdgeSort e = edge_stack.front()->get();
+ edge_stack.pop_front();
+
+ if (edge_process[e.edge]) {
+ //nothing to do here
+ continue;
+ }
+
+ Vector<int> points;
+ points.push_back(e.prev_point);
+
+ int prev_point = e.prev_point;
+ int to_point = e.edge_point;
+ int current_edge = e.edge;
+
+ edge_process[e.edge] = true; //mark as processed
+
+ int limit = p_poly.points.size() * 4; //avoid infinite recursion
+
+ while (to_point != e.prev_point && limit) {
+
+ Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
+
+ //construct a basis transform from the segment, which will be used to check the angle
+ Transform2D t2d;
+ t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
+ t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
+ t2d[2] = segment[1]; //origin
+
+ if (t2d.basis_determinant() == 0)
+ break; //abort poly
+
+ t2d.affine_invert();
+
+ //push all edges found here, they will be sorted by minimum angle later.
+ Vector<EdgeSort> next_edges;
+
+ for (int i = 0; i < vertex_process[to_point].size(); i++) {
+
+ int edge = vertex_process[to_point][i];
+ int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
+ if (opposite_point == prev_point)
+ continue; //not going back
+
+ EdgeSort e;
+ Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point);
+ e.angle = -local_vec.angle(); //negate so we can sort by minimum angle
+ e.edge = edge;
+ e.edge_point = opposite_point;
+ e.prev_point = to_point;
+
+ next_edges.push_back(e);
+ }
+
+ //finally, sort by minimum angle
+ next_edges.sort();
+
+ int next_point = -1;
+ int next_edge = -1;
+
+ for (int i = 0; i < next_edges.size(); i++) {
+
+ if (i == 0) {
+ //minimum angle found is the next point
+ next_point = next_edges[i].edge_point;
+ next_edge = next_edges[i].edge;
+
+ } else {
+ //the rest are pushed to the stack IF they were not processed yet.
+ if (!edge_process[next_edges[i].edge]) {
+ edge_stack.push_back(next_edges[i]);
+ }
+ }
+ }
+
+ if (next_edge == -1) {
+ //did not find anything, may be a dead-end edge (this should normally not happen)
+ //just flip the direction and go back
+ next_point = prev_point;
+ next_edge = current_edge;
+ }
+
+ points.push_back(to_point);
+
+ prev_point = to_point;
+ to_point = next_point;
+ edge_process[next_edge] = true; //mark this edge as processed
+ current_edge = next_edge;
+
+ limit--;
+ }
+
+ //if more than 2 points were added to the polygon, add it to the list of polygons.
+ if (points.size() > 2) {
+ PolyPoints pp;
+ pp.points = points;
+ r_poly.push_back(pp);
+ }
+ }
+}
+
+void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline) {
+
+ //this is the opposite of the function above. It adds polygon outlines instead.
+ //this is used for triangulating holes.
+ //no stack is used here because only the bigger outline is interesting.
+
+ r_outline.push_back(p_from_point);
+
+ int prev_point = p_from_point;
+ int to_point = p_to_point;
+
+ int limit = p_poly.points.size() * 4; //avoid infinite recursion
+
+ while (to_point != p_from_point && limit) {
+
+ Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
+ //again create a transform to compute the angle.
+ Transform2D t2d;
+ t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
+ t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
+ t2d[2] = segment[1]; //origin
+
+ if (t2d.basis_determinant() == 0)
+ break; //abort poly
+
+ t2d.affine_invert();
+
+ float max_angle;
+ int next_point_angle = -1;
+
+ for (int i = 0; i < vertex_process[to_point].size(); i++) {
+
+ int edge = vertex_process[to_point][i];
+ int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
+ if (opposite_point == prev_point)
+ continue; //not going back
+
+ float angle = -t2d.xform(p_poly.points[opposite_point].point).angle();
+ if (next_point_angle == -1 || angle > max_angle) { //same as before but use greater to check.
+ max_angle = angle;
+ next_point_angle = opposite_point;
+ }
+ }
+
+ if (next_point_angle == -1) {
+ //go back because no route found
+ next_point_angle = prev_point;
+ }
+
+ r_outline.push_back(to_point);
+ prev_point = to_point;
+ to_point = next_point_angle;
+
+ limit--;
+ }
+}
+
+void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b) {
+
+ //finally, merge the 2D polygon back to 3D
+
+ Vector<Vector<int> > vertex_process;
+ Vector<bool> edge_process;
+
+ vertex_process.resize(p_poly.points.size());
+ edge_process.resize(p_poly.edges.size());
+
+ //none processed by default
+ for (int i = 0; i < edge_process.size(); i++) {
+ edge_process[i] = false;
+ }
+
+ //put edges in points, so points can go through them
+ for (int i = 0; i < p_poly.edges.size(); i++) {
+ vertex_process[p_poly.edges[i].points[0]].push_back(i);
+ vertex_process[p_poly.edges[i].points[1]].push_back(i);
+ }
+
+ Vector<PolyPoints> polys;
+
+ //process points that were not processed
+ for (int i = 0; i < edge_process.size(); i++) {
+ if (edge_process[i] == true)
+ continue; //already processed
+
+ int intersect_poly = -1;
+
+ if (i > 0) {
+ //this is disconnected, so it's clearly a hole. lets find where it belongs
+ Vector2 ref_point = p_poly.points[p_poly.edges[i].points[0]].point;
+
+ for (int j = 0; j < polys.size(); j++) {
+
+ //find a point outside poly
+ Vector2 out_point(-1e20, -1e20);
+
+ const PolyPoints &pp = polys[j];
+
+ for (int k = 0; k < pp.points.size(); k++) {
+ Vector2 p = p_poly.points[pp.points[k]].point;
+ out_point.x = MAX(out_point.x, p.x);
+ out_point.y = MAX(out_point.y, p.y);
+ }
+
+ out_point += Vector2(0.12341234, 0.4123412); // move to a random place to avoid direct edge-point chances
+
+ int intersections = 0;
+
+ for (int k = 0; k < pp.points.size(); k++) {
+ Vector2 p1 = p_poly.points[pp.points[k]].point;
+ Vector2 p2 = p_poly.points[pp.points[(k + 1) % pp.points.size()]].point;
+
+ if (Geometry::segment_intersects_segment_2d(ref_point, out_point, p1, p2, NULL)) {
+ intersections++;
+ }
+ }
+
+ if (intersections % 2 == 1) {
+ //hole is inside this poly
+ intersect_poly = j;
+ break;
+ }
+ }
+ }
+
+ if (intersect_poly != -1) {
+ //must add this as a hole
+ Vector<int> outline;
+ _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline);
+
+ if (outline.size() > 1) {
+ polys[intersect_poly].holes.push_back(outline);
+ }
+ }
+ _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys);
+ }
+
+ //get rid of holes, not the most optiomal way, but also not a common case at all to be inoptimal
+ for (int i = 0; i < polys.size(); i++) {
+
+ if (!polys[i].holes.size())
+ continue;
+
+ //repeat until no more holes are left to be merged
+ while (polys[i].holes.size()) {
+
+ //try to merge a hole with the outline
+ bool added_hole = false;
+
+ for (int j = 0; j < polys[i].holes.size(); j++) {
+
+ //try hole vertices
+ int with_outline_vertex = -1;
+ int from_hole_vertex = -1;
+
+ bool found = false;
+
+ for (int k = 0; k < polys[i].holes[j].size(); k++) {
+
+ int from_idx = polys[i].holes[j][k];
+ Vector2 from = p_poly.points[from_idx].point;
+
+ //try a segment from hole vertex to outline vertices
+ from_hole_vertex = k;
+
+ bool valid = true;
+
+ for (int l = 0; l < polys[i].points.size(); l++) {
+
+ int to_idx = polys[i].points[l];
+ Vector2 to = p_poly.points[to_idx].point;
+ with_outline_vertex = l;
+
+ //try agaisnt outline (other points) first
+
+ valid = true;
+
+ for (int m = 0; m < polys[i].points.size(); m++) {
+
+ int m_next = (m + 1) % polys[i].points.size();
+ if (m == with_outline_vertex || m_next == with_outline_vertex) //do not test with edges that share this point
+ continue;
+
+ if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].points[m]].point, p_poly.points[polys[i].points[m_next]].point, NULL)) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (!valid)
+ continue;
+
+ //try agaisnt all holes including self
+
+ for (int m = 0; m < polys[i].holes.size(); m++) {
+
+ for (int n = 0; n < polys[i].holes[m].size(); n++) {
+
+ int n_next = (n + 1) % polys[i].holes[m].size();
+ if (m == j && (n == from_hole_vertex || n_next == from_hole_vertex)) //contains vertex being tested from current hole, skip
+ continue;
+
+ if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].holes[m][n]].point, p_poly.points[polys[i].holes[m][n_next]].point, NULL)) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (!valid)
+ break;
+ }
+
+ if (valid) //all passed! exit loop
+ break;
+ else
+ continue; //something went wrong, go on.
+ }
+
+ if (valid) {
+ found = true; //if in the end this was valid, use it
+ break;
+ }
+ }
+
+ if (found) {
+
+ //hook this hole with outline, and remove from list of holes
+
+ //duplicate point
+ int insert_at = with_outline_vertex;
+ polys[i].points.insert(insert_at, polys[i].points[insert_at]);
+ insert_at++;
+ //insert all others, outline should be backwards (must check)
+ int holesize = polys[i].holes[j].size();
+ for (int k = 0; k <= holesize; k++) {
+ int idx = (from_hole_vertex + k) % holesize;
+ polys[i].points.insert(insert_at, polys[i].holes[j][idx]);
+ insert_at++;
+ }
+
+ added_hole = true;
+ polys[i].holes.remove(j);
+ break; //got rid of hole, break and continue
+ }
+ }
+
+ ERR_BREAK(!added_hole);
+ }
+ }
+
+ //triangulate polygons
+
+ for (int i = 0; i < polys.size(); i++) {
+
+ Vector<Vector2> vertices;
+ vertices.resize(polys[i].points.size());
+ for (int j = 0; j < vertices.size(); j++) {
+ vertices[j] = p_poly.points[polys[i].points[j]].point;
+ }
+
+ Vector<int> indices = Geometry::triangulate_polygon(vertices);
+
+ for (int j = 0; j < indices.size(); j += 3) {
+
+ //obtain the vertex
+
+ Vector3 face[3];
+ Vector2 uv[3];
+ float cp = Geometry::vec2_cross(p_poly.points[polys[i].points[indices[j + 0]]].point, p_poly.points[polys[i].points[indices[j + 1]]].point, p_poly.points[polys[i].points[indices[j + 2]]].point);
+ if (Math::abs(cp) < CMP_EPSILON)
+ continue;
+
+ for (int k = 0; k < 3; k++) {
+
+ Vector2 p = p_poly.points[polys[i].points[indices[j + k]]].point;
+ face[k] = p_poly.to_world.xform(Vector3(p.x, p.y, 0));
+ uv[k] = p_poly.points[polys[i].points[indices[j + k]]].uv;
+ }
+
+ mesh.add_face(face[0], face[1], face[2], uv[0], uv[1], uv[2], p_poly.smooth, p_poly.invert, p_poly.material, p_from_b);
+ }
+ }
+}
+
+//use a limit to speed up bvh and limit the depth
+#define BVH_LIMIT 8
+
+int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) {
+
+ if (p_depth > max_depth) {
+ max_depth = p_depth;
+ }
+
+ if (p_size <= BVH_LIMIT) {
+
+ for (int i = 0; i < p_size - 1; i++) {
+ p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh;
+ }
+ return p_bb[p_from] - p_bvh;
+ } else if (p_size == 0) {
+
+ return -1;
+ }
+
+ AABB aabb;
+ aabb = p_bb[p_from]->aabb;
+ for (int i = 1; i < p_size; i++) {
+
+ aabb.merge_with(p_bb[p_from + i]->aabb);
+ }
+
+ int li = aabb.get_longest_axis_index();
+
+ switch (li) {
+
+ case Vector3::AXIS_X: {
+ SortArray<BVH *, BVHCmpX> sort_x;
+ sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_x.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Y: {
+ SortArray<BVH *, BVHCmpY> sort_y;
+ sort_y.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_y.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Z: {
+ SortArray<BVH *, BVHCmpZ> sort_z;
+ sort_z.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_z.sort(&p_bb[p_from],p_size);
+
+ } break;
+ }
+
+ int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc);
+ int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc);
+
+ int index = max_alloc++;
+ BVH *_new = &p_bvh[index];
+ _new->aabb = aabb;
+ _new->center = aabb.position + aabb.size * 0.5;
+ _new->face = -1;
+ _new->left = left;
+ _new->right = right;
+ _new->next = -1;
+
+ return index;
+}
+
+int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const {
+
+ uint32_t *stack = (uint32_t *)alloca(sizeof(int) * p_max_depth);
+
+ enum {
+ TEST_AABB_BIT = 0,
+ VISIT_LEFT_BIT = 1,
+ VISIT_RIGHT_BIT = 2,
+ VISIT_DONE_BIT = 3,
+ VISITED_BIT_SHIFT = 29,
+ NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
+ VISITED_BIT_MASK = ~NODE_IDX_MASK,
+
+ };
+
+ int intersections = 0;
+
+ int level = 0;
+
+ const Vector3 *vertexptr = points.ptr();
+ const Face *facesptr = faces.ptr();
+ AABB segment_aabb;
+ segment_aabb.position = p_begin;
+ segment_aabb.expand_to(p_end);
+
+ int pos = p_bvh_first;
+
+ stack[0] = pos;
+ while (true) {
+
+ uint32_t node = stack[level] & NODE_IDX_MASK;
+ const BVH &b = bvhptr[node];
+ bool done = false;
+
+ switch (stack[level] >> VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+ if (b.face >= 0) {
+
+ const BVH *bp = &b;
+
+ while (bp) {
+
+ bool valid = segment_aabb.intersects(bp->aabb) && bp->aabb.intersects_segment(p_begin, p_end);
+
+ if (valid && p_exclude != bp->face) {
+ const Face &s = facesptr[bp->face];
+ Face3 f3(vertexptr[s.points[0]], vertexptr[s.points[1]], vertexptr[s.points[2]]);
+
+ Vector3 res;
+
+ if (f3.intersects_segment(p_begin, p_end, &res)) {
+ intersections++;
+ }
+ }
+ if (bp->next != -1) {
+ bp = &bvhptr[bp->next];
+ } else {
+ bp = NULL;
+ }
+ }
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ bool valid = segment_aabb.intersects(b.aabb) && b.aabb.intersects_segment(p_begin, p_end);
+
+ if (!valid) {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+ stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
+ }
+ }
+ continue;
+ }
+ case VISIT_LEFT_BIT: {
+
+ stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.left | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_RIGHT_BIT: {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.right | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_DONE_BIT: {
+
+ if (level == 0) {
+ done = true;
+ break;
+ } else
+ level--;
+ continue;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return intersections;
+}
+
+void CSGBrushOperation::MeshMerge::mark_inside_faces() {
+
+ // mark faces that are inside. This helps later do the boolean ops when merging.
+ // this approach is very brute force (with a bunch of optimizatios, such as BVH and pre AABB intersection test)
+
+ AABB aabb;
+
+ for (int i = 0; i < points.size(); i++) {
+ if (i == 0) {
+ aabb.position = points[i];
+ } else {
+ aabb.expand_to(points[i]);
+ }
+ }
+
+ float max_distance = aabb.size.length() * 1.2;
+
+ Vector<BVH> bvhvec;
+ bvhvec.resize(faces.size() * 3); //will never be larger than this (todo make better)
+ BVH *bvh = bvhvec.ptrw();
+
+ AABB faces_a;
+ AABB faces_b;
+
+ bool first_a = true;
+ bool first_b = true;
+
+ for (int i = 0; i < faces.size(); i++) {
+ bvh[i].left = -1;
+ bvh[i].right = -1;
+ bvh[i].face = i;
+ bvh[i].aabb.position = points[faces[i].points[0]];
+ bvh[i].aabb.expand_to(points[faces[i].points[1]]);
+ bvh[i].aabb.expand_to(points[faces[i].points[2]]);
+ bvh[i].center = bvh[i].aabb.position + bvh[i].aabb.size * 0.5;
+ bvh[i].next = -1;
+ if (faces[i].from_b) {
+ if (first_b) {
+ faces_b = bvh[i].aabb;
+ first_b = false;
+ } else {
+ faces_b.merge_with(bvh[i].aabb);
+ }
+ } else {
+ if (first_a) {
+ faces_a = bvh[i].aabb;
+ first_a = false;
+ } else {
+ faces_a.merge_with(bvh[i].aabb);
+ }
+ }
+ }
+
+ AABB intersection_aabb = faces_a.intersection(faces_b);
+ intersection_aabb.grow_by(intersection_aabb.get_longest_axis_size() * 0.01); //grow a little, avoid numerical error
+
+ if (intersection_aabb.size == Vector3()) //AABB do not intersect, so neither do shapes.
+ return;
+
+ Vector<BVH *> bvhtrvec;
+ bvhtrvec.resize(faces.size());
+ BVH **bvhptr = bvhtrvec.ptrw();
+ for (int i = 0; i < faces.size(); i++) {
+
+ bvhptr[i] = &bvh[i];
+ }
+
+ int max_depth = 0;
+ int max_alloc = faces.size();
+ _create_bvh(bvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc);
+
+ for (int i = 0; i < faces.size(); i++) {
+
+ if (!intersection_aabb.intersects(bvh[i].aabb))
+ continue; //not in AABB intersection, so not in face intersection
+ Vector3 center = points[faces[i].points[0]];
+ center += points[faces[i].points[1]];
+ center += points[faces[i].points[2]];
+ center /= 3.0;
+
+ Plane plane(points[faces[i].points[0]], points[faces[i].points[1]], points[faces[i].points[2]]);
+ Vector3 target = center + plane.normal * max_distance + Vector3(0.0001234, 0.000512, 0.00013423); //reduce chance of edge hits by doing a small increment
+
+ int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i);
+
+ if (intersections & 1) {
+ faces[i].inside = true;
+ }
+ }
+}
+
+void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
+
+ Vector3 src_points[3] = { p_a, p_b, p_c };
+ Vector2 src_uvs[3] = { p_uv_a, p_uv_b, p_uv_c };
+ int indices[3];
+ for (int i = 0; i < 3; i++) {
+
+ VertexKey vk;
+ vk.x = int((double(src_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.y = int((double(src_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.z = int((double(src_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+
+ int res;
+ if (snap_cache.lookup(vk, res)) {
+ indices[i] = res;
+ } else {
+ indices[i] = points.size();
+ points.push_back(src_points[i]);
+ snap_cache.set(vk, indices[i]);
+ }
+ }
+
+ if (indices[0] == indices[2] || indices[0] == indices[1] || indices[1] == indices[2])
+ return; //not adding degenerate
+
+ MeshMerge::Face face;
+ face.from_b = p_from_b;
+ face.inside = false;
+ face.smooth = p_smooth;
+ face.invert = p_invert;
+ if (p_material.is_valid()) {
+ if (!materials.has(p_material)) {
+ face.material_idx = materials.size();
+ materials[p_material] = face.material_idx;
+ } else {
+ face.material_idx = materials[p_material];
+ }
+ } else {
+ face.material_idx = -1;
+ }
+
+ for (int k = 0; k < 3; k++) {
+
+ face.points[k] = indices[k];
+ face.uvs[k] = src_uvs[k];
+ ;
+ }
+
+ faces.push_back(face);
+}
+
+void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap) {
+
+ CallbackData cd;
+ cd.self = this;
+ cd.A = &p_A;
+ cd.B = &p_B;
+
+ MeshMerge mesh_merge;
+ mesh_merge.vertex_snap = p_snap;
+
+ //check intersections between faces. Use AABB to speed up precheck
+ //this generates list of buildpolys and clips them.
+ //this was originally BVH optimized, but its not really worth it.
+ for (int i = 0; i < p_A.faces.size(); i++) {
+ cd.face_a = i;
+ for (int j = 0; j < p_B.faces.size(); j++) {
+ if (p_A.faces[i].aabb.intersects(p_B.faces[j].aabb)) {
+ _collision_callback(&p_A, i, cd.build_polys_A, &p_B, j, cd.build_polys_B, mesh_merge);
+ }
+ }
+ }
+
+ //merge the already cliped polys back to 3D
+ for (Map<int, BuildPoly>::Element *E = cd.build_polys_A.front(); E; E = E->next()) {
+ _merge_poly(mesh_merge, E->key(), E->get(), false);
+ }
+
+ for (Map<int, BuildPoly>::Element *E = cd.build_polys_B.front(); E; E = E->next()) {
+ _merge_poly(mesh_merge, E->key(), E->get(), true);
+ }
+
+ //merge the non clipped faces back
+
+ for (int i = 0; i < p_A.faces.size(); i++) {
+
+ if (cd.build_polys_A.has(i))
+ continue; //made from buildpoly, skipping
+
+ Vector3 points[3];
+ Vector2 uvs[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = p_A.faces[i].vertices[j];
+ uvs[j] = p_A.faces[i].uvs[j];
+ }
+ Ref<Material> material;
+ if (p_A.faces[i].material != -1) {
+ material = p_A.materials[p_A.faces[i].material];
+ }
+ mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_A.faces[i].smooth, p_A.faces[i].invert, material, false);
+ }
+
+ for (int i = 0; i < p_B.faces.size(); i++) {
+
+ if (cd.build_polys_B.has(i))
+ continue; //made from buildpoly, skipping
+
+ Vector3 points[3];
+ Vector2 uvs[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = p_B.faces[i].vertices[j];
+ uvs[j] = p_B.faces[i].uvs[j];
+ }
+ Ref<Material> material;
+ if (p_B.faces[i].material != -1) {
+ material = p_B.materials[p_B.faces[i].material];
+ }
+ mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_B.faces[i].smooth, p_B.faces[i].invert, material, true);
+ }
+
+ //mark faces that ended up inside the intersection
+ mesh_merge.mark_inside_faces();
+
+ //regen new brush to start filling it again
+ result.clear();
+
+ switch (p_operation) {
+
+ case OPERATION_UNION: {
+
+ int outside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].inside)
+ continue;
+
+ outside_count++;
+ }
+
+ result.faces.resize(outside_count);
+
+ outside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].inside)
+ continue;
+ for (int j = 0; j < 3; j++) {
+ result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ result.faces[outside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[outside_count].invert = mesh_merge.faces[i].invert;
+ result.faces[outside_count].material = mesh_merge.faces[i].material_idx;
+ outside_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ case OPERATION_INTERSECTION: {
+
+ int inside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (!mesh_merge.faces[i].inside)
+ continue;
+
+ inside_count++;
+ }
+
+ result.faces.resize(inside_count);
+
+ inside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (!mesh_merge.faces[i].inside)
+ continue;
+ for (int j = 0; j < 3; j++) {
+ result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ result.faces[inside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[inside_count].invert = mesh_merge.faces[i].invert;
+ result.faces[inside_count].material = mesh_merge.faces[i].material_idx;
+ inside_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ case OPERATION_SUBSTRACTION: {
+
+ int face_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
+
+ face_count++;
+ }
+
+ result.faces.resize(face_count);
+
+ face_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
+
+ for (int j = 0; j < 3; j++) {
+ result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ if (mesh_merge.faces[i].from_b) {
+ //invert facing of insides of B
+ SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]);
+ SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]);
+ }
+
+ result.faces[face_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[face_count].invert = mesh_merge.faces[i].invert;
+ result.faces[face_count].material = mesh_merge.faces[i].material_idx;
+ face_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ }
+
+ //updatelist of materials
+ result.materials.resize(mesh_merge.materials.size());
+ for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
+ result.materials[E->get()] = E->key();
+ }
+}
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
new file mode 100644
index 0000000000..53303a6533
--- /dev/null
+++ b/modules/csg/csg.h
@@ -0,0 +1,236 @@
+/*************************************************************************/
+/* csg.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 CSG_H
+#define CSG_H
+
+#include "aabb.h"
+#include "dvector.h"
+#include "map.h"
+#include "math_2d.h"
+#include "oa_hash_map.h"
+#include "plane.h"
+#include "scene/resources/material.h"
+#include "transform.h"
+#include "vector3.h"
+
+struct CSGBrush {
+
+ struct Face {
+
+ Vector3 vertices[3];
+ Vector2 uvs[3];
+ AABB aabb;
+ bool smooth;
+ bool invert;
+ int material;
+ };
+
+ Vector<Face> faces;
+ Vector<Ref<Material> > materials;
+
+ void _regen_face_aabbs();
+ //create a brush from faces
+ void build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces);
+ void copy_from(const CSGBrush &p_brush, const Transform &p_xform);
+
+ void clear();
+};
+
+struct CSGBrushOperation {
+
+ enum Operation {
+ OPERATION_UNION,
+ OPERATION_INTERSECTION,
+ OPERATION_SUBSTRACTION,
+
+ };
+
+ struct MeshMerge {
+
+ struct BVH {
+ int face;
+ int left;
+ int right;
+ int next;
+ Vector3 center;
+ AABB aabb;
+ };
+
+ struct BVHCmpX {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.x < p_right->center.x;
+ }
+ };
+
+ struct BVHCmpY {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.y < p_right->center.y;
+ }
+ };
+ struct BVHCmpZ {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.z < p_right->center.z;
+ }
+ };
+
+ int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const;
+ int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc);
+
+ struct VertexKey {
+ int32_t x, y, z;
+ _FORCE_INLINE_ bool operator<(const VertexKey &p_key) const {
+ if (x == p_key.x) {
+ if (y == p_key.y) {
+ return z < p_key.z;
+ } else {
+ return y < p_key.y;
+ }
+ } else {
+ return x < p_key.x;
+ }
+ }
+
+ _FORCE_INLINE_ bool operator==(const VertexKey &p_key) const {
+ return (x == p_key.x && y == p_key.y && z == p_key.z);
+ }
+ };
+
+ struct VertexKeyHash {
+ static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) {
+ uint32_t h = hash_djb2_one_32(p_vk.x);
+ h = hash_djb2_one_32(p_vk.y, h);
+ h = hash_djb2_one_32(p_vk.z, h);
+ return h;
+ }
+ };
+
+ OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
+
+ Vector<Vector3> points;
+
+ struct Face {
+ bool from_b;
+ bool inside;
+ int points[3];
+ Vector2 uvs[3];
+ bool smooth;
+ bool invert;
+ int material_idx;
+ };
+
+ Vector<Face> faces;
+
+ Map<Ref<Material>, int> materials;
+
+ Map<Vector3, int> vertex_map;
+ void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
+ // void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b);
+
+ float vertex_snap;
+ void mark_inside_faces();
+ };
+
+ struct BuildPoly {
+
+ Plane plane;
+ Transform to_poly;
+ Transform to_world;
+ int face_index;
+
+ struct Point {
+ Vector2 point;
+ Vector2 uv;
+ };
+
+ Vector<Point> points;
+
+ struct Edge {
+ bool outer;
+ int points[2];
+ Edge() {
+ outer = false;
+ }
+ };
+
+ Vector<Edge> edges;
+ Ref<Material> material;
+ bool smooth;
+ bool invert;
+
+ int base_edges; //edges from original triangle, even if split
+
+ void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B);
+
+ void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
+ void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
+ };
+
+ struct PolyPoints {
+
+ Vector<int> points;
+
+ Vector<Vector<int> > holes;
+ };
+
+ struct EdgeSort {
+ int edge;
+ int prev_point;
+ int edge_point;
+ float angle;
+ bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; }
+ };
+
+ struct CallbackData {
+ const CSGBrush *A;
+ const CSGBrush *B;
+ int face_a;
+ CSGBrushOperation *self;
+ Map<int, BuildPoly> build_polys_A;
+ Map<int, BuildPoly> build_polys_B;
+ };
+
+ void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly);
+ void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline);
+ void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b);
+
+ void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge);
+
+ static void _collision_callbacks(void *ud, int p_face_b);
+ void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001);
+};
+
+#endif // CSG_H
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
new file mode 100644
index 0000000000..2150320c4a
--- /dev/null
+++ b/modules/csg/csg_gizmos.cpp
@@ -0,0 +1,345 @@
+/*************************************************************************/
+/* csg_gizmos.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 "csg_gizmos.h"
+
+///////////
+
+String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ return "Radius";
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ static const char *hname[3] = { "Width", "Height", "Depth" };
+ return hname[p_idx];
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ return p_idx == 0 ? "Radius" : "Height";
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ return p_idx == 0 ? "InnerRadius" : "OuterRadius";
+ }
+
+ return "";
+}
+Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+ return s->get_radius();
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+ switch (p_idx) {
+ case 0: return s->get_width();
+ case 1: return s->get_height();
+ case 2: return s->get_depth();
+ }
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+ return p_idx == 0 ? s->get_radius() : s->get_height();
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+ return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius();
+ }
+
+ return Variant();
+}
+void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+
+ Transform gt = cs->get_global_transform();
+ gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ float d = ra.x;
+ if (d < 0.001)
+ d = 0.001;
+
+ s->set_radius(d);
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+
+ Vector3 axis;
+ axis[p_idx] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_idx];
+ if (d < 0.001)
+ d = 0.001;
+
+ switch (p_idx) {
+ case 0: s->set_width(d); break;
+ case 1: s->set_height(d); break;
+ case 2: s->set_depth(d); break;
+ }
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+
+ Vector3 axis;
+ axis[p_idx == 0 ? 0 : 1] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ s->set_radius(d);
+ else if (p_idx == 1)
+ s->set_height(d * 2.0);
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+
+ Vector3 axis;
+ axis[0] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ s->set_inner_radius(d);
+ else if (p_idx == 1)
+ s->set_outer_radius(d);
+ }
+}
+void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+ if (p_cancel) {
+ s->set_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Sphere Shape Radius"));
+ ur->add_do_method(s, "set_radius", s->get_radius());
+ ur->add_undo_method(s, "set_radius", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+ if (p_cancel) {
+ switch (p_idx) {
+ case 0: s->set_width(p_restore); break;
+ case 1: s->set_height(p_restore); break;
+ case 2: s->set_depth(p_restore); break;
+ }
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Box Shape Extents"));
+ static const char *method[3] = { "set_width", "set_height", "set_depth" };
+ float current;
+ switch (p_idx) {
+ case 0: current = s->get_width(); break;
+ case 1: current = s->get_height(); break;
+ case 2: current = s->get_depth(); break;
+ }
+
+ ur->add_do_method(s, method[p_idx], current);
+ ur->add_undo_method(s, method[p_idx], p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+ if (p_cancel) {
+ if (p_idx == 0)
+ s->set_radius(p_restore);
+ else
+ s->set_height(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 0) {
+ ur->create_action(TTR("Change Cylinder Radius"));
+ ur->add_do_method(s, "set_radius", s->get_radius());
+ ur->add_undo_method(s, "set_radius", p_restore);
+ } else {
+ ur->create_action(TTR("Change Cylinder Height"));
+ ur->add_do_method(s, "set_height", s->get_height());
+ ur->add_undo_method(s, "set_height", p_restore);
+ }
+
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+ if (p_cancel) {
+ if (p_idx == 0)
+ s->set_inner_radius(p_restore);
+ else
+ s->set_outer_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 0) {
+ ur->create_action(TTR("Change Torus Inner Radius"));
+ ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
+ ur->add_undo_method(s, "set_inner_radius", p_restore);
+ } else {
+ ur->create_action(TTR("Change Torus Outer Radius"));
+ ur->add_do_method(s, "set_outer_radius", s->get_outer_radius());
+ ur->add_undo_method(s, "set_outer_radius", p_restore);
+ }
+
+ ur->commit_action();
+ }
+}
+void CSGShapeSpatialGizmo::redraw() {
+
+ clear();
+
+ Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
+ Ref<Material> material = create_material("shape_material", gizmo_color);
+
+ PoolVector<Vector3> faces = cs->get_brush_faces();
+
+ Vector<Vector3> lines;
+ lines.resize(faces.size() * 2);
+ {
+ PoolVector<Vector3>::Read r = faces.read();
+
+ for (int i = 0; i < lines.size(); i += 6) {
+ int f = i / 6;
+ for (int j = 0; j < 3; j++) {
+ int j_n = (j + 1) % 3;
+ lines[i + j * 2 + 0] = r[f * 3 + j];
+ lines[i + j * 2 + 1] = r[f * 3 + j_n];
+ }
+ }
+ }
+
+ add_lines(lines, material);
+ add_collision_segments(lines);
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+
+ float r = s->get_radius();
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r, 0, 0));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_width(), 0, 0));
+ handles.push_back(Vector3(0, s->get_height(), 0));
+ handles.push_back(Vector3(0, 0, s->get_depth()));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_radius(), 0, 0));
+ handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_inner_radius(), 0, 0));
+ handles.push_back(Vector3(s->get_outer_radius(), 0, 0));
+ add_handles(handles);
+ }
+}
+CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) {
+
+ cs = p_cs;
+ set_spatial_node(p_cs);
+}
+
+Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) {
+ if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) {
+ Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial)));
+ return csg;
+ }
+
+ return Ref<SpatialEditorGizmo>();
+}
+
+EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) {
+
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1));
+}
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
new file mode 100644
index 0000000000..68e916823b
--- /dev/null
+++ b/modules/csg/csg_gizmos.h
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* csg_gizmos.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 CSG_GIZMOS_H
+#define CSG_GIZMOS_H
+
+#include "csg_shape.h"
+#include "editor/editor_plugin.h"
+#include "editor/spatial_editor_gizmos.h"
+
+class CSGShapeSpatialGizmo : public EditorSpatialGizmo {
+
+ GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo);
+
+ CSGShape *cs;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ void redraw();
+ CSGShapeSpatialGizmo(CSGShape *p_cs = NULL);
+};
+
+class EditorPluginCSG : public EditorPlugin {
+ GDCLASS(EditorPluginCSG, EditorPlugin)
+public:
+ virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
+ EditorPluginCSG(EditorNode *p_editor);
+};
+
+#endif // CSG_GIZMOS_H
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
new file mode 100644
index 0000000000..5f13474d2c
--- /dev/null
+++ b/modules/csg/csg_shape.cpp
@@ -0,0 +1,2164 @@
+/*************************************************************************/
+/* csg_shape.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 "csg_shape.h"
+#include "scene/3d/path.h"
+
+void CSGShape::set_use_collision(bool p_enable) {
+
+ if (use_collision == p_enable)
+ return;
+
+ use_collision = p_enable;
+
+ if (!is_inside_tree() || !is_root_shape())
+ return;
+
+ if (use_collision) {
+ root_collision_shape.instance();
+ root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform());
+ PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
+ PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
+ _make_dirty(); //force update
+ } else {
+ PhysicsServer::get_singleton()->free(root_collision_instance);
+ root_collision_instance = RID();
+ root_collision_shape.unref();
+ }
+}
+
+bool CSGShape::is_using_collision() const {
+ return use_collision;
+}
+
+bool CSGShape::is_root_shape() const {
+
+ return !parent;
+}
+
+void CSGShape::set_snap(float p_snap) {
+ snap = p_snap;
+}
+
+float CSGShape::get_snap() const {
+ return snap;
+}
+
+void CSGShape::_make_dirty() {
+
+ if (!is_inside_tree())
+ return;
+
+ if (dirty) {
+ return;
+ }
+
+ dirty = true;
+
+ if (parent) {
+ parent->_make_dirty();
+ } else {
+ //only parent will do
+ call_deferred("_update_shape");
+ }
+}
+
+CSGBrush *CSGShape::_get_brush() {
+
+ if (dirty) {
+ if (brush) {
+ memdelete(brush);
+ }
+ brush = NULL;
+
+ CSGBrush *n = _build_brush();
+
+ for (int i = 0; i < get_child_count(); i++) {
+
+ CSGShape *child = Object::cast_to<CSGShape>(get_child(i));
+ if (!child)
+ continue;
+ if (!child->is_visible_in_tree())
+ continue;
+
+ CSGBrush *n2 = child->_get_brush();
+ if (!n2)
+ continue;
+ if (!n) {
+ n = memnew(CSGBrush);
+
+ n->copy_from(*n2, child->get_transform());
+
+ } else {
+
+ CSGBrush *nn = memnew(CSGBrush);
+ CSGBrush *nn2 = memnew(CSGBrush);
+ nn2->copy_from(*n2, child->get_transform());
+
+ CSGBrushOperation bop;
+
+ switch (child->get_operation()) {
+ case CSGShape::OPERATION_UNION: bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap); break;
+ case CSGShape::OPERATION_INTERSECTION: bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); break;
+ case CSGShape::OPERATION_SUBTRACTION: bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap); break;
+ }
+ memdelete(n);
+ memdelete(nn2);
+ n = nn;
+ }
+ }
+
+ if (n) {
+ AABB aabb;
+ for (int i = 0; i < n->faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ if (i == 0 && j == 0)
+ aabb.position = n->faces[i].vertices[j];
+ else
+ aabb.expand_to(n->faces[i].vertices[j]);
+ }
+ }
+ node_aabb = aabb;
+ } else {
+ node_aabb = AABB();
+ }
+
+ brush = n;
+
+ dirty = false;
+ }
+
+ return 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
+
+ CSGBrush *n = _get_brush();
+ ERR_FAIL_COND(!n);
+
+ OAHashMap<Vector3, Vector3> vec_map;
+
+ Vector<int> face_count;
+ face_count.resize(n->materials.size() + 1);
+ for (int i = 0; i < face_count.size(); i++) {
+ face_count[i] = 0;
+ }
+
+ for (int i = 0; i < n->faces.size(); i++) {
+ int mat = n->faces[i].material;
+ ERR_CONTINUE(mat < -1 || mat >= face_count.size());
+ int idx = mat == -1 ? face_count.size() - 1 : mat;
+ if (n->faces[i].smooth) {
+
+ Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = n->faces[i].vertices[j];
+ Vector3 add;
+ if (vec_map.lookup(v, add)) {
+ add += p.normal;
+ } else {
+ add = p.normal;
+ }
+ vec_map.set(v, add);
+ }
+ }
+
+ face_count[idx]++;
+ }
+
+ Vector<ShapeUpdateSurface> surfaces;
+
+ surfaces.resize(face_count.size());
+
+ //create arrays
+ for (int i = 0; i < surfaces.size(); i++) {
+
+ surfaces[i].vertices.resize(face_count[i] * 3);
+ surfaces[i].normals.resize(face_count[i] * 3);
+ surfaces[i].uvs.resize(face_count[i] * 3);
+ surfaces[i].last_added = 0;
+
+ if (i != surfaces.size() - 1) {
+ surfaces[i].material = n->materials[i];
+ }
+
+ surfaces[i].verticesw = surfaces[i].vertices.write();
+ surfaces[i].normalsw = surfaces[i].normals.write();
+ surfaces[i].uvsw = surfaces[i].uvs.write();
+ }
+
+ //fill arrays
+ PoolVector<Vector3> physics_faces;
+ bool fill_physics_faces = false;
+ if (root_collision_shape.is_valid()) {
+ physics_faces.resize(n->faces.size() * 3);
+ fill_physics_faces = true;
+ }
+
+ {
+ PoolVector<Vector3>::Write physicsw;
+
+ if (fill_physics_faces) {
+ physicsw = physics_faces.write();
+ }
+
+ for (int i = 0; i < n->faces.size(); i++) {
+
+ int order[3] = { 0, 1, 2 };
+
+ if (n->faces[i].invert) {
+ SWAP(order[1], order[2]);
+ }
+
+ if (fill_physics_faces) {
+ physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]];
+ physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]];
+ physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]];
+ }
+
+ int mat = n->faces[i].material;
+ ERR_CONTINUE(mat < -1 || mat >= face_count.size());
+ int idx = mat == -1 ? face_count.size() - 1 : mat;
+
+ int last = surfaces[idx].last_added;
+
+ Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+
+ Vector3 v = n->faces[i].vertices[j];
+
+ Vector3 normal = p.normal;
+
+ if (n->faces[i].smooth && vec_map.lookup(v, normal)) {
+ normal.normalize();
+ }
+
+ if (n->faces[i].invert) {
+
+ normal = -normal;
+ }
+
+ surfaces[idx].verticesw[last + order[j]] = v;
+ surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j];
+ surfaces[idx].normalsw[last + order[j]] = normal;
+ }
+
+ surfaces[idx].last_added += 3;
+ }
+ }
+
+ root_mesh.instance();
+ //create surfaces
+
+ for (int i = 0; i < surfaces.size(); i++) {
+
+ surfaces[i].verticesw = PoolVector<Vector3>::Write();
+ surfaces[i].normalsw = PoolVector<Vector3>::Write();
+ surfaces[i].uvsw = PoolVector<Vector2>::Write();
+
+ if (surfaces[i].last_added == 0)
+ continue;
+
+ Array array;
+ array.resize(Mesh::ARRAY_MAX);
+
+ array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices;
+ array[Mesh::ARRAY_NORMAL] = surfaces[i].normals;
+ array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs;
+
+ int idx = root_mesh->get_surface_count();
+ root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ root_mesh->surface_set_material(idx, surfaces[i].material);
+ }
+
+ if (root_collision_shape.is_valid()) {
+ root_collision_shape->set_faces(physics_faces);
+ }
+
+ set_base(root_mesh->get_rid());
+}
+AABB CSGShape::get_aabb() const {
+ return node_aabb;
+}
+
+PoolVector<Vector3> CSGShape::get_brush_faces() {
+ ERR_FAIL_COND_V(!is_inside_tree(), PoolVector<Vector3>());
+ CSGBrush *b = _get_brush();
+ if (!b) {
+ return PoolVector<Vector3>();
+ }
+
+ PoolVector<Vector3> faces;
+ int fc = b->faces.size();
+ faces.resize(fc * 3);
+ {
+ PoolVector<Vector3>::Write w = faces.write();
+ for (int i = 0; i < fc; i++) {
+ w[i * 3 + 0] = b->faces[i].vertices[0];
+ w[i * 3 + 1] = b->faces[i].vertices[1];
+ w[i * 3 + 2] = b->faces[i].vertices[2];
+ }
+ }
+
+ return faces;
+}
+
+PoolVector<Face3> CSGShape::get_faces(uint32_t p_usage_flags) const {
+
+ return PoolVector<Face3>();
+}
+
+void CSGShape::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ 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()) {
+ root_collision_shape.instance();
+ root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform());
+ PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
+ PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
+ }
+
+ _make_dirty();
+ }
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+
+ //print_line("local xform changed");
+ if (parent) {
+ parent->_make_dirty();
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+
+ if (parent)
+ parent->_make_dirty();
+ parent = NULL;
+
+ if (use_collision && is_root_shape()) {
+ PhysicsServer::get_singleton()->free(root_collision_instance);
+ root_collision_instance = RID();
+ root_collision_shape.unref();
+ }
+ _make_dirty();
+ }
+}
+
+void CSGShape::set_operation(Operation p_operation) {
+
+ operation = p_operation;
+ _make_dirty();
+}
+
+CSGShape::Operation CSGShape::get_operation() const {
+ return operation;
+}
+
+void CSGShape::_validate_property(PropertyInfo &property) const {
+ if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
+ //hide collision if not root
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
+void CSGShape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape);
+ ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape::is_root_shape);
+
+ ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation);
+ ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation);
+
+ ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision);
+ ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision);
+
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
+ ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+
+ BIND_ENUM_CONSTANT(OPERATION_UNION);
+ BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
+ BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
+}
+
+CSGShape::CSGShape() {
+ brush = NULL;
+ set_notify_local_transform(true);
+ dirty = false;
+ parent = NULL;
+ use_collision = false;
+ operation = OPERATION_UNION;
+ snap = 0.001;
+}
+
+CSGShape::~CSGShape() {
+ if (brush) {
+ memdelete(brush);
+ brush = NULL;
+ }
+}
+//////////////////////////////////
+
+CSGBrush *CSGCombiner::_build_brush() {
+
+ return NULL; //does not build anything
+}
+
+CSGCombiner::CSGCombiner() {
+}
+
+/////////////////////
+
+CSGBrush *CSGPrimitive::_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials) {
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ PoolVector<bool> invert;
+ invert.resize(p_vertices.size() / 3);
+ {
+ int ic = invert.size();
+ PoolVector<bool>::Write w = invert.write();
+ for (int i = 0; i < ic; i++) {
+ w[i] = invert_faces;
+ }
+ }
+ brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
+
+ return brush;
+}
+
+void CSGPrimitive::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive::set_invert_faces);
+ ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive::is_inverting_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces");
+}
+
+void CSGPrimitive::set_invert_faces(bool p_invert) {
+ if (invert_faces == p_invert)
+ return;
+
+ invert_faces = p_invert;
+
+ _make_dirty();
+}
+
+bool CSGPrimitive::is_inverting_faces() {
+ return invert_faces;
+}
+
+CSGPrimitive::CSGPrimitive() {
+ invert_faces = false;
+}
+
+/////////////////////
+
+CSGBrush *CSGMesh::_build_brush() {
+
+ if (!mesh.is_valid())
+ return NULL;
+
+ PoolVector<Vector3> vertices;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<Vector2> uvs;
+
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+
+ if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+
+ Array arrays = mesh->surface_get_arrays(i);
+
+ PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
+ if (avertices.size() == 0)
+ continue;
+
+ PoolVector<Vector3>::Read vr = avertices.read();
+
+ PoolVector<Vector3> anormals = arrays[Mesh::ARRAY_NORMAL];
+ PoolVector<Vector3>::Read nr;
+ bool nr_used = false;
+ if (anormals.size()) {
+ nr = anormals.read();
+ nr_used = true;
+ }
+
+ PoolVector<Vector2> auvs = arrays[Mesh::ARRAY_TEX_UV];
+ PoolVector<Vector2>::Read uvr;
+ bool uvr_used = false;
+ if (auvs.size()) {
+ uvr = auvs.read();
+ uvr_used = true;
+ }
+
+ Ref<Material> mat = mesh->surface_get_material(i);
+
+ PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX];
+ if (aindices.size()) {
+ int as = vertices.size();
+ int is = aindices.size();
+
+ vertices.resize(as + is);
+ smooth.resize((as + is) / 3);
+ materials.resize((as + is) / 3);
+ uvs.resize(as + is);
+
+ PoolVector<Vector3>::Write vw = vertices.write();
+ PoolVector<bool>::Write sw = smooth.write();
+ PoolVector<Vector2>::Write uvw = uvs.write();
+ PoolVector<Ref<Material> >::Write mw = materials.write();
+
+ PoolVector<int>::Read ir = aindices.read();
+
+ for (int j = 0; j < is; j += 3) {
+
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+
+ for (int k = 0; k < 3; k++) {
+ int idx = ir[j + k];
+ vertex[k] = vr[idx];
+ if (nr_used) {
+ normal[k] = nr[idx];
+ }
+ if (uvr_used) {
+ uv[k] = uvr[idx];
+ }
+ }
+
+ bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+
+ vw[as + j + 0] = vertex[0];
+ vw[as + j + 1] = vertex[1];
+ vw[as + j + 2] = vertex[2];
+
+ uvw[as + j + 0] = uv[0];
+ uvw[as + j + 1] = uv[1];
+ uvw[as + j + 2] = uv[2];
+
+ sw[j / 3] = !flat;
+ mw[j / 3] = mat;
+ }
+ } else {
+ int is = vertices.size();
+ int as = avertices.size();
+
+ vertices.resize(as + is);
+ smooth.resize((as + is) / 3);
+ uvs.resize(as + is);
+ materials.resize((as + is) / 3);
+
+ PoolVector<Vector3>::Write vw = vertices.write();
+ PoolVector<bool>::Write sw = smooth.write();
+ PoolVector<Vector2>::Write uvw = uvs.write();
+ PoolVector<Ref<Material> >::Write mw = materials.write();
+
+ for (int j = 0; j < is; j += 3) {
+
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+
+ for (int k = 0; k < 3; k++) {
+ vertex[k] = vr[j + k];
+ if (nr_used) {
+ normal[k] = nr[j + k];
+ }
+ if (uvr_used) {
+ uv[k] = uvr[j + k];
+ }
+ }
+
+ bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+
+ vw[as + j + 0] = vertex[0];
+ vw[as + j + 1] = vertex[1];
+ vw[as + j + 2] = vertex[2];
+
+ uvw[as + j + 0] = uv[0];
+ uvw[as + j + 1] = uv[1];
+ uvw[as + j + 2] = uv[2];
+
+ sw[j / 3] = !flat;
+ mw[j / 3] = mat;
+ }
+ }
+ }
+
+ //print_line("total vertices? " + itos(vertices.size()));
+ if (vertices.size() == 0)
+ return NULL;
+
+ return _create_brush_from_arrays(vertices, uvs, smooth, materials);
+}
+
+void CSGMesh::_mesh_changed() {
+ _make_dirty();
+ update_gizmo();
+}
+
+void CSGMesh::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+}
+
+void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) {
+
+ if (mesh == p_mesh)
+ return;
+ if (mesh.is_valid()) {
+ mesh->disconnect("changed", this, "_mesh_changed");
+ }
+ mesh = p_mesh;
+
+ if (mesh.is_valid()) {
+ mesh->connect("changed", this, "_mesh_changed");
+ }
+
+ _make_dirty();
+}
+
+Ref<Mesh> CSGMesh::get_mesh() {
+ return mesh;
+}
+
+////////////////////////////////
+
+CSGBrush *CSGSphere::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = rings * radial_segments * 2 - radial_segments * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ for (int i = 1; i <= rings; i++) {
+ double lat0 = Math_PI * (-0.5 + (double)(i - 1) / rings);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+ double u0 = double(i - 1) / rings;
+
+ double lat1 = Math_PI * (-0.5 + (double)i / rings);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+ double u1 = double(i) / rings;
+
+ for (int j = radial_segments; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double)(j - 1) / radial_segments;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+ double v0 = double(i - 1) / radial_segments;
+
+ double lng1 = 2 * Math_PI * (double)(j) / radial_segments;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+ double v1 = double(i) / radial_segments;
+
+ Vector3 v[4] = {
+ Vector3(x1 * zr0, z0, y1 * zr0) * radius,
+ Vector3(x1 * zr1, z1, y1 * zr1) * radius,
+ Vector3(x0 * zr1, z1, y0 * zr1) * radius,
+ Vector3(x0 * zr0, z0, y0 * zr0) * radius
+ };
+
+ Vector2 u[4] = {
+ Vector2(v1, u0),
+ Vector2(v1, u1),
+ Vector2(v0, u1),
+ Vector2(v0, u0),
+
+ };
+
+ if (i < rings) {
+
+ //face 1
+ facesw[face * 3 + 0] = v[0];
+ facesw[face * 3 + 1] = v[1];
+ facesw[face * 3 + 2] = v[2];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+
+ if (i > 1) {
+ //face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere::set_radial_segments);
+ ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere::get_radial_segments);
+ ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere::set_rings);
+ ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere::get_rings);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere::get_smooth_faces);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere::get_material);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGSphere::set_radius(const float p_radius) {
+ ERR_FAIL_COND(p_radius <= 0);
+ radius = p_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGSphere::get_radius() const {
+ return radius;
+}
+
+void CSGSphere::set_radial_segments(const int p_radial_segments) {
+ radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGSphere::get_radial_segments() const {
+ return radial_segments;
+}
+
+void CSGSphere::set_rings(const int p_rings) {
+ rings = p_rings > 1 ? p_rings : 1;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGSphere::get_rings() const {
+ return rings;
+}
+
+void CSGSphere::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGSphere::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGSphere::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGSphere::get_material() const {
+
+ return material;
+}
+
+CSGSphere::CSGSphere() {
+ // defaults
+ radius = 1.0;
+ radial_segments = 12;
+ rings = 6;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGBox::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = 12; //it's a cube..
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ Vector3 vertex_mul(width, height, depth);
+
+ {
+
+ for (int i = 0; i < 6; i++) {
+
+ Vector3 face_points[4];
+ float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 };
+
+ for (int j = 0; j < 4; j++) {
+
+ float v[3];
+ v[0] = 1.0;
+ v[1] = 1 - 2 * ((j >> 1) & 1);
+ v[2] = v[1] * (1 - 2 * (j & 1));
+
+ for (int k = 0; k < 3; k++) {
+
+ if (i < 3)
+ face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ else
+ face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ }
+ }
+
+ Vector2 u[4];
+ for (int j = 0; j < 4; j++) {
+ u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]);
+ }
+
+ //face 1
+ facesw[face * 3 + 0] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[2] * vertex_mul;
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ //face 1
+ facesw[face * 3 + 0] = face_points[2] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[3] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[0] * vertex_mul;
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &CSGBox::get_width);
+
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CSGBox::get_height);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
+ 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::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGBox::set_width(const float p_width) {
+ width = p_width;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_width() const {
+ return width;
+}
+
+void CSGBox::set_height(const float p_height) {
+ height = p_height;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_height() const {
+ return height;
+}
+
+void CSGBox::set_depth(const float p_depth) {
+ depth = p_depth;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_depth() const {
+ return depth;
+}
+
+void CSGBox::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+ update_gizmo();
+}
+
+Ref<Material> CSGBox::get_material() const {
+
+ return material;
+}
+
+CSGBox::CSGBox() {
+ // defaults
+ width = 1.0;
+ height = 1.0;
+ depth = 1.0;
+}
+
+///////////////
+
+CSGBrush *CSGCylinder::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ Vector3 vertex_mul(radius, height * 0.5, radius);
+
+ {
+
+ for (int i = 0; i < sides; i++) {
+
+ float inc = float(i) / sides;
+ float inc_n = float((i + 1)) / sides;
+
+ float ang = inc * Math_PI * 2.0;
+ float ang_n = inc_n * Math_PI * 2.0;
+
+ Vector3 base(Math::cos(ang), 0, Math::sin(ang));
+ Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
+
+ Vector3 face_points[4] = {
+ base + Vector3(0, -1, 0),
+ base_n + Vector3(0, -1, 0),
+ base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ };
+
+ Vector2 u[4] = {
+ Vector2(inc, 0),
+ Vector2(inc_n, 0),
+ Vector2(inc_n, 1),
+ Vector2(inc, 1),
+ };
+
+ //side face 1
+ facesw[face * 3 + 0] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[2] * vertex_mul;
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+
+ if (!cone) {
+ //side face 2
+ facesw[face * 3 + 0] = face_points[2] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[3] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[0] * vertex_mul;
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+ }
+
+ //bottom face 1
+ facesw[face * 3 + 0] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul;
+
+ uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+
+ if (!cone) {
+ //top face 1
+ facesw[face * 3 + 0] = face_points[3] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[2] * vertex_mul;
+ facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul;
+
+ uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGCylinder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder::get_height);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder::get_sides);
+
+ ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder::set_cone);
+ ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder::is_cone);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGCylinder::set_radius(const float p_radius) {
+ radius = p_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGCylinder::get_radius() const {
+ return radius;
+}
+
+void CSGCylinder::set_height(const float p_height) {
+ height = p_height;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGCylinder::get_height() const {
+ return height;
+}
+
+void CSGCylinder::set_sides(const int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ sides = p_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGCylinder::get_sides() const {
+ return sides;
+}
+
+void CSGCylinder::set_cone(const bool p_cone) {
+ cone = p_cone;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGCylinder::is_cone() const {
+ return cone;
+}
+
+void CSGCylinder::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGCylinder::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGCylinder::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGCylinder::get_material() const {
+
+ return material;
+}
+
+CSGCylinder::CSGCylinder() {
+ // defaults
+ radius = 1.0;
+ height = 1.0;
+ sides = 8;
+ cone = false;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGTorus::_build_brush() {
+
+ // set our bounding box
+
+ float min_radius = inner_radius;
+ float max_radius = outer_radius;
+
+ if (min_radius == max_radius)
+ return NULL; //sorry, can't
+
+ if (min_radius > max_radius) {
+ SWAP(min_radius, max_radius);
+ }
+
+ float radius = (max_radius - min_radius) * 0.5;
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = ring_sides * sides * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ {
+
+ for (int i = 0; i < sides; i++) {
+
+ float inci = float(i) / sides;
+ float inci_n = float((i + 1)) / sides;
+
+ float angi = inci * Math_PI * 2.0;
+ float angi_n = inci_n * Math_PI * 2.0;
+
+ Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
+ Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
+
+ for (int j = 0; j < ring_sides; j++) {
+
+ float incj = float(j) / ring_sides;
+ float incj_n = float((j + 1)) / ring_sides;
+
+ float angj = incj * Math_PI * 2.0;
+ float angj_n = incj_n * Math_PI * 2.0;
+
+ Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0);
+ Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0);
+
+ Vector3 face_points[4] = {
+ Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x),
+ Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x),
+ Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x),
+ Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x)
+ };
+
+ Vector2 u[4] = {
+ Vector2(inci, incj),
+ Vector2(inci, incj_n),
+ Vector2(inci_n, incj_n),
+ Vector2(inci_n, incj),
+ };
+
+ // face 1
+ facesw[face * 3 + 0] = face_points[0];
+ facesw[face * 3 + 1] = face_points[2];
+ facesw[face * 3 + 2] = face_points[1];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[2];
+ uvsw[face * 3 + 2] = u[1];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+
+ //face 2
+ facesw[face * 3 + 0] = face_points[3];
+ facesw[face * 3 + 1] = face_points[2];
+ facesw[face * 3 + 2] = face_points[0];
+
+ uvsw[face * 3 + 0] = u[3];
+ uvsw[face * 3 + 1] = u[2];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGTorus::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus::set_inner_radius);
+ ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus::get_inner_radius);
+
+ ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus::set_outer_radius);
+ ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus::get_outer_radius);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus::get_sides);
+
+ ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus::set_ring_sides);
+ ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus::get_ring_sides);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_outer_radius", "get_outer_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGTorus::set_inner_radius(const float p_inner_radius) {
+ inner_radius = p_inner_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGTorus::get_inner_radius() const {
+ return inner_radius;
+}
+
+void CSGTorus::set_outer_radius(const float p_outer_radius) {
+ outer_radius = p_outer_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGTorus::get_outer_radius() const {
+ return outer_radius;
+}
+
+void CSGTorus::set_sides(const int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ sides = p_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGTorus::get_sides() const {
+ return sides;
+}
+
+void CSGTorus::set_ring_sides(const int p_ring_sides) {
+ ERR_FAIL_COND(p_ring_sides < 3);
+ ring_sides = p_ring_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGTorus::get_ring_sides() const {
+ return ring_sides;
+}
+
+void CSGTorus::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGTorus::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGTorus::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGTorus::get_material() const {
+
+ return material;
+}
+
+CSGTorus::CSGTorus() {
+ // defaults
+ inner_radius = 2.0;
+ outer_radius = 3.0;
+ sides = 8;
+ ring_sides = 6;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGPolygon::_build_brush() {
+
+ // set our bounding box
+
+ if (polygon.size() < 3)
+ return NULL;
+
+ Vector<Point2> final_polygon = polygon;
+
+ if (Triangulate::get_area(final_polygon) > 0) {
+ final_polygon.invert();
+ }
+
+ Vector<int> triangles = Geometry::triangulate_polygon(final_polygon);
+
+ if (triangles.size() < 3)
+ return NULL;
+
+ Path *path = NULL;
+ Ref<Curve3D> curve;
+
+ if (mode == MODE_PATH) {
+ if (!has_node(path_node))
+ return NULL;
+ Node *n = get_node(path_node);
+ if (!n)
+ return NULL;
+ path = Object::cast_to<Path>(n);
+ if (!path)
+ return NULL;
+
+ if (path != path_cache) {
+ if (path_cache) {
+ path_cache->disconnect("tree_exited", this, "_path_exited");
+ path_cache->disconnect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+
+ path_cache = path;
+
+ if (path_cache) {
+ path_cache->connect("tree_exited", this, "_path_exited");
+ path_cache->connect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+ }
+ curve = path->get_curve();
+ if (curve.is_null())
+ return NULL;
+ if (curve->get_baked_length() <= 0)
+ return NULL;
+ }
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count;
+
+ switch (mode) {
+ case MODE_DEPTH: face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; break;
+ case MODE_SPIN: face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides; break;
+ case MODE_PATH: {
+ float bl = curve->get_baked_length();
+ int splits = MAX(2, Math::ceil(bl / path_interval));
+ face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ } break;
+ }
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ AABB aabb; //must be computed
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ switch (mode) {
+ case MODE_DEPTH: {
+
+ //add triangles, front and back
+ for (int i = 0; i < 2; i++) {
+
+ for (int j = 0; j < triangles.size(); j += 3) {
+ for (int k = 0; k < 3; k++) {
+ int src[3] = { 0, i == 0 ? 1 : 2, i == 0 ? 2 : 1 };
+ Vector2 p = final_polygon[triangles[j + src[k]]];
+ Vector3 v = Vector3(p.x, p.y, 0);
+ if (i == 0) {
+ v.z -= depth;
+ }
+ facesw[face * 3 + k] = v;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ //add triangles for depth
+ for (int i = 0; i < final_polygon.size(); i++) {
+
+ int i_n = (i + 1) % final_polygon.size();
+
+ Vector3 v[4] = {
+ Vector3(final_polygon[i].x, final_polygon[i].y, -depth),
+ Vector3(final_polygon[i_n].x, final_polygon[i_n].y, -depth),
+ Vector3(final_polygon[i_n].x, final_polygon[i_n].y, 0),
+ Vector3(final_polygon[i].x, final_polygon[i].y, 0),
+ };
+
+ Vector2 u[4] = {
+ Vector2(0, 0),
+ Vector2(0, 1),
+ Vector2(1, 1),
+ Vector2(1, 0)
+ };
+
+ // face 1
+ facesw[face * 3 + 0] = v[0];
+ facesw[face * 3 + 1] = v[1];
+ facesw[face * 3 + 2] = v[2];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+
+ // face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+
+ } break;
+ case MODE_SPIN: {
+
+ for (int i = 0; i < spin_sides; i++) {
+
+ float inci = float(i) / spin_sides;
+ float inci_n = float((i + 1)) / spin_sides;
+
+ float angi = -(inci * spin_degrees / 360.0) * Math_PI * 2.0;
+ float angi_n = -(inci_n * spin_degrees / 360.0) * Math_PI * 2.0;
+
+ Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
+ Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
+
+ //add triangles for depth
+ for (int j = 0; j < final_polygon.size(); j++) {
+
+ int j_n = (j + 1) % final_polygon.size();
+
+ Vector3 v[4] = {
+ Vector3(normali.x * final_polygon[j].x, final_polygon[j].y, normali.z * final_polygon[j].x),
+ Vector3(normali.x * final_polygon[j_n].x, final_polygon[j_n].y, normali.z * final_polygon[j_n].x),
+ Vector3(normali_n.x * final_polygon[j_n].x, final_polygon[j_n].y, normali_n.z * final_polygon[j_n].x),
+ Vector3(normali_n.x * final_polygon[j].x, final_polygon[j].y, normali_n.z * final_polygon[j].x),
+ };
+
+ Vector2 u[4] = {
+ Vector2(0, 0),
+ Vector2(0, 1),
+ Vector2(1, 1),
+ Vector2(1, 0)
+ };
+
+ // face 1
+ facesw[face * 3 + 0] = v[0];
+ facesw[face * 3 + 1] = v[2];
+ facesw[face * 3 + 2] = v[1];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[2];
+ uvsw[face * 3 + 2] = u[1];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+
+ // face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[0];
+ facesw[face * 3 + 2] = v[3];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[0];
+ uvsw[face * 3 + 2] = u[3];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+
+ if (i == 0 && spin_degrees < 360) {
+
+ for (int j = 0; j < triangles.size(); j += 3) {
+ for (int k = 0; k < 3; k++) {
+ int src[3] = { 0, 2, 1 };
+ Vector2 p = final_polygon[triangles[j + src[k]]];
+ Vector3 v = Vector3(p.x, p.y, 0);
+ facesw[face * 3 + k] = v;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ if (i == spin_sides - 1 && spin_degrees < 360) {
+
+ for (int j = 0; j < triangles.size(); j += 3) {
+ for (int k = 0; k < 3; k++) {
+ int src[3] = { 0, 1, 2 };
+ Vector2 p = final_polygon[triangles[j + src[k]]];
+ Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x);
+ facesw[face * 3 + k] = v;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+ }
+ } break;
+ case MODE_PATH: {
+
+ float bl = curve->get_baked_length();
+ int splits = MAX(2, Math::ceil(bl / path_interval));
+
+ Transform path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
+
+ Transform prev_xf;
+
+ Vector3 lookat_dir;
+
+ if (path_rotation == PATH_ROTATION_POLYGON) {
+ lookat_dir = (path->get_global_transform().affine_inverse() * get_global_transform()).xform(Vector3(0, 0, -1));
+ } else {
+ Vector3 p1, p2;
+ p1 = curve->interpolate_baked(0);
+ p2 = curve->interpolate_baked(0.1);
+ lookat_dir = (p2 - p1).normalized();
+ }
+
+ for (int i = 0; i <= splits; i++) {
+
+ float ofs = i * path_interval;
+
+ Transform xf;
+ xf.origin = curve->interpolate_baked(ofs);
+
+ Vector3 local_dir;
+
+ if (path_rotation == PATH_ROTATION_PATH_FOLLOW && ofs > 0) {
+ //before end
+ Vector3 p1 = curve->interpolate_baked(ofs - 0.1);
+ Vector3 p2 = curve->interpolate_baked(ofs);
+ local_dir = (p2 - p1).normalized();
+
+ } else {
+ local_dir = lookat_dir;
+ }
+
+ xf = xf.looking_at(xf.origin + local_dir, Vector3(0, 1, 0));
+ Basis rot(Vector3(0, 0, 1), curve->interpolate_baked_tilt(ofs));
+
+ xf = xf * rot; //post mult
+
+ xf = path_to_this * xf;
+
+ if (i > 0) {
+ //put triangles where they belong
+ //add triangles for depth
+ for (int j = 0; j < final_polygon.size(); j++) {
+
+ int j_n = (j + 1) % final_polygon.size();
+
+ Vector3 v[4] = {
+ prev_xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)),
+ prev_xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)),
+ xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)),
+ xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)),
+ };
+
+ Vector2 u[4] = {
+ Vector2(0, 0),
+ Vector2(0, 1),
+ Vector2(1, 1),
+ Vector2(1, 0)
+ };
+
+ // face 1
+ facesw[face * 3 + 0] = v[0];
+ facesw[face * 3 + 1] = v[1];
+ facesw[face * 3 + 2] = v[2];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+
+ // face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+
+ if (i == 0) {
+
+ for (int j = 0; j < triangles.size(); j += 3) {
+ for (int k = 0; k < 3; k++) {
+ int src[3] = { 0, 1, 2 };
+ Vector2 p = final_polygon[triangles[j + src[k]]];
+ Vector3 v = Vector3(p.x, p.y, 0);
+ facesw[face * 3 + k] = xf.xform(v);
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ if (i == splits) {
+
+ for (int j = 0; j < triangles.size(); j += 3) {
+ for (int k = 0; k < 3; k++) {
+ int src[3] = { 0, 2, 1 };
+ Vector2 p = final_polygon[triangles[j + src[k]]];
+ Vector3 v = Vector3(p.x, p.y, 0);
+ facesw[face * 3 + k] = xf.xform(v);
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ prev_xf = xf;
+ }
+
+ } break;
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ for (int i = 0; i < face_count * 3; i++) {
+ if (i == 0) {
+ aabb.position = facesw[i];
+ } else {
+ aabb.expand_to(facesw[i]);
+ }
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGPolygon::_notification(int p_what) {
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ if (path_cache) {
+ path_cache->disconnect("tree_exited", this, "_path_exited");
+ path_cache->disconnect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+ }
+}
+
+void CSGPolygon::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("spin") && mode != MODE_SPIN) {
+ property.usage = 0;
+ }
+ if (property.name.begins_with("path") && mode != MODE_PATH) {
+ property.usage = 0;
+ }
+ if (property.name == "depth" && mode != MODE_DEPTH) {
+ property.usage = 0;
+ }
+
+ CSGShape::_validate_property(property);
+}
+
+void CSGPolygon::_path_changed() {
+ _make_dirty();
+ update_gizmo();
+}
+
+void CSGPolygon::_path_exited() {
+ path_cache = NULL;
+}
+
+void CSGPolygon::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon::set_polygon);
+ ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon::get_polygon);
+
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon::set_mode);
+ ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon::get_mode);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon::set_spin_degrees);
+ ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon::get_spin_degrees);
+
+ ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon::set_spin_sides);
+ ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon::get_spin_sides);
+
+ ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon::set_path_node);
+ ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon::get_path_node);
+
+ ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygon::set_path_interval);
+ ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon::get_path_interval);
+
+ ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation);
+ ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon::get_smooth_faces);
+
+ ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon::_is_editable_3d_polygon);
+ ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon::_has_editable_3d_polygon_no_depth);
+
+ ClassDB::bind_method(D_METHOD("_path_exited"), &CSGPolygon::_path_exited);
+ ClassDB::bind_method(D_METHOD("_path_changed"), &CSGPolygon::_path_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
+ 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", 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");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+
+ BIND_ENUM_CONSTANT(MODE_DEPTH);
+ BIND_ENUM_CONSTANT(MODE_SPIN);
+ BIND_ENUM_CONSTANT(MODE_PATH);
+
+ BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON);
+ BIND_ENUM_CONSTANT(PATH_ROTATION_PATH);
+ BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW);
+}
+
+void CSGPolygon::set_polygon(const Vector<Vector2> &p_polygon) {
+ polygon = p_polygon;
+ _make_dirty();
+ update_gizmo();
+}
+
+Vector<Vector2> CSGPolygon::get_polygon() const {
+ return polygon;
+}
+
+void CSGPolygon::set_mode(Mode p_mode) {
+ mode = p_mode;
+ _make_dirty();
+ update_gizmo();
+ _change_notify();
+}
+
+CSGPolygon::Mode CSGPolygon::get_mode() const {
+ return mode;
+}
+
+void CSGPolygon::set_depth(const float p_depth) {
+ ERR_FAIL_COND(p_depth < 0.001);
+ depth = p_depth;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGPolygon::get_depth() const {
+ return depth;
+}
+
+void CSGPolygon::set_spin_degrees(const float p_spin_degrees) {
+ ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
+ spin_degrees = p_spin_degrees;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGPolygon::get_spin_degrees() const {
+ return spin_degrees;
+}
+
+void CSGPolygon::set_spin_sides(const int p_spin_sides) {
+ ERR_FAIL_COND(p_spin_sides < 3);
+ spin_sides = p_spin_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGPolygon::get_spin_sides() const {
+ return spin_sides;
+}
+
+void CSGPolygon::set_path_node(const NodePath &p_path) {
+ path_node = p_path;
+ _make_dirty();
+ update_gizmo();
+}
+
+NodePath CSGPolygon::get_path_node() const {
+ return path_node;
+}
+
+void CSGPolygon::set_path_interval(float p_interval) {
+ ERR_FAIL_COND(p_interval < 0.001);
+ path_interval = p_interval;
+ _make_dirty();
+ update_gizmo();
+}
+float CSGPolygon::get_path_interval() const {
+ return path_interval;
+}
+
+void CSGPolygon::set_path_rotation(PathRotation p_rotation) {
+ path_rotation = p_rotation;
+ _make_dirty();
+ update_gizmo();
+}
+
+CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const {
+ return path_rotation;
+}
+
+void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGPolygon::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGPolygon::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGPolygon::get_material() const {
+
+ return material;
+}
+
+bool CSGPolygon::_is_editable_3d_polygon() const {
+ return true;
+}
+
+bool CSGPolygon::_has_editable_3d_polygon_no_depth() const {
+ return true;
+}
+
+CSGPolygon::CSGPolygon() {
+ // defaults
+ mode = MODE_DEPTH;
+ polygon.push_back(Vector2(0, 0));
+ polygon.push_back(Vector2(0, 1));
+ polygon.push_back(Vector2(1, 1));
+ polygon.push_back(Vector2(1, 0));
+ depth = 1.0;
+ spin_degrees = 360;
+ spin_sides = 8;
+ smooth_faces = false;
+ path_interval = 1;
+ path_rotation = PATH_ROTATION_PATH;
+ path_cache = NULL;
+}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
new file mode 100644
index 0000000000..cbb5c7e041
--- /dev/null
+++ b/modules/csg/csg_shape.h
@@ -0,0 +1,390 @@
+/*************************************************************************/
+/* csg_shape.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 CSG_SHAPE_H
+#define CSG_SHAPE_H
+
+#define CSGJS_HEADER_ONLY
+
+#include "csg.h"
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/concave_polygon_shape.h"
+
+class CSGShape : public VisualInstance {
+ GDCLASS(CSGShape, VisualInstance);
+
+public:
+ enum Operation {
+ OPERATION_UNION,
+ OPERATION_INTERSECTION,
+ OPERATION_SUBTRACTION,
+
+ };
+
+private:
+ Operation operation;
+ CSGShape *parent;
+
+ CSGBrush *brush;
+
+ AABB node_aabb;
+
+ bool dirty;
+ float snap;
+
+ bool use_collision;
+ Ref<ConcavePolygonShape> root_collision_shape;
+ RID root_collision_instance;
+
+ Ref<ArrayMesh> root_mesh;
+
+ struct Vector3Hasher {
+ _ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
+ uint32_t h = hash_djb2_one_float(p_vec3.x);
+ h = hash_djb2_one_float(p_vec3.y, h);
+ h = hash_djb2_one_float(p_vec3.z, h);
+ return h;
+ }
+ };
+
+ struct ShapeUpdateSurface {
+ PoolVector<Vector3> vertices;
+ PoolVector<Vector3> normals;
+ PoolVector<Vector2> uvs;
+ Ref<Material> material;
+ int last_added;
+
+ PoolVector<Vector3>::Write verticesw;
+ PoolVector<Vector3>::Write normalsw;
+ PoolVector<Vector2>::Write uvsw;
+ };
+
+ void _update_shape();
+
+protected:
+ void _notification(int p_what);
+ virtual CSGBrush *_build_brush() = 0;
+ void _make_dirty();
+
+ static void _bind_methods();
+
+ friend class CSGCombiner;
+ CSGBrush *_get_brush();
+
+ virtual void _validate_property(PropertyInfo &property) const;
+
+public:
+ void set_operation(Operation p_operation);
+ Operation get_operation() const;
+
+ virtual PoolVector<Vector3> get_brush_faces();
+
+ virtual AABB get_aabb() const;
+ virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_use_collision(bool p_enable);
+ bool is_using_collision() const;
+
+ void set_snap(float p_snap);
+ float get_snap() const;
+
+ bool is_root_shape() const;
+ CSGShape();
+ ~CSGShape();
+};
+
+VARIANT_ENUM_CAST(CSGShape::Operation)
+
+class CSGCombiner : public CSGShape {
+ GDCLASS(CSGCombiner, CSGShape)
+private:
+ virtual CSGBrush *_build_brush();
+
+public:
+ CSGCombiner();
+};
+
+class CSGPrimitive : public CSGShape {
+ GDCLASS(CSGPrimitive, CSGShape)
+
+private:
+ bool invert_faces;
+
+protected:
+ CSGBrush *_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials);
+ static void _bind_methods();
+
+public:
+ void set_invert_faces(bool p_invert);
+ bool is_inverting_faces();
+
+ CSGPrimitive();
+};
+
+class CSGMesh : public CSGPrimitive {
+ GDCLASS(CSGMesh, CSGPrimitive)
+
+ virtual CSGBrush *_build_brush();
+
+ Ref<Mesh> mesh;
+
+ void _mesh_changed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh();
+};
+
+class CSGSphere : public CSGPrimitive {
+
+ GDCLASS(CSGSphere, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ bool smooth_faces;
+ float radius;
+ int radial_segments;
+ int rings;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(const float p_radius);
+ float get_radius() const;
+
+ void set_radial_segments(const int p_radial_segments);
+ int get_radial_segments() const;
+
+ void set_rings(const int p_rings);
+ int get_rings() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ CSGSphere();
+};
+
+class CSGBox : public CSGPrimitive {
+
+ GDCLASS(CSGBox, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float width;
+ float height;
+ float depth;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_width(const float p_width);
+ float get_width() const;
+
+ void set_height(const float p_height);
+ float get_height() const;
+
+ void set_depth(const float p_depth);
+ float get_depth() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGBox();
+};
+
+class CSGCylinder : public CSGPrimitive {
+
+ GDCLASS(CSGCylinder, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float radius;
+ float height;
+ int sides;
+ bool cone;
+ bool smooth_faces;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(const float p_radius);
+ float get_radius() const;
+
+ void set_height(const float p_height);
+ float get_height() const;
+
+ void set_sides(const int p_sides);
+ int get_sides() const;
+
+ void set_cone(const bool p_cone);
+ bool is_cone() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGCylinder();
+};
+
+class CSGTorus : public CSGPrimitive {
+
+ GDCLASS(CSGTorus, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float inner_radius;
+ float outer_radius;
+ int sides;
+ int ring_sides;
+ bool smooth_faces;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_inner_radius(const float p_inner_radius);
+ float get_inner_radius() const;
+
+ void set_outer_radius(const float p_outer_radius);
+ float get_outer_radius() const;
+
+ void set_sides(const int p_sides);
+ int get_sides() const;
+
+ void set_ring_sides(const int p_ring_sides);
+ int get_ring_sides() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGTorus();
+};
+
+class CSGPolygon : public CSGPrimitive {
+
+ GDCLASS(CSGPolygon, CSGPrimitive)
+
+public:
+ enum Mode {
+ MODE_DEPTH,
+ MODE_SPIN,
+ MODE_PATH
+ };
+
+ enum PathRotation {
+ PATH_ROTATION_POLYGON,
+ PATH_ROTATION_PATH,
+ PATH_ROTATION_PATH_FOLLOW,
+ };
+
+private:
+ virtual CSGBrush *_build_brush();
+
+ Vector<Vector2> polygon;
+ Ref<Material> material;
+
+ Mode mode;
+
+ float depth;
+
+ float spin_degrees;
+ int spin_sides;
+
+ NodePath path_node;
+ float path_interval;
+ PathRotation path_rotation;
+
+ Node *path_cache;
+
+ bool smooth_faces;
+
+ bool _is_editable_3d_polygon() const;
+ bool _has_editable_3d_polygon_no_depth() const;
+
+ void _path_changed();
+ void _path_exited();
+
+protected:
+ static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
+ void _notification(int p_what);
+
+public:
+ void set_polygon(const Vector<Vector2> &p_polygon);
+ Vector<Vector2> get_polygon() const;
+
+ void set_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ void set_depth(float p_depth);
+ float get_depth() const;
+
+ void set_spin_degrees(float p_spin_degrees);
+ float get_spin_degrees() const;
+
+ void set_spin_sides(int p_sides);
+ int get_spin_sides() const;
+
+ void set_path_node(const NodePath &p_path);
+ NodePath get_path_node() const;
+
+ void set_path_interval(float p_interval);
+ float get_path_interval() const;
+
+ void set_path_rotation(PathRotation p_rotation);
+ PathRotation get_path_rotation() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGPolygon();
+};
+
+VARIANT_ENUM_CAST(CSGPolygon::Mode)
+VARIANT_ENUM_CAST(CSGPolygon::PathRotation)
+
+#endif // CSG_SHAPE_H
diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml
new file mode 100644
index 0000000000..80455fda80
--- /dev/null
+++ b/modules/csg/doc_classes/CSGBox.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="depth" type="float" setter="set_depth" getter="get_depth">
+ </member>
+ <member name="height" type="float" setter="set_height" getter="get_height">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="width" type="float" setter="set_width" getter="get_width">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml
new file mode 100644
index 0000000000..b2265d7703
--- /dev/null
+++ b/modules/csg/doc_classes/CSGCombiner.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml
new file mode 100644
index 0000000000..0cab26ad3d
--- /dev/null
+++ b/modules/csg/doc_classes/CSGCylinder.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="cone" type="bool" setter="set_cone" getter="is_cone">
+ </member>
+ <member name="height" type="float" setter="set_height" getter="get_height">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius">
+ </member>
+ <member name="sides" type="int" setter="set_sides" getter="get_sides">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml
new file mode 100644
index 0000000000..e5c3e5ccf3
--- /dev/null
+++ b/modules/csg/doc_classes/CSGMesh.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml
new file mode 100644
index 0000000000..379c512d6a
--- /dev/null
+++ b/modules/csg/doc_classes/CSGPolygon.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="depth" type="float" setter="set_depth" getter="get_depth">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode">
+ </member>
+ <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
+ </member>
+ <member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
+ </member>
+ <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon.PathRotation">
+ </member>
+ <member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ <member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
+ </member>
+ <member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
+ </member>
+ </members>
+ <constants>
+ <constant name="MODE_DEPTH" value="0" enum="Mode">
+ </constant>
+ <constant name="MODE_SPIN" value="1" enum="Mode">
+ </constant>
+ <constant name="MODE_PATH" value="2" enum="Mode">
+ </constant>
+ <constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
+ </constant>
+ <constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
+ </constant>
+ <constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml
new file mode 100644
index 0000000000..bf41c40f22
--- /dev/null
+++ b/modules/csg/doc_classes/CSGPrimitive.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
new file mode 100644
index 0000000000..cf236a4207
--- /dev/null
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="is_root_shape" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
+ </member>
+ <member name="snap" type="float" setter="set_snap" getter="get_snap">
+ </member>
+ <member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision">
+ </member>
+ </members>
+ <constants>
+ <constant name="OPERATION_UNION" value="0" enum="Operation">
+ </constant>
+ <constant name="OPERATION_INTERSECTION" value="1" enum="Operation">
+ </constant>
+ <constant name="OPERATION_SUBTRACTION" value="2" enum="Operation">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml
new file mode 100644
index 0000000000..520368506e
--- /dev/null
+++ b/modules/csg/doc_classes/CSGSphere.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments">
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius">
+ </member>
+ <member name="rings" type="int" setter="set_rings" getter="get_rings">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml
new file mode 100644
index 0000000000..58bbef2600
--- /dev/null
+++ b/modules/csg/doc_classes/CSGTorus.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <members>
+ <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius">
+ </member>
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ </member>
+ <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius">
+ </member>
+ <member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides">
+ </member>
+ <member name="sides" type="int" setter="set_sides" getter="get_sides">
+ </member>
+ <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
new file mode 100644
index 0000000000..0dea09808a
--- /dev/null
+++ b/modules/csg/register_types.cpp
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* 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 "csg_gizmos.h"
+#include "csg_shape.h"
+
+void register_csg_types() {
+
+#ifndef _3D_DISABLED
+
+ ClassDB::register_virtual_class<CSGShape>();
+ ClassDB::register_virtual_class<CSGPrimitive>();
+ ClassDB::register_class<CSGMesh>();
+ ClassDB::register_class<CSGSphere>();
+ ClassDB::register_class<CSGBox>();
+ ClassDB::register_class<CSGCylinder>();
+ ClassDB::register_class<CSGTorus>();
+ ClassDB::register_class<CSGPolygon>();
+ ClassDB::register_class<CSGCombiner>();
+
+#ifdef TOOLS_ENABLED
+ EditorPlugins::add_by_type<EditorPluginCSG>();
+#endif
+#endif
+}
+
+void unregister_csg_types() {
+}
diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h
new file mode 100644
index 0000000000..49490d31d3
--- /dev/null
+++ b/modules/csg/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_csg_types();
+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 23ee327cc5..fab4b05da9 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.0-stable">
+<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1">
<brief_description>
PacketPeer implementation using the ENet library.
</brief_description>
<description>
- A connection (or a listening server) that should be passed to [method SceneTree.set_network_peer]. Socket events can be handled by connecting to [SceneTree] signals.
+ 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>
+ <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>
@@ -14,13 +16,16 @@
<method name="close_connection">
<return type="void">
</return>
+ <argument index="0" name="wait_usec" type="int" default="100">
+ </argument>
<description>
+ Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server.
</description>
</method>
<method name="create_client">
<return type="int" enum="Error">
</return>
- <argument index="0" name="ip" type="String">
+ <argument index="0" name="address" type="String">
</argument>
<argument index="1" name="port" type="int">
</argument>
@@ -28,8 +33,10 @@
</argument>
<argument index="3" name="out_bandwidth" type="int" default="0">
</argument>
+ <argument index="4" name="client_port" type="int" default="0">
+ </argument>
<description>
- Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code].
+ Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain nome (e.g. [code]www.example.com[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]192.168.1.1[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port, this is useful in some NAT traveral technique.
</description>
</method>
<method name="create_server">
@@ -44,7 +51,50 @@
<argument index="3" name="out_bandwidth" type="int" default="0">
</argument>
<description>
- Create server that listens to connections via [code]port[/code].
+ Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created.
+ </description>
+ </method>
+ <method name="disconnect_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="now" type="bool" default="false">
+ </argument>
+ <description>
+ Disconnect the given peer. If "now" is set to true, the connection will be closed immediately without flushing queued messages.
+ </description>
+ </method>
+ <method name="get_last_packet_channel" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the channel of the last packet fetched via [method PacketPeer.get_packet]
+ </description>
+ </method>
+ <method name="get_packet_channel" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet_peer]
+ </description>
+ </method>
+ <method name="get_peer_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the IP address of the given peer.
+ </description>
+ </method>
+ <method name="get_peer_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the remote port of the given peer.
</description>
</method>
<method name="set_bind_ip">
@@ -53,23 +103,39 @@
<argument index="0" name="ip" type="String">
</argument>
<description>
+ The IP used when creating a server. This is set to the wildcard [code]*[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code].
</description>
</method>
</methods>
<members>
+ <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered">
+ Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system.
+ </member>
+ <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count">
+ The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis.
+ </member>
<member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode">
+ The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
+ </member>
+ <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel">
+ Set the default channel to be used to transfer data. By default this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data.
</member>
</members>
<constants>
<constant name="COMPRESS_NONE" value="0" enum="CompressionMode">
+ No compression.
</constant>
<constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode">
+ ENet's buildin range encoding.
</constant>
<constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode">
+ FastLZ compression.
</constant>
<constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode">
+ zlib compression.
</constant>
<constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode">
+ ZStandard compression.
</constant>
</constants>
</class>
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index f3f4acd768..88768829d7 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "networked_multiplayer_enet.h"
+#include "io/ip.h"
#include "io/marshalls.h"
#include "os/os.h"
@@ -54,9 +55,29 @@ int NetworkedMultiplayerENet::get_packet_peer() const {
return incoming_packets.front()->get().from;
}
+int NetworkedMultiplayerENet::get_packet_channel() const {
+
+ ERR_FAIL_COND_V(!active, -1);
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, -1);
+
+ return incoming_packets.front()->get().channel;
+}
+
+int NetworkedMultiplayerENet::get_last_packet_channel() const {
+
+ ERR_FAIL_COND_V(!active, -1);
+ ERR_FAIL_COND_V(!current_packet.packet, -1);
+
+ return current_packet.channel;
+}
+
Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_max_clients < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER);
ENetAddress address;
@@ -78,9 +99,9 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
host = enet_host_create(&address /* the address to bind the server host to */,
p_max_clients /* allow up to 32 clients and/or outgoing connections */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
- p_in_bandwidth /* assume any amount of incoming bandwidth */,
- p_out_bandwidth /* assume any amount of outgoing bandwidth */);
+ channel_count /* allow up to channel_count to be used */,
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
ERR_FAIL_COND_V(!host, ERR_CANT_CREATE);
@@ -92,43 +113,84 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
connection_status = CONNECTION_CONNECTED;
return OK;
}
-Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth) {
+Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_client_port) {
ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER);
+
+ if (p_client_port != 0) {
+ ENetAddress c_client;
+
+#ifdef GODOT_ENET
+ if (bind_ip.is_wildcard()) {
+ c_client.wildcard = 1;
+ } else {
+ enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16);
+ }
+#else
+ if (bind_ip.is_wildcard()) {
+ c_client.host = 0;
+ } else {
+ ERR_FAIL_COND_V(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER);
+ c_client.host = *(uint32_t *)bind_ip.get_ipv4();
+ }
+#endif
+
+ c_client.port = p_client_port;
- host = enet_host_create(NULL /* create a client host */,
- 1 /* only allow 1 outgoing connection */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
- p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */,
- p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */);
+ host = enet_host_create(&c_client /* create a client host */,
+ 1 /* only allow 1 outgoing connection */,
+ channel_count /* allow up to channel_count to be used */,
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
+ } else {
+ host = enet_host_create(NULL /* create a client host */,
+ 1 /* only allow 1 outgoing connection */,
+ channel_count /* allow up to channel_count to be used */,
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
+ }
ERR_FAIL_COND_V(!host, ERR_CANT_CREATE);
_setup_compressor();
+ IP_Address ip;
+ if (p_address.is_valid_ip_address()) {
+ ip = p_address;
+ } else {
+#ifdef GODOT_ENET
+ ip = IP::get_singleton()->resolve_hostname(p_address);
+#else
+ ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4);
+#endif
+
+ ERR_FAIL_COND_V(!ip.is_valid(), ERR_CANT_RESOLVE);
+ }
+
ENetAddress address;
#ifdef GODOT_ENET
- enet_address_set_ip(&address, p_ip.get_ipv6(), 16);
+ enet_address_set_ip(&address, ip.get_ipv6(), 16);
#else
- ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER);
- address.host = *(uint32_t *)p_ip.get_ipv4();
+ ERR_FAIL_COND_V(!ip.is_ipv4(), ERR_INVALID_PARAMETER);
+ address.host = *(uint32_t *)ip.get_ipv4();
#endif
address.port = p_port;
- //enet_address_set_host (& address, "localhost");
- //address.port = p_port;
-
unique_id = _gen_unique_id();
- /* Initiate the connection, allocating the enough channels */
- ENetPeer *peer = enet_host_connect(host, &address, SYSCH_MAX, unique_id);
+ // Initiate connection, allocating enough channels
+ ENetPeer *peer = enet_host_connect(host, &address, channel_count, unique_id);
if (peer == NULL) {
enet_host_destroy(host);
ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE);
}
- //technically safe to ignore the peer or anything else.
+ // Technically safe to ignore the peer or anything else.
connection_status = CONNECTION_CONNECTING;
active = true;
@@ -148,13 +210,13 @@ void NetworkedMultiplayerENet::poll() {
/* Wait up to 1000 milliseconds for an event. */
while (true) {
- if (!host || !active) //might have been disconnected while emitting a notification
+ if (!host || !active) // Might have been disconnected while emitting a notification
return;
int ret = enet_host_service(host, &event, 1);
if (ret < 0) {
- //error, do something?
+ // Error, do something?
break;
} else if (ret == 0) {
break;
@@ -162,7 +224,7 @@ void NetworkedMultiplayerENet::poll() {
switch (event.type) {
case ENET_EVENT_TYPE_CONNECT: {
- /* Store any relevant client information here. */
+ // Store any relevant client information here.
if (server && refuse_connections) {
enet_peer_reset(event.peer);
@@ -172,7 +234,7 @@ void NetworkedMultiplayerENet::poll() {
int *new_id = memnew(int);
*new_id = event.data;
- if (*new_id == 0) { //data zero is sent by server (enet won't let you configure this). Server is always 1
+ if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1.
*new_id = 1;
}
@@ -180,22 +242,22 @@ void NetworkedMultiplayerENet::poll() {
peer_map[*new_id] = event.peer;
- connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something!
+ connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something!
emit_signal("peer_connected", *new_id);
if (server) {
- //someone connected, let it know of all the peers available
+ // Someone connected, notify all the peers available
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *new_id)
continue;
- //send existing peers to new peer
+ // Send existing peers to new peer
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(E->key(), &packet->data[4]);
enet_peer_send(event.peer, SYSCH_CONFIG, packet);
- //send the new peer to existing peers
+ // Send the new peer to existing peers
packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(*new_id, &packet->data[4]);
@@ -209,7 +271,7 @@ void NetworkedMultiplayerENet::poll() {
} break;
case ENET_EVENT_TYPE_DISCONNECT: {
- /* Reset the peer's client information. */
+ // Reset the peer's client information.
int *id = (int *)event.peer->data;
@@ -220,12 +282,12 @@ void NetworkedMultiplayerENet::poll() {
} else {
if (server) {
- //someone disconnected, let it know to everyone else
+ // Someone disconnected, notify everyone else
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *id)
continue;
- //send the new peer to existing peers
+
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
encode_uint32(*id, &packet->data[4]);
@@ -246,7 +308,7 @@ void NetworkedMultiplayerENet::poll() {
case ENET_EVENT_TYPE_RECEIVE: {
if (event.channelID == SYSCH_CONFIG) {
- //some config message
+ // Some config message
ERR_CONTINUE(event.packet->dataLength < 8);
// Only server can send config messages
@@ -270,7 +332,7 @@ void NetworkedMultiplayerENet::poll() {
}
enet_packet_destroy(event.packet);
- } else if (event.channelID < SYSCH_MAX) {
+ } else if (event.channelID < channel_count) {
Packet packet;
packet.packet = event.packet;
@@ -284,6 +346,7 @@ void NetworkedMultiplayerENet::poll() {
uint32_t flags = decode_uint32(&event.packet->data[8]);
packet.from = source;
+ packet.channel = event.channelID;
if (server) {
// Someone is cheating and trying to fake the source!
@@ -292,13 +355,13 @@ void NetworkedMultiplayerENet::poll() {
packet.from = *id;
if (target == 0) {
- //re-send the everyone but sender :|
+ // Re-send to everyone but sender :|
incoming_packets.push_back(packet);
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source) //do not resend to self
+ if (uint32_t(E->key()) == source) // Do not resend to self
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -307,12 +370,12 @@ void NetworkedMultiplayerENet::poll() {
}
} else if (target < 0) {
- //to all but one
+ // To all but one
- //and make copies for sending
+ // And make copies for sending
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source || E->key() == -target) //do not resend to self, also do not send to excluded
+ if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -321,18 +384,18 @@ void NetworkedMultiplayerENet::poll() {
}
if (-target != 1) {
- //server is not excluded
+ // Server is not excluded
incoming_packets.push_back(packet);
} else {
- //server is excluded, erase packet
+ // Server is excluded, erase packet
enet_packet_destroy(packet.packet);
}
} else if (target == 1) {
- //to myself and only myself
+ // To myself and only myself
incoming_packets.push_back(packet);
} else {
- //to someone else, specifically
+ // To someone else, specifically
ERR_CONTINUE(!peer_map.has(target));
enet_peer_send(peer_map[target], event.channelID, packet.packet);
}
@@ -341,14 +404,14 @@ void NetworkedMultiplayerENet::poll() {
incoming_packets.push_back(packet);
}
- //destroy packet later..
+ // Destroy packet later
} else {
ERR_CONTINUE(true);
}
} break;
case ENET_EVENT_TYPE_NONE: {
- //do nothing
+ // Do nothing
} break;
}
}
@@ -360,10 +423,10 @@ bool NetworkedMultiplayerENet::is_server() const {
return server;
}
-void NetworkedMultiplayerENet::close_connection() {
+void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) {
- if (!active)
- return;
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(wait_usec < 0);
_pop_current_packet();
@@ -377,20 +440,54 @@ void NetworkedMultiplayerENet::close_connection() {
if (peers_disconnected) {
enet_host_flush(host);
- OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send
+
+ if (wait_usec > 0) {
+ OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send
+ }
}
enet_host_destroy(host);
active = false;
incoming_packets.clear();
- unique_id = 1; //server is 1
+ unique_id = 1; // Server is 1
connection_status = CONNECTION_DISCONNECTED;
}
+void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
+
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(!is_server());
+ ERR_FAIL_COND(!peer_map.has(p_peer))
+
+ if (now) {
+ enet_peer_disconnect_now(peer_map[p_peer], 0);
+
+ // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT,
+ // notify everyone else, send disconnect signal & remove from peer_map like in poll()
+
+ for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
+
+ if (E->key() == p_peer)
+ continue;
+
+ ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
+ encode_uint32(p_peer, &packet->data[4]);
+ enet_peer_send(E->get(), SYSCH_CONFIG, packet);
+ }
+
+ emit_signal("peer_disconnected", p_peer);
+ peer_map.erase(p_peer);
+ } else {
+ enet_peer_disconnect_later(peer_map[p_peer], 0);
+ }
+}
+
int NetworkedMultiplayerENet::get_available_packet_count() const {
return incoming_packets.size();
}
+
Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
ERR_FAIL_COND_V(incoming_packets.size() == 0, ERR_UNAVAILABLE);
@@ -405,6 +502,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff
return OK;
}
+
Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!active, ERR_UNCONFIGURED);
@@ -415,7 +513,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
switch (transfer_mode) {
case TRANSFER_MODE_UNRELIABLE: {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
+ if (always_ordered)
+ packet_flags = 0;
+ else
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
channel = SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_UNRELIABLE_ORDERED: {
@@ -428,6 +529,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
} break;
}
+ if (transfer_channel > SYSCH_CONFIG)
+ channel = transfer_channel;
+
Map<int, ENetPeer *>::Element *E = NULL;
if (target_peer != 0) {
@@ -440,9 +544,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
}
ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); //source ID
- encode_uint32(target_peer, &packet->data[4]); //dest ID
- encode_uint32(packet_flags, &packet->data[8]); //dest ID
+ encode_uint32(unique_id, &packet->data[0]); // Source ID
+ encode_uint32(target_peer, &packet->data[4]); // Dest ID
+ encode_uint32(packet_flags, &packet->data[8]); // Dest ID
copymem(&packet->data[12], p_buffer, p_buffer_size);
if (server) {
@@ -450,14 +554,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
if (target_peer == 0) {
enet_host_broadcast(host, channel, packet);
} else if (target_peer < 0) {
- //send to all but one
- //and make copies for sending
+ // Send to all but one
+ // and make copies for sending
int exclude = -target_peer;
for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) {
- if (F->key() == exclude) // exclude packet
+ if (F->key() == exclude) // Exclude packet
continue;
ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags);
@@ -465,14 +569,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
enet_peer_send(F->get(), channel, packet2);
}
- enet_packet_destroy(packet); //original packet no longer needed
+ enet_packet_destroy(packet); // Original packet no longer needed
} else {
enet_peer_send(E->get(), channel, packet);
}
} else {
ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG);
- enet_peer_send(peer_map[1], channel, packet); //send to server for broadcast..
+ enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast
}
enet_host_flush(host);
@@ -482,7 +586,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
int NetworkedMultiplayerENet::get_max_packet_size() const {
- return 1 << 24; //anything is good
+ return 1 << 24; // Anything is good
}
void NetworkedMultiplayerENet::_pop_current_packet() {
@@ -491,6 +595,7 @@ void NetworkedMultiplayerENet::_pop_current_packet() {
enet_packet_destroy(current_packet.packet);
current_packet.packet = NULL;
current_packet.from = 0;
+ current_packet.channel = -1;
}
}
@@ -511,16 +616,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
(uint32_t)OS::get_singleton()->get_unix_time(), hash);
hash = hash_djb2_one_32(
(uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
- /*
hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_unique_id().hash64(), hash );
- */
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); //rely on aslr heap
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
- hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
}
return hash;
@@ -596,7 +697,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *
return 0;
if (ret > int(outLimit))
- return 0; //do not bother
+ return 0; // Do not bother
copymem(outData, enet->dst_compressor_mem.ptr(), ret);
@@ -651,19 +752,96 @@ void NetworkedMultiplayerENet::_setup_compressor() {
void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
- //do none
+ // Nothing to do
+}
+
+IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address());
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address());
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address());
+
+ IP_Address out;
+#ifdef GODOT_ENET
+ out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#else
+ out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host));
+#endif
+
+ return out;
+}
+
+int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), 0);
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0);
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0);
+#ifdef GODOT_ENET
+ return peer_map[p_peer_id]->address.port;
+#else
+ return peer_map[p_peer_id]->address.port;
+#endif
+}
+
+void NetworkedMultiplayerENet::set_transfer_channel(int p_channel) {
+
+ ERR_FAIL_COND(p_channel < -1 || p_channel >= channel_count);
+
+ if (p_channel == SYSCH_CONFIG) {
+ ERR_EXPLAIN("Channel " + itos(SYSCH_CONFIG) + " is reserved");
+ ERR_FAIL();
+ }
+ transfer_channel = p_channel;
+}
+
+int NetworkedMultiplayerENet::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void NetworkedMultiplayerENet::set_channel_count(int p_channel) {
+
+ ERR_FAIL_COND(active);
+ ERR_FAIL_COND(p_channel < SYSCH_MAX);
+ channel_count = p_channel;
+}
+
+int NetworkedMultiplayerENet::get_channel_count() const {
+ return channel_count;
+}
+
+void NetworkedMultiplayerENet::set_always_ordered(bool p_ordered) {
+ always_ordered = p_ordered;
+}
+
+bool NetworkedMultiplayerENet::is_always_ordered() const {
+ return always_ordered;
}
void NetworkedMultiplayerENet::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("create_client", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection);
+ ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "client_port"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &NetworkedMultiplayerENet::close_connection, DEFVAL(100));
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode);
ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode);
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip);
+ ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &NetworkedMultiplayerENet::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &NetworkedMultiplayerENet::get_peer_port);
+
+ ClassDB::bind_method(D_METHOD("get_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel);
+ ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &NetworkedMultiplayerENet::get_last_packet_channel);
+ ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &NetworkedMultiplayerENet::set_transfer_channel);
+ ClassDB::bind_method(D_METHOD("get_transfer_channel"), &NetworkedMultiplayerENet::get_transfer_channel);
+ ClassDB::bind_method(D_METHOD("set_channel_count", "channels"), &NetworkedMultiplayerENet::set_channel_count);
+ ClassDB::bind_method(D_METHOD("get_channel_count"), &NetworkedMultiplayerENet::get_channel_count);
+ ClassDB::bind_method(D_METHOD("set_always_ordered", "ordered"), &NetworkedMultiplayerENet::set_always_ordered);
+ ClassDB::bind_method(D_METHOD("is_always_ordered"), &NetworkedMultiplayerENet::is_always_ordered);
ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel"), "set_transfer_channel", "get_transfer_channel");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "channel_count"), "set_channel_count", "get_channel_count");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "always_ordered"), "set_always_ordered", "is_always_ordered");
BIND_ENUM_CONSTANT(COMPRESS_NONE);
BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);
@@ -681,6 +859,9 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet() {
target_peer = 0;
current_packet.packet = NULL;
transfer_mode = TRANSFER_MODE_RELIABLE;
+ channel_count = SYSCH_MAX;
+ transfer_channel = -1;
+ always_ordered = false;
connection_status = CONNECTION_DISCONNECTED;
compression_mode = COMPRESS_NONE;
enet_compressor.context = this;
@@ -696,7 +877,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
close_connection();
}
-// sets IP for ENet to bind when using create_server
+// Sets IP for ENet to bind when using create_server or create_client
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) {
ERR_FAIL_COND(!p_ip.is_valid() && !p_ip.is_wildcard());
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index 440e9b5400..705807d429 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -68,6 +68,9 @@ private:
int target_peer;
TransferMode transfer_mode;
+ int transfer_channel;
+ int channel_count;
+ bool always_ordered;
ENetEvent event;
ENetPeer *peer;
@@ -83,6 +86,7 @@ private:
ENetPacket *packet;
int from;
+ int channel;
};
CompressionMode compression_mode;
@@ -115,10 +119,15 @@ public:
virtual int get_packet_peer() const;
+ virtual IP_Address get_peer_address(int p_peer_id) const;
+ virtual int get_peer_port(int p_peer_id) const;
+
Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
- Error create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_client_port = 0);
+
+ void close_connection(uint32_t wait_usec = 100);
- void close_connection();
+ void disconnect_peer(int p_peer, bool now = false);
virtual void poll();
@@ -140,6 +149,15 @@ public:
void set_compression_mode(CompressionMode p_mode);
CompressionMode get_compression_mode() const;
+ int get_packet_channel() const;
+ int get_last_packet_channel() const;
+ void set_transfer_channel(int p_channel);
+ int get_transfer_channel() const;
+ void set_channel_count(int p_channel);
+ int get_channel_count() const;
+ void set_always_ordered(bool p_ordered);
+ bool is_always_ordered() const;
+
NetworkedMultiplayerENet();
~NetworkedMultiplayerENet();
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/SCsub b/modules/freetype/SCsub
index f69b632e76..301f218361 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -17,18 +17,15 @@ if env['builtin_freetype']:
"src/base/ftbitmap.c",
"src/base/ftcid.c",
"src/base/ftdebug.c",
- "src/base/ftfntfmt.c",
"src/base/ftfstype.c",
"src/base/ftgasp.c",
"src/base/ftglyph.c",
"src/base/ftgxval.c",
"src/base/ftinit.c",
- "src/base/ftlcdfil.c",
"src/base/ftmm.c",
"src/base/ftotval.c",
"src/base/ftpatent.c",
"src/base/ftpfr.c",
- "src/base/ftpic.c",
"src/base/ftstroke.c",
"src/base/ftsynth.c",
"src/base/ftsystem.c",
@@ -49,7 +46,6 @@ if env['builtin_freetype']:
"src/pshinter/pshinter.c",
"src/psnames/psnames.c",
"src/raster/raster.c",
- "src/sfnt/sfnt.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -58,9 +54,18 @@ if env['builtin_freetype']:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- # Include header for UWP to fix build issues
- if "platform" in env and env["platform"] == "uwp":
- env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
+ sfnt = thirdparty_dir + 'src/sfnt/sfnt.c'
+
+ if 'platform' in env:
+ if env['platform'] == 'uwp':
+ # Include header for UWP to fix build issues
+ env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
+ elif env['platform'] == 'javascript':
+ # Forcibly undefine this macro so SIMD is not used in this file,
+ # since currently unsuported in WASM
+ sfnt = env.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__'])
+
+ thirdparty_sources += [sfnt]
env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
@@ -87,6 +92,6 @@ if env['builtin_freetype']:
# Godot source files
env.add_source_files(env.modules_sources, "*.cpp")
-env.Append(CCFLAGS=['-DFREETYPE_ENABLED'])
+env.Append(CCFLAGS=['-DFREETYPE_ENABLED', '-DFT_CONFIG_OPTION_USE_PNG'])
Export('env')
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 c92c3f30a2..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")
@@ -23,17 +25,19 @@ def _build_gdnative_api_struct_header(api):
'\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;'
]
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
gdnative_api_init_macro.append(
'\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name))
gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;')
- gdnative_api_init_macro.append('\tfor (int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ')
+ gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ')
gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {')
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
gdnative_api_init_macro.append(
- '\t\t\tcase GDNATIVE_EXT_%s:' % api['extensions'][name]['type'])
+ '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type'])
gdnative_api_init_macro.append(
'\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)'
' _gdnative_wrapper_api_struct->extensions[i];'.format(name))
@@ -47,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>',
@@ -61,24 +66,36 @@ def _build_gdnative_api_struct_header(api):
'\tGDNATIVE_' + api['core']['type'] + ','
]
- for name in api['extensions']:
- out += ['\tGDNATIVE_EXT_' + api['extensions'][name]['type'] + ',']
+ for ext in api['extensions']:
+ out += ['\tGDNATIVE_EXT_' + ext['type'] + ',']
out += ['};', '']
- for name in api['extensions']:
- out += [
- 'typedef struct godot_gdnative_ext_' + name + '_api_struct {',
+
+ def generate_extension_struct(name, ext, include_version=True):
+ ret_val = []
+ if ext['next']:
+ ret_val += generate_extension_struct(name, ext['next'])
+
+ ret_val += [
+ 'typedef struct godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct {',
'\tunsigned int type;',
'\tgodot_gdnative_api_version version;',
'\tconst godot_gdnative_api_struct *next;'
]
- for funcdef in api['extensions'][name]['api']:
+ for funcdef in ext['api']:
args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
- out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+ ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ ret_val += ['} godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct;', '']
+
+ return ret_val
- out += ['} godot_gdnative_ext_' + name + '_api_struct;', '']
+
+ for ext in api['extensions']:
+ name = ext['name']
+ out += generate_extension_struct(name, ext, False)
out += [
'typedef struct godot_gdnative_core_api_struct {',
@@ -113,22 +130,41 @@ def _build_gdnative_api_struct_source(api):
''
]
- for name in api['extensions']:
- out += [
- 'extern const godot_gdnative_ext_' + name + '_api_struct api_extension_' + name + '_struct = {',
- '\tGDNATIVE_EXT_' + api['extensions'][name]['type'] + ',',
- '\t{' + str(api['extensions'][name]['version']['major']) + ', ' + str(api['extensions'][name]['version']['minor']) + '},',
- '\tNULL,'
+ def get_extension_struct_name(name, ext, include_version=True):
+ return 'godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct'
+
+ def get_extension_struct_instance_name(name, ext, include_version=True):
+ return 'api_extension_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_struct'
+
+ def get_extension_struct_definition(name, ext, include_version=True):
+
+ ret_val = []
+
+ if ext['next']:
+ ret_val += get_extension_struct_definition(name, ext['next'])
+
+ ret_val += [
+ 'extern const ' + get_extension_struct_name(name, ext, include_version) + ' ' + get_extension_struct_instance_name(name, ext, include_version) + ' = {',
+ '\tGDNATIVE_EXT_' + ext['type'] + ',',
+ '\t{' + str(ext['version']['major']) + ', ' + str(ext['version']['minor']) + '},',
+ '\t' + ('NULL' if not ext['next'] else ('(const godot_gdnative_api_struct *)&' + get_extension_struct_instance_name(name, ext['next']))) + ','
]
- for funcdef in api['extensions'][name]['api']:
- out.append('\t%s,' % funcdef['name'])
+ for funcdef in ext['api']:
+ ret_val.append('\t%s,' % funcdef['name'])
+
+ ret_val += ['};\n']
+
+ return ret_val
- out += ['};\n']
+ for ext in api['extensions']:
+ name = ext['name']
+ out += get_extension_struct_definition(name, ext, False)
out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {']
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,']
out += ['};\n']
@@ -161,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])
@@ -186,7 +222,8 @@ def _build_gdnative_wrapper_code(api):
'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;',
]
- for name in api['extensions']:
+ for ext in api['extensions']:
+ name = ext['name']
out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;')
out += ['']
@@ -204,8 +241,9 @@ def _build_gdnative_wrapper_code(api):
out.append('}')
out.append('')
- for name in api['extensions']:
- for funcdef in api['extensions'][name]['api']:
+ for ext in api['extensions']:
+ name = ext['name']
+ for funcdef in ext['api']:
args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
@@ -239,16 +277,16 @@ 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)
+#build 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/'])
if gd_wrapper_env['use_lto']:
if not env.msvc:
- gd_wrapper_env.Append(CCFLAGS=['--no-lto'])
- gd_wrapper_env.Append(LINKFLAGS=['--no-lto'])
+ gd_wrapper_env.Append(CCFLAGS=['-fno-lto'])
+ gd_wrapper_env.Append(LINKFLAGS=['-fno-lto'])
else:
gd_wrapper_env.Append(CCFLAGS=['/GL-'])
gd_wrapper_env.Append(LINKFLAGS=['/LTCG:OFF'])
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/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml
index bceb4f1f4c..be86ff0541 100644
--- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml
+++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0-stable">
+<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.1">
<brief_description>
GDNative wrapper for an ARVR interface
</brief_description>
diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml
index 7e4d956604..ca0457623f 100644
--- a/modules/gdnative/doc_classes/GDNative.xml
+++ b/modules/gdnative/doc_classes/GDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDNative" inherits="Reference" category="Core" version="3.0-stable">
+<class name="GDNative" inherits="Reference" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index a6874c9ae8..754a6d2514 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0-stable">
+<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
@@ -9,12 +9,6 @@
<demos>
</demos>
<methods>
- <method name="get_config_file">
- <return type="ConfigFile">
- </return>
- <description>
- </description>
- </method>
<method name="get_current_dependencies" qualifiers="const">
<return type="PoolStringArray">
</return>
@@ -29,6 +23,8 @@
</method>
</methods>
<members>
+ <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file">
+ </member>
<member name="load_once" type="bool" setter="set_load_once" getter="should_load_once">
</member>
<member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable">
diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml
index f713e4112e..1d3053244b 100644
--- a/modules/gdnative/doc_classes/NativeScript.xml
+++ b/modules/gdnative/doc_classes/NativeScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NativeScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="NativeScript" inherits="Script" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
@@ -9,10 +9,46 @@
<demos>
</demos>
<methods>
+ <method name="get_class_documentation" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the documentation string that was previously set with [code]godot_nativescript_set_class_documentation[/code].
+ </description>
+ </method>
+ <method name="get_method_documentation" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="method" type="String">
+ </argument>
+ <description>
+ Returns the documentation string that was previously set with [code]godot_nativescript_set_method_documentation[/code].
+ </description>
+ </method>
+ <method name="get_property_documentation" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ Returns the documentation string that was previously set with [code]godot_nativescript_set_property_documentation[/code].
+ </description>
+ </method>
+ <method name="get_signal_documentation" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="signal_name" type="String">
+ </argument>
+ <description>
+ Returns the documentation string that was previously set with [code]godot_nativescript_set_signal_documentation[/code].
+ </description>
+ </method>
<method name="new" qualifiers="vararg">
<return type="Object">
</return>
<description>
+ Constructs a new object of the base type with a script of this type already attached.
+ [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension.
</description>
</method>
</methods>
diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml
index fbdd8f09e6..27c6adae3f 100644
--- a/modules/gdnative/doc_classes/PluginScript.xml
+++ b/modules/gdnative/doc_classes/PluginScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="PluginScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="PluginScript" inherits="Script" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 42c3028f2c..e7ebcc73af 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -66,8 +66,169 @@ GDNativeLibrary::GDNativeLibrary() {
GDNativeLibrary::~GDNativeLibrary() {
}
+bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_property) {
+
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ config_file->set_value("entry", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ config_file->set_value("dependencies", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const {
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ r_property = config_file->get_value("entry", key);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ r_property = config_file->get_value("dependencies", key);
+
+ return true;
+ }
+
+ return false;
+}
+
+void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
+ // set entries
+ List<String> entry_key_list;
+
+ if (config_file->has_section("entry"))
+ config_file->get_section_keys("entry", &entry_key_list);
+
+ for (List<String>::Element *E = entry_key_list.front(); E; E = E->next()) {
+ String key = E->get();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "entry/" + key;
+
+ p_list->push_back(prop);
+ }
+
+ // set dependencies
+ List<String> dependency_key_list;
+
+ if (config_file->has_section("dependencies"))
+ config_file->get_section_keys("dependencies", &dependency_key_list);
+
+ for (List<String>::Element *E = dependency_key_list.front(); E; E = E->next()) {
+ String key = E->get();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "dependency/" + key;
+
+ p_list->push_back(prop);
+ }
+}
+
+void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
+
+ set_singleton(p_config_file->get_value("general", "singleton", default_singleton));
+ set_load_once(p_config_file->get_value("general", "load_once", default_load_once));
+ set_symbol_prefix(p_config_file->get_value("general", "symbol_prefix", default_symbol_prefix));
+ set_reloadable(p_config_file->get_value("general", "reloadable", default_reloadable));
+
+ String entry_lib_path;
+ {
+
+ List<String> entry_keys;
+
+ if (p_config_file->has_section("entry"))
+ p_config_file->get_section_keys("entry", &entry_keys);
+
+ for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
+ String key = E->get();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ entry_lib_path = p_config_file->get_value("entry", key);
+ break;
+ }
+ }
+
+ Vector<String> dependency_paths;
+ {
+
+ List<String> dependency_keys;
+
+ if (p_config_file->has_section("dependencies"))
+ p_config_file->get_section_keys("dependencies", &dependency_keys);
+
+ for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
+ String key = E->get();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ dependency_paths = p_config_file->get_value("dependencies", key);
+ break;
+ }
+ }
+
+ current_library_path = entry_lib_path;
+ current_dependencies = dependency_paths;
+}
+
void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file);
+ ClassDB::bind_method(D_METHOD("set_config_file", "config_file"), &GDNativeLibrary::set_config_file);
ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path);
ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies);
@@ -82,6 +243,8 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
@@ -306,7 +469,9 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced
godot_variant result = E->get()(procedure_handle, (godot_array *)&p_arguments);
- return *(Variant *)&result;
+ Variant res = *(Variant *)&result;
+ godot_variant_destroy(&result);
+ return res;
}
Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) {
@@ -337,73 +502,7 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or
*r_error = err;
}
- lib->set_singleton(config->get_value("general", "singleton", default_singleton));
- lib->set_load_once(config->get_value("general", "load_once", default_load_once));
- lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix));
- lib->set_reloadable(config->get_value("general", "reloadable", default_reloadable));
-
- String entry_lib_path;
- {
-
- List<String> entry_keys;
- config->get_section_keys("entry", &entry_keys);
-
- for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
- String key = E->get();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- entry_lib_path = config->get_value("entry", key);
- break;
- }
- }
-
- Vector<String> dependency_paths;
- {
-
- List<String> dependency_keys;
- config->get_section_keys("dependencies", &dependency_keys);
-
- for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
- String key = E->get();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- dependency_paths = config->get_value("dependencies", key);
- break;
- }
- }
-
- lib->current_library_path = entry_lib_path;
- lib->current_dependencies = dependency_paths;
+ lib->set_config_file(config);
return lib;
}
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 3298ea950f..b17bb94f1c 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -66,8 +66,14 @@ public:
GDNativeLibrary();
~GDNativeLibrary();
+ virtual bool _set(const StringName &p_name, const Variant &p_property);
+ virtual bool _get(const StringName &p_name, Variant &r_property) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
_FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; }
+ void set_config_file(Ref<ConfigFile> p_config_file);
+
// things that change per-platform
// so there are no setters for this
_FORCE_INLINE_ String get_current_library_path() const {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 59a9c0b090..217fd87c3e 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5,6 +5,7 @@
"major": 1,
"minor": 0
},
+ "next": null,
"api": [
{
"name": "godot_color_new_rgba",
@@ -3963,7 +3964,7 @@
"name": "godot_variant_new_bool",
"return_type": "void",
"arguments": [
- ["godot_variant *", "p_v"],
+ ["godot_variant *", "r_dest"],
["const godot_bool", "p_b"]
]
},
@@ -5755,13 +5756,129 @@
}
]
},
- "extensions": {
- "nativescript": {
+ "extensions": [
+ {
+ "name": "nativescript",
"type": "NATIVESCRIPT",
"version": {
"major": 1,
"minor": 0
},
+ "next": {
+ "type": "NATIVESCRIPT",
+ "version": {
+ "major": 1,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_nativescript_set_method_argument_information",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_function_name"],
+ ["int", "p_num_args"],
+ ["const godot_method_arg *", "p_args"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_class_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_method_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_function_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_property_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_path"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_signal_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_signal_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_global_type_tag",
+ "return_type": "void",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"],
+ ["const void *", "p_type_tag"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_global_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_type_tag",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const void *", "p_type_tag"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ ["const godot_object *", "p_object"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_instance_binding_data_functions",
+ "return_type": "int",
+ "arguments": [
+ ["godot_instance_binding_functions", "p_binding_functions"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_unregister_instance_binding_data_functions",
+ "return_type": "void",
+ "arguments": [
+ ["int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_instance_binding_data",
+ "return_type": "void *",
+ "arguments": [
+ ["int", "p_idx"],
+ ["godot_object *", "p_object"]
+ ]
+ }
+ ]
+ },
"api": [
{
"name": "godot_nativescript_register_class",
@@ -5826,12 +5943,14 @@
}
]
},
- "pluginscript": {
+ {
+ "name": "pluginscript",
"type": "PLUGINSCRIPT",
"version": {
"major": 1,
"minor": 0
},
+ "next": null,
"api": [
{
"name": "godot_pluginscript_register_language",
@@ -5842,12 +5961,37 @@
}
]
},
- "arvr": {
- "type": "ARVR",
+ {
+ "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": 1
+ },
+ "next": null,
"api": [
{
"name": "godot_arvr_register_interface",
@@ -5937,5 +6081,5 @@
}
]
}
- }
+ ]
}
diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h
index 94bc2adc7d..04d2911d8b 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.h
+++ b/modules/gdnative/gdnative_library_editor_plugin.h
@@ -79,7 +79,7 @@ protected:
void _on_library_selected(const String &file);
void _on_dependencies_selected(const PoolStringArray &files);
void _on_filter_selected(int id);
- void _on_item_collapsed(Object *item);
+ void _on_item_collapsed(Object *p_item);
void _on_item_activated();
void _on_create_new_entry();
void _set_target_value(const String &section, const String &target, Variant file);
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/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h
index d2e8246bfb..6779dc4092 100644
--- a/modules/gdnative/include/gdnative/variant.h
+++ b/modules/gdnative/include/gdnative/variant.h
@@ -135,7 +135,7 @@ void GDAPI godot_variant_new_copy(godot_variant *r_dest, const godot_variant *p_
void GDAPI godot_variant_new_nil(godot_variant *r_dest);
-void GDAPI godot_variant_new_bool(godot_variant *p_v, const godot_bool p_b);
+void GDAPI godot_variant_new_bool(godot_variant *r_dest, const godot_bool p_b);
void GDAPI godot_variant_new_uint(godot_variant *r_dest, const uint64_t p_i);
void GDAPI godot_variant_new_int(godot_variant *r_dest, const int64_t p_i);
void GDAPI godot_variant_new_real(godot_variant *r_dest, const double p_r);
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 11017ae78d..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 {
@@ -185,6 +188,55 @@ void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const cha
void GDAPI *godot_nativescript_get_userdata(godot_object *p_instance);
+/*
+ *
+ *
+ * NativeScript 1.1
+ *
+ *
+ */
+
+// method registering with argument names
+
+typedef struct {
+ godot_string name;
+
+ godot_variant_type type;
+ godot_property_hint hint;
+ godot_string hint_string;
+} godot_method_arg;
+
+void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_method_arg *p_args);
+
+// documentation
+
+void GDAPI godot_nativescript_set_class_documentation(void *p_gdnative_handle, const char *p_name, godot_string p_documentation);
+void GDAPI godot_nativescript_set_method_documentation(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_string p_documentation);
+void GDAPI godot_nativescript_set_property_documentation(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_string p_documentation);
+void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, const char *p_name, const char *p_signal_name, godot_string p_documentation);
+
+// type tag API
+
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag);
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name);
+
+void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag);
+const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object);
+
+// instance binding API
+
+typedef struct {
+ GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *);
+ GDCALLINGCONV void (*free_instance_binding_data)(void *, void *);
+ void *data;
+ GDCALLINGCONV void (*free_func)(void *);
+} godot_instance_binding_functions;
+
+int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions);
+void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_idx);
+
+void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object);
+
#ifdef __cplusplus
}
#endif
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/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
index 671be3bbb9..d1671c014e 100644
--- a/modules/gdnative/include/pluginscript/godot_pluginscript.h
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -64,7 +64,7 @@ typedef struct {
//this is used by script languages that keep a reference counter of their own
//you can make make Ref<> not die when it reaches zero, so deleting the reference
//depends entirely from the script.
- // Note: You can set thoses function pointer to NULL if not needed.
+ // Note: You can set those function pointer to NULL if not needed.
void (*refcount_incremented)(godot_pluginscript_instance_data *p_data);
bool (*refcount_decremented)(godot_pluginscript_instance_data *p_data); // return true if it can die
} godot_pluginscript_instance_desc;
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index b4f7e1555e..ace2ecac5c 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -106,7 +106,7 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
if (!E) {
- ERR_EXPLAIN("Attempt to register method on non-existant class!");
+ ERR_EXPLAIN("Attempted to register method on non-existent class!");
ERR_FAIL();
}
@@ -125,7 +125,7 @@ void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const c
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
if (!E) {
- ERR_EXPLAIN("Attempt to register method on non-existant class!");
+ ERR_EXPLAIN("Attempted to register method on non-existent class!");
ERR_FAIL();
}
@@ -150,7 +150,7 @@ void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const cha
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
if (!E) {
- ERR_EXPLAIN("Attempt to register method on non-existant class!");
+ ERR_EXPLAIN("Attempted to register method on non-existent class!");
ERR_FAIL();
}
@@ -201,6 +201,170 @@ void GDAPI *godot_nativescript_get_userdata(godot_object *p_instance) {
return NULL;
}
+/*
+ *
+ *
+ * NativeScript 1.1
+ *
+ *
+ */
+
+void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_method_arg *p_args) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to add argument information for a method on a non-existent class!");
+ ERR_FAIL();
+ }
+
+ Map<StringName, NativeScriptDesc::Method>::Element *method = E->get().methods.find(p_function_name);
+ if (!method) {
+ ERR_EXPLAIN("Attempted to add argument information to non-existent method!");
+ ERR_FAIL();
+ }
+
+ MethodInfo *method_information = &method->get().info;
+
+ List<PropertyInfo> args;
+
+ for (int i = 0; i < p_num_args; i++) {
+ godot_method_arg arg = p_args[i];
+ String name = *(String *)&arg.name;
+ String hint_string = *(String *)&arg.hint_string;
+
+ Variant::Type type = (Variant::Type)arg.type;
+ PropertyHint hint = (PropertyHint)arg.hint;
+
+ args.push_back(PropertyInfo(type, p_name, hint, hint_string));
+ }
+
+ method_information->arguments = args;
+}
+
+void GDAPI godot_nativescript_set_class_documentation(void *p_gdnative_handle, const char *p_name, godot_string p_documentation) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to add documentation to a non-existent class!");
+ ERR_FAIL();
+ }
+
+ E->get().documentation = *(String *)&p_documentation;
+}
+
+void GDAPI godot_nativescript_set_method_documentation(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_string p_documentation) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to add documentation to a method on a non-existent class!");
+ ERR_FAIL();
+ }
+
+ Map<StringName, NativeScriptDesc::Method>::Element *method = E->get().methods.find(p_function_name);
+ if (!method) {
+ ERR_EXPLAIN("Attempted to add documentatino to non-existent method!");
+ ERR_FAIL();
+ }
+
+ method->get().documentation = *(String *)&p_documentation;
+}
+
+void GDAPI godot_nativescript_set_property_documentation(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_string p_documentation) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to add documentation to a property on a non-existent class!");
+ ERR_FAIL();
+ }
+
+ OrderedHashMap<StringName, NativeScriptDesc::Property>::Element property = E->get().properties.find(p_path);
+ if (!property) {
+ ERR_EXPLAIN("Attempted to add documentation to non-existent property!");
+ ERR_FAIL();
+ }
+
+ property.get().documentation = *(String *)&p_documentation;
+}
+
+void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, const char *p_name, const char *p_signal_name, godot_string p_documentation) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to add documentation to a signal on a non-existent class!");
+ ERR_FAIL();
+ }
+
+ Map<StringName, NativeScriptDesc::Signal>::Element *signal = E->get().signals_.find(p_signal_name);
+ if (!signal) {
+ ERR_EXPLAIN("Attempted to add documentation to non-existent signal!");
+ ERR_FAIL();
+ }
+
+ signal->get().documentation = *(String *)&p_documentation;
+}
+
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag) {
+ NativeScriptLanguage::get_singleton()->set_global_type_tag(p_idx, StringName(p_name), p_type_tag);
+}
+
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name) {
+ return NativeScriptLanguage::get_singleton()->get_global_type_tag(p_idx, StringName(p_name));
+}
+
+void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag) {
+ String *s = (String *)p_gdnative_handle;
+
+ Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
+
+ if (!E) {
+ ERR_EXPLAIN("Attempted to set type tag on a non-existent class!");
+ ERR_FAIL();
+ }
+
+ E->get().type_tag = p_type_tag;
+}
+
+const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object) {
+
+ const Object *o = (Object *)p_object;
+
+ if (!o->get_script_instance()) {
+ return NULL;
+ } else {
+ NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr());
+ if (!script) {
+ return NULL;
+ }
+
+ if (script->get_script_desc())
+ return script->get_script_desc()->type_tag;
+ }
+
+ return NULL;
+}
+
+int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) {
+ return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions);
+}
+
+void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_idx) {
+ NativeScriptLanguage::get_singleton()->unregister_binding_functions(p_idx);
+}
+
+void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) {
+ return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index aaa7d634d1..d6abbc1bcf 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -55,12 +55,6 @@
#include "editor/editor_node.h"
#endif
-//
-//
-// Script stuff
-//
-//
-
void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_class_name", "class_name"), &NativeScript::set_class_name);
ClassDB::bind_method(D_METHOD("get_class_name"), &NativeScript::get_class_name);
@@ -68,6 +62,11 @@ void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_library", "library"), &NativeScript::set_library);
ClassDB::bind_method(D_METHOD("get_library"), &NativeScript::get_library);
+ ClassDB::bind_method(D_METHOD("get_class_documentation"), &NativeScript::get_class_documentation);
+ ClassDB::bind_method(D_METHOD("get_method_documentation", "method"), &NativeScript::get_method_documentation);
+ ClassDB::bind_method(D_METHOD("get_signal_documentation", "signal_name"), &NativeScript::get_signal_documentation);
+ ClassDB::bind_method(D_METHOD("get_property_documentation", "path"), &NativeScript::get_property_documentation);
+
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
@@ -358,14 +357,13 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
NativeScriptDesc *script_data = get_script_desc();
Set<StringName> existing_properties;
+ List<PropertyInfo>::Element *original_back = p_list->back();
while (script_data) {
- List<PropertyInfo>::Element *insert_position = p_list->front();
- bool insert_before = true;
+ List<PropertyInfo>::Element *insert_position = original_back;
for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
if (!existing_properties.has(E.key())) {
- insert_position = insert_before ? p_list->insert_before(insert_position, E.get().info) : p_list->insert_after(insert_position, E.get().info);
- insert_before = false;
+ insert_position = p_list->insert_after(insert_position, E.get().info);
existing_properties.insert(E.key());
}
}
@@ -373,6 +371,86 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
+String NativeScript::get_class_documentation() const {
+ NativeScriptDesc *script_data = get_script_desc();
+
+ if (!script_data) {
+ ERR_EXPLAIN("Attempt to get class documentation on invalid NativeScript");
+ ERR_FAIL_V("");
+ }
+
+ return script_data->documentation;
+}
+
+String NativeScript::get_method_documentation(const StringName &p_method) const {
+ NativeScriptDesc *script_data = get_script_desc();
+
+ if (!script_data) {
+ ERR_EXPLAIN("Attempt to get method documentation on invalid NativeScript");
+ ERR_FAIL_V("");
+ }
+
+ while (script_data) {
+
+ Map<StringName, NativeScriptDesc::Method>::Element *method = script_data->methods.find(p_method);
+
+ if (method) {
+ return method->get().documentation;
+ }
+
+ script_data = script_data->base_data;
+ }
+
+ ERR_EXPLAIN("Attempt to get method documentation for non-existent method");
+ ERR_FAIL_V("");
+}
+
+String NativeScript::get_signal_documentation(const StringName &p_signal_name) const {
+ NativeScriptDesc *script_data = get_script_desc();
+
+ if (!script_data) {
+ ERR_EXPLAIN("Attempt to get signal documentation on invalid NativeScript");
+ ERR_FAIL_V("");
+ }
+
+ while (script_data) {
+
+ Map<StringName, NativeScriptDesc::Signal>::Element *signal = script_data->signals_.find(p_signal_name);
+
+ if (signal) {
+ return signal->get().documentation;
+ }
+
+ script_data = script_data->base_data;
+ }
+
+ ERR_EXPLAIN("Attempt to get signal documentation for non-existent signal");
+ ERR_FAIL_V("");
+}
+
+String NativeScript::get_property_documentation(const StringName &p_path) const {
+ NativeScriptDesc *script_data = get_script_desc();
+
+ if (!script_data) {
+ ERR_EXPLAIN("Attempt to get property documentation on invalid NativeScript");
+ ERR_FAIL_V("");
+ }
+
+ while (script_data) {
+
+ OrderedHashMap<StringName, NativeScriptDesc::Property>::Element property = script_data->properties.find(p_path);
+
+ if (property) {
+ return property.get().documentation;
+ }
+
+ script_data = script_data->base_data;
+ }
+
+ ERR_EXPLAIN("Attempt to get property documentation for non-existent signal");
+ ERR_FAIL_V("");
+}
+
Variant NativeScript::_new(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
if (lib_path.empty() || class_name.empty() || library.is_null()) {
@@ -443,12 +521,6 @@ NativeScript::~NativeScript() {
#endif
}
- //
- //
- // ScriptInstance stuff
- //
- //
-
#define GET_SCRIPT_DESC() script->get_script_desc()
void NativeScriptInstance::_ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount) {
@@ -610,7 +682,7 @@ Variant::Type NativeScriptInstance::get_property_type(const StringName &p_name,
}
void NativeScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
- script->get_method_list(p_list);
+ script->get_script_method_list(p_list);
}
bool NativeScriptInstance::has_method(const StringName &p_method) const {
@@ -675,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();
@@ -685,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();
@@ -715,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() {
@@ -787,15 +865,12 @@ NativeScriptInstance::~NativeScriptInstance() {
}
}
-//
-//
-// ScriptingLanguage stuff
-//
-//
-
NativeScriptLanguage *NativeScriptLanguage::singleton;
void NativeScriptLanguage::_unload_stuff(bool p_reload) {
+
+ Map<String, Ref<GDNative> > erase_and_unload;
+
for (Map<String, Map<StringName, NativeScriptDesc> >::Element *L = library_classes.front(); L; L = L->next()) {
String lib_path = L->key();
@@ -824,6 +899,13 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
}
}
+ Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path);
+ Ref<GDNative> gdn;
+
+ if (E) {
+ gdn = E->get();
+ }
+
for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) {
// free property stuff first
@@ -848,12 +930,34 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
if (C->get().destroy_func.free_func)
C->get().destroy_func.free_func(C->get().destroy_func.method_data);
}
+
+ erase_and_unload.insert(lib_path, gdn);
+ }
+
+ for (Map<String, Ref<GDNative> >::Element *E = erase_and_unload.front(); E; E = E->next()) {
+ String lib_path = E->key();
+ Ref<GDNative> gdn = E->get();
+
+ library_classes.erase(lib_path);
+
+ if (gdn.is_valid() && gdn->get_library().is_valid()) {
+ Ref<GDNativeLibrary> lib = gdn->get_library();
+ void *terminate_fn;
+ Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true);
+
+ if (err == OK) {
+ void (*terminate)(void *) = (void (*)(void *))terminate_fn;
+
+ terminate((void *)&lib_path);
+ }
+ }
}
}
NativeScriptLanguage::NativeScriptLanguage() {
NativeScriptLanguage::singleton = this;
#ifndef NO_THREADS
+ has_objects_to_register = false;
mutex = Mutex::create();
#endif
}
@@ -1011,6 +1115,140 @@ int NativeScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, in
return 0;
}
+int NativeScriptLanguage::register_binding_functions(godot_instance_binding_functions p_binding_functions) {
+
+ // find index
+
+ int idx = -1;
+
+ for (int i = 0; i < binding_functions.size(); i++) {
+ if (!binding_functions[i].first) {
+ // free, we'll take it
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1) {
+ idx = binding_functions.size();
+ binding_functions.resize(idx + 1);
+ }
+
+ // set the functions
+ binding_functions[idx].first = true;
+ binding_functions[idx].second = p_binding_functions;
+
+ return idx;
+}
+
+void NativeScriptLanguage::unregister_binding_functions(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, binding_functions.size());
+
+ for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) {
+ Vector<void *> &binding_data = *E->get();
+
+ if (binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data)
+ binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]);
+ }
+
+ binding_functions[p_idx].first = false;
+
+ if (binding_functions[p_idx].second.free_func)
+ binding_functions[p_idx].second.free_func(binding_functions[p_idx].second.data);
+}
+
+void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_object) {
+ ERR_FAIL_INDEX_V(p_idx, binding_functions.size(), NULL);
+
+ if (!binding_functions[p_idx].first) {
+ ERR_EXPLAIN("Tried to get binding data for a nativescript binding that does not exist");
+ ERR_FAIL_V(NULL);
+ }
+
+ Vector<void *> *binding_data = (Vector<void *> *)p_object->get_script_instance_binding(lang_idx);
+
+ if (!binding_data)
+ return NULL; // should never happen.
+
+ if (binding_data->size() <= p_idx) {
+ // okay, add new elements here.
+ int old_size = binding_data->size();
+
+ binding_data->resize(p_idx + 1);
+
+ for (int i = old_size; i <= p_idx; i++) {
+ (*binding_data)[i] = NULL;
+ }
+ }
+
+ if (!(*binding_data)[p_idx]) {
+
+ const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+
+ // no binding data yet, soooooo alloc new one \o/
+ (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
+ }
+
+ return (*binding_data)[p_idx];
+}
+
+void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) {
+
+ Vector<void *> *binding_data = new Vector<void *>;
+
+ binding_data->resize(binding_functions.size());
+
+ for (int i = 0; i < binding_functions.size(); i++) {
+ (*binding_data)[i] = NULL;
+ }
+
+ binding_instances.insert(binding_data);
+
+ return (void *)binding_data;
+}
+
+void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
+
+ if (!p_data)
+ return;
+
+ Vector<void *> &binding_data = *(Vector<void *> *)p_data;
+
+ for (int i = 0; i < binding_data.size(); i++) {
+ if (!binding_data[i])
+ continue;
+
+ if (binding_functions[i].first && binding_functions[i].second.free_instance_binding_data) {
+ binding_functions[i].second.free_instance_binding_data(binding_functions[i].second.data, binding_data[i]);
+ }
+ }
+
+ binding_instances.erase(&binding_data);
+
+ delete &binding_data;
+}
+
+void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) {
+ if (!global_type_tags.has(p_idx)) {
+ global_type_tags.insert(p_idx, HashMap<StringName, const void *>());
+ }
+
+ HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ tags.set(p_class_name, p_type_tag);
+}
+
+const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_class_name) const {
+ if (!global_type_tags.has(p_idx))
+ return NULL;
+
+ const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ const void *tag = tags.get(p_class_name);
+
+ return tag;
+}
+
#ifndef NO_THREADS
void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) {
MutexLock lock(mutex);
@@ -1148,6 +1386,7 @@ void NativeReloadNode::_notification(int p_what) {
MutexLock lock(NSL->mutex);
#endif
NSL->_unload_stuff(true);
+
for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
Ref<GDNative> gdn = L->get();
@@ -1161,7 +1400,6 @@ void NativeReloadNode::_notification(int p_what) {
}
gdn->terminate();
- NSL->library_classes.erase(L->key());
}
unloaded = true;
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index ac94c84bc4..b47962dc37 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -36,6 +36,7 @@
#include "core/self_list.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
+#include "oa_hash_map.h"
#include "ordered_hash_map.h"
#include "os/thread_safe.h"
#include "scene/main/node.h"
@@ -53,6 +54,7 @@ struct NativeScriptDesc {
godot_instance_method method;
MethodInfo info;
int rpc_mode;
+ String documentation;
};
struct Property {
godot_property_set_func setter;
@@ -60,12 +62,16 @@ struct NativeScriptDesc {
PropertyInfo info;
Variant default_value;
int rset_mode;
+ String documentation;
};
struct Signal {
MethodInfo signal;
+ String documentation;
};
+ String documentation;
+
Map<StringName, Method> methods;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
@@ -75,6 +81,8 @@ struct NativeScriptDesc {
godot_instance_create_func create_func;
godot_instance_destroy_func destroy_func;
+ const void *type_tag;
+
bool is_tool;
inline NativeScriptDesc() :
@@ -82,7 +90,9 @@ struct NativeScriptDesc {
properties(),
signals_(),
base(),
- base_native_type() {
+ base_native_type(),
+ documentation(),
+ type_tag(NULL) {
zeromem(&create_func, sizeof(godot_instance_create_func));
zeromem(&destroy_func, sizeof(godot_instance_destroy_func));
}
@@ -154,6 +164,11 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+ String get_class_documentation() const;
+ String get_method_documentation(const StringName &p_method) const;
+ String get_signal_documentation(const StringName &p_signal_name) const;
+ String get_property_documentation(const StringName &p_path) const;
+
Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
NativeScript();
@@ -181,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);
@@ -204,6 +219,7 @@ class NativeScriptLanguage : public ScriptLanguage {
private:
static NativeScriptLanguage *singleton;
+ int lang_idx;
void _unload_stuff(bool p_reload = false);
@@ -222,6 +238,11 @@ private:
void call_libraries_cb(const StringName &name);
+ Vector<Pair<bool, godot_instance_binding_functions> > binding_functions;
+ Set<Vector<void *> *> binding_instances;
+
+ Map<int, HashMap<StringName, const void *> > global_type_tags;
+
public:
// These two maps must only be touched on the main thread
Map<String, Map<StringName, NativeScriptDesc> > library_classes;
@@ -232,6 +253,8 @@ public:
const StringName _init_call_type = "nativescript_init";
const StringName _init_call_name = "nativescript_init";
+ const StringName _terminate_call_name = "nativescript_terminate";
+
const StringName _noarg_call_type = "nativescript_no_arg";
const StringName _frame_call_name = "nativescript_frame";
@@ -250,6 +273,8 @@ public:
void _hacky_api_anchor();
+ _FORCE_INLINE_ void set_language_index(int p_idx) { lang_idx = p_idx; }
+
#ifndef NO_THREADS
virtual void thread_enter();
virtual void thread_exit();
@@ -293,6 +318,17 @@ public:
virtual void profiling_stop();
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max);
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max);
+
+ int register_binding_functions(godot_instance_binding_functions p_binding_functions);
+ void unregister_binding_functions(int p_idx);
+
+ void *get_instance_binding_data(int p_idx, Object *p_object);
+
+ virtual void *alloc_instance_binding_data(Object *p_object);
+ virtual void free_instance_binding_data(void *p_data);
+
+ void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag);
+ const void *get_global_type_tag(int p_idx, StringName p_class_name) const;
};
inline NativeScriptDesc *NativeScript::get_script_desc() const {
diff --git a/modules/gdnative/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp
index cb55a13b3e..9a0e764391 100644
--- a/modules/gdnative/nativescript/register_types.cpp
+++ b/modules/gdnative/nativescript/register_types.cpp
@@ -47,6 +47,7 @@ void register_nativescript_types() {
ClassDB::register_class<NativeScript>();
+ native_script_language->set_language_index(ScriptServer::get_language_count());
ScriptServer::register_language(native_script_language);
resource_saver_gdns = memnew(ResourceFormatSaverNativeScript);
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 6729eecb32..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
@@ -112,12 +112,12 @@ public:
virtual void update_exports();
virtual void get_script_method_list(List<MethodInfo> *r_methods) const;
- virtual void get_script_property_list(List<PropertyInfo> *r_propertieslist) const;
+ virtual void get_script_property_list(List<PropertyInfo> *r_properties) const;
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/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
index 8888f9e157..924abf29df 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -64,7 +64,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->make_function is not mandatory
// desc->complete_code is not mandatory
// desc->auto_indent_code is not mandatory
- // desc->add_global_constant is not mandatory
+ ERR_FAIL_COND_V(!desc->add_global_constant, ERR_BUG);
// desc->debug_get_error is not mandatory
// desc->debug_get_stack_level_count is not mandatory
// desc->debug_get_stack_level_line is not mandatory
@@ -78,7 +78,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->profiling_stop is not mandatory
// desc->profiling_get_accumulated_data is not mandatory
// desc->profiling_get_frame_data is not mandatory
- // desc->frame is not mandatory
+ // desc->profiling_frame is not mandatory
ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG);
ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG);
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/SCsub b/modules/gdscript/SCsub
index 13870170a5..73f09f1659 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -7,4 +7,7 @@ env_gdscript = env_modules.Clone()
env_gdscript.add_source_files(env.modules_sources, "*.cpp")
+if env['tools']:
+ env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp")
+
Export('env')
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 59cb00e3f6..632970f8c0 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="GDScript" inherits="Script" category="Core" version="3.1">
<brief_description>
A script implemented in the GDScript programming language.
</brief_description>
@@ -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/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
index 8510136f68..c205cedef5 100644
--- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml
+++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable">
+<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.1">
<brief_description>
State of a function call after yielding.
</brief_description>
diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml
index 48826ec1e0..90935b5c22 100644
--- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml
+++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.0-stable">
+<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
new file mode 100644
index 0000000000..fedc510f01
--- /dev/null
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -0,0 +1,337 @@
+/*************************************************************************/
+/* gdscript_highlighter.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 "gdscript_highlighter.h"
+#include "../gdscript_tokenizer.h"
+#include "editor/editor_settings.h"
+#include "scene/gui/text_edit.h"
+
+inline bool _is_symbol(CharType c) {
+
+ return is_symbol(c);
+}
+
+static bool _is_text_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+static bool _is_whitespace(CharType c) {
+ return c == '\t' || c == ' ';
+}
+
+static bool _is_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+}
+
+static bool _is_number(CharType c) {
+ return (c >= '0' && c <= '9');
+}
+
+static bool _is_hex_symbol(CharType c) {
+ return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
+ Map<int, TextEdit::HighlighterInfo> color_map;
+
+ Type next_type = NONE;
+ Type current_type = NONE;
+ Type previous_type = NONE;
+
+ String previous_text = "";
+ int previous_column = 0;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool in_node_path = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = text_editor->_is_line_in_region(p_line);
+ int deregion = 0;
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line);
+ const String &str = text_editor->get_line(p_line);
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ TextEdit::HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else {
+ TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region);
+ if (in_region == cri.region && !cr.line_only) { //ignore otherwise
+ if (cri.end || cr.eq) {
+ deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length();
+ }
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ String word = str.substr(j, to - j);
+ Color col = Color();
+ if (text_editor->has_keyword_color(word)) {
+ col = text_editor->get_keyword_color(word);
+ } else if (text_editor->has_member_color(word)) {
+ col = text_editor->get_member_color(word);
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = Color(); //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+
+ if (col != Color()) {
+ in_keyword = true;
+ keyword_color = col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (!in_node_path && in_region == -1 && str[j] == '$') {
+ in_node_path = true;
+ } else if (in_region != -1 || (is_symbol && str[j] != '/')) {
+ in_node_path = false;
+ }
+
+ if (in_region >= 0) {
+ next_type = REGION;
+ color = text_editor->_get_color_region(in_region).color;
+ } else if (in_node_path) {
+ next_type = NODE_PATH;
+ color = node_path_color;
+ } else if (in_keyword) {
+ next_type = KEYWORD;
+ color = keyword_color;
+ } else if (in_member_variable) {
+ next_type = MEMBER;
+ color = member_color;
+ } else if (in_function_name) {
+ next_type = FUNCTION;
+
+ if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_FUNCTION)) {
+ color = function_definition_color;
+ } else {
+ color = function_color;
+ }
+ } else if (is_symbol) {
+ next_type = SYMBOL;
+ color = symbol_color;
+ } else if (is_number) {
+ next_type = NUMBER;
+ color = number_color;
+ } else {
+ next_type = IDENTIFIER;
+ }
+
+ if (next_type != current_type) {
+ if (current_type == NONE) {
+ current_type = next_type;
+ } else {
+ previous_type = current_type;
+ current_type = next_type;
+
+ // no need to store regions...
+ if (previous_type == REGION) {
+ previous_text = "";
+ previous_column = j;
+ } else {
+ String text = str.substr(previous_column, j - previous_column).strip_edges();
+ previous_column = j;
+
+ // ignore if just whitespace
+ if (text != "") {
+ previous_text = text;
+ }
+ }
+ }
+ }
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+ return color_map;
+}
+
+String GDScriptSyntaxHighlighter::get_name() {
+ return "GDScript";
+}
+
+List<String> GDScriptSyntaxHighlighter::get_supported_languages() {
+ List<String> languages;
+ languages.push_back("GDScript");
+ return languages;
+}
+
+void GDScriptSyntaxHighlighter::_update_cache() {
+ font_color = text_editor->get_color("font_color");
+ symbol_color = text_editor->get_color("symbol_color");
+ function_color = text_editor->get_color("function_color");
+ number_color = text_editor->get_color("number_color");
+ member_color = text_editor->get_color("member_variable_color");
+
+ 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() {
+ return memnew(GDScriptSyntaxHighlighter);
+}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
new file mode 100644
index 0000000000..0296ab7652
--- /dev/null
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -0,0 +1,70 @@
+/*************************************************************************/
+/* gdscript_highlighter.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 GDSCRIPT_HIGHLIGHTER_H
+#define GDSCRIPT_HIGHLIGHTER_H
+
+#include "scene/gui/text_edit.h"
+
+class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
+private:
+ enum Type {
+ NONE,
+ REGION,
+ NODE_PATH,
+ SYMBOL,
+ NUMBER,
+ FUNCTION,
+ KEYWORD,
+ MEMBER,
+ IDENTIFIER
+ };
+
+ // colours
+ Color font_color;
+ Color symbol_color;
+ Color function_color;
+ Color function_definition_color;
+ Color built_in_type_color;
+ Color number_color;
+ Color member_color;
+ Color node_path_color;
+
+public:
+ static SyntaxHighlighter *create();
+
+ virtual void _update_cache();
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
+ virtual String get_name();
+ virtual List<String> get_supported_languages();
+};
+
+#endif // GDSCRIPT_HIGHLIGHTER_H
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4e3ee4d22c..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() {
@@ -1333,6 +1333,15 @@ void GDScriptLanguage::add_global_constant(const StringName &p_variable, const V
_add_global(p_variable, p_value);
}
+void GDScriptLanguage::add_named_global_constant(const StringName &p_name, const Variant &p_value) {
+ named_globals[p_name] = p_value;
+}
+
+void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
+ ERR_FAIL_COND(!named_globals.has(p_name));
+ named_globals.erase(p_name);
+}
+
void GDScriptLanguage::init() {
//populate global constants
@@ -1760,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 9566e3b32e..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();
@@ -262,6 +262,7 @@ class GDScriptLanguage : public ScriptLanguage {
Variant *_global_array;
Vector<Variant> global_array;
Map<StringName, int> globals;
+ Map<StringName, Variant> named_globals;
struct CallLevel {
@@ -369,7 +370,8 @@ public:
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
- _FORCE_INLINE_ const Map<StringName, int> &get_global_map() { return globals; }
+ _FORCE_INLINE_ const Map<StringName, int> &get_global_map() const { return globals; }
+ _FORCE_INLINE_ const Map<StringName, Variant> &get_named_globals_map() const { return named_globals; }
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
@@ -403,6 +405,8 @@ public:
virtual String _get_indentation() const;
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
virtual void add_global_constant(const StringName &p_variable, const Variant &p_value);
+ virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value);
+ virtual void remove_named_global_constant(const StringName &p_name);
/* DEBUGGER FUNCTIONS */
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 1649fb52f2..5c834966c5 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -181,7 +181,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
//wait, identifier could be a local variable or something else... careful here, must reference properly
//as stack may be more interesting to work with
- //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot.
+ //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases performance a lot.
const GDScriptParser::IdentifierNode *in = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
@@ -278,6 +278,18 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
}
+#ifdef TOOLS_ENABLED
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
+
+ int idx = codegen.named_globals.find(identifier);
+ if (idx == -1) {
+ idx = codegen.named_globals.size();
+ codegen.named_globals.push_back(identifier);
+ }
+ return idx | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ }
+#endif
+
//not found, error
_set_error("Identifier not found: " + String(identifier), p_expression);
@@ -726,6 +738,9 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_NEG: {
if (!_create_unary_operator(codegen, on, Variant::OP_NEGATE, p_stack_level)) return -1;
} break;
+ case GDScriptParser::OperatorNode::OP_POS: {
+ if (!_create_unary_operator(codegen, on, Variant::OP_POSITIVE, p_stack_level)) return -1;
+ } break;
case GDScriptParser::OperatorNode::OP_NOT: {
if (!_create_unary_operator(codegen, on, Variant::OP_NOT, p_stack_level)) return -1;
} break;
@@ -1511,6 +1526,18 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->_global_names_count = 0;
}
+#ifdef TOOLS_ENABLED
+ // Named globals
+ if (codegen.named_globals.size()) {
+ gdfunc->named_globals.resize(codegen.named_globals.size());
+ gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
+ for (int i = 0; i < codegen.named_globals.size(); i++) {
+ gdfunc->named_globals[i] = codegen.named_globals[i];
+ }
+ gdfunc->_named_globals_count = gdfunc->named_globals.size();
+ }
+#endif
+
if (codegen.opcodes.size()) {
gdfunc->code = codegen.opcodes;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 62aafdbe01..237b0de9e7 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -94,6 +94,9 @@ class GDScriptCompiler {
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
Map<StringName, int> name_map;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
int get_name_map_pos(const StringName &p_identifier) {
int ret;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index c4269ab4a9..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 every time the node is added to the scene.\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()));
@@ -369,8 +376,8 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
mi.name = "yield";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
mi.arguments.push_back(PropertyInfo(Variant::STRING, "signal"));
- mi.default_arguments.push_back(Variant::NIL);
- mi.default_arguments.push_back(Variant::STRING);
+ mi.default_arguments.push_back(Variant());
+ mi.default_arguments.push_back(String());
mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "GDScriptFunctionState");
p_functions->push_back(mi);
}
@@ -410,15 +417,13 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
String s = "func " + p_name + "(";
if (p_args.size()) {
- s += " ";
for (int i = 0; i < p_args.size(); i++) {
if (i > 0)
s += ", ";
s += p_args[i].get_slice(":", 0);
}
- s += " ";
}
- s += "):\n" + _get_indentation() + "pass # replace with function body\n";
+ s += "):\n" + _get_indentation() + "pass # Replace with function body.\n";
return s;
}
@@ -432,6 +437,9 @@ struct GDScriptCompletionIdentifier {
Ref<GDScript> script;
Variant::Type type;
Variant value; //im case there is a value, also return it
+
+ GDScriptCompletionIdentifier() :
+ type(Variant::NIL) {}
};
static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) {
@@ -553,9 +561,7 @@ static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) {
static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) {
- //eeh...
GDScriptCompletionIdentifier id;
- id.type = Variant::NIL;
REF pc = _get_parent_class(context);
if (!pc.is_valid()) {
@@ -1335,13 +1341,23 @@ static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p
for (int i = 0; i < context.block->statements.size(); i++) {
- if (context.block->statements[i]->line > p_line)
+ GDScriptParser::Node *statement = context.block->statements[i];
+ if (statement->line > p_line)
continue;
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
+ GDScriptParser::BlockNode::Type statementType = statement->type;
+ if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]);
+ const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement);
result.insert(lv->name.operator String());
+ } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) {
+
+ const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
+ if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) {
+
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]);
+ result.insert(id->name.operator String());
+ }
}
}
}
@@ -1513,6 +1529,13 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo
result.insert(_type_names[i]);
}
+ List<String> reserved_words;
+ GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words);
+
+ for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) {
+ result.insert(E->get());
+ }
+
//autoload singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2620,6 +2643,13 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
+ if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
GDScriptParser p;
p.parse(p_code, p_base_path, false, "", true);
@@ -2633,6 +2663,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
context.function = p.get_completion_function();
context.base = p_owner;
context.base_path = p_base_path;
+
+ if (context._class && context._class->extends_class.size() > 0) {
+ bool success = false;
+ ClassDB::get_integer_constant(context._class->extends_class[0], p_symbol, &success);
+ if (success) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = context._class->extends_class[0];
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
bool isfunction = false;
switch (p.get_completion_type()) {
@@ -2852,7 +2894,24 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} else {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ /*
+ // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope
+ // We cannot determine the exact nature of the identifier here
+ // Otherwise these codes would work
+ StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
+ if (enumName != NULL) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = enumName;
+ return OK;
+ }
+ else {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = p_symbol;
+ return OK;
+ }*/
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE;
r_result.class_name = "@GlobalScope";
r_result.class_member = p_symbol;
return OK;
@@ -2915,6 +2974,14 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
+ StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true);
+ if (enumName != StringName()) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = t.obj_type;
+ r_result.class_member = enumName;
+ return OK;
+ }
+
bool success;
ClassDB::get_integer_constant(t.obj_type, p_symbol, &success);
if (success) {
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index a2f449909f..61130cb58f 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -86,7 +86,7 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
o = o->_owner;
}
- ERR_EXPLAIN("GDScriptCompiler bug..");
+ ERR_EXPLAIN("GDScriptCompiler bug...");
ERR_FAIL_V(NULL);
} break;
case ADDR_TYPE_LOCAL_CONSTANT: {
@@ -108,6 +108,21 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
#endif
return &GDScriptLanguage::get_singleton()->get_global_array()[address];
} break;
+#ifdef TOOLS_ENABLED
+ case ADDR_TYPE_NAMED_GLOBAL: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _named_globals_count, NULL);
+#endif
+ StringName id = _named_globals_ptr[address];
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
+ return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
+ } else {
+ r_error = "Autoload singleton '" + String(id) + "' has been removed.";
+ return NULL;
+ }
+ } break;
+#endif
case ADDR_TYPE_NIL: {
return &nil;
} break;
@@ -1311,9 +1326,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
}
-#endif
if (ScriptDebugger::get_singleton())
GDScriptLanguage::get_singleton()->exit_function();
+#endif
if (_stack_size) {
//free stack
@@ -1440,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;
@@ -1535,15 +1550,21 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
// then the function did yield again after resuming.
if (ret.is_ref()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
- if (gdfs && gdfs->function == function)
+ if (gdfs && gdfs->function == function) {
completed = false;
+ gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ }
}
function = NULL; //cleaned up;
state.result = Variant();
if (completed) {
- emit_signal("completed", ret);
+ GDScriptFunctionState *state = this;
+ while (state != NULL) {
+ state->emit_signal("completed", ret);
+ state = *(state->previous_state);
+ }
}
return ret;
@@ -1591,15 +1612,21 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
// then the function did yield again after resuming.
if (ret.is_ref()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
- if (gdfs && gdfs->function == function)
+ if (gdfs && gdfs->function == function) {
completed = false;
+ gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ }
}
function = NULL; //cleaned up;
state.result = Variant();
if (completed) {
- emit_signal("completed", ret);
+ GDScriptFunctionState *state = this;
+ while (state != NULL) {
+ state->emit_signal("completed", ret);
+ state = *(state->previous_state);
+ }
}
return ret;
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 9310444c7a..836325f0fe 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -92,15 +92,8 @@ public:
ADDR_TYPE_STACK = 5,
ADDR_TYPE_STACK_VARIABLE = 6,
ADDR_TYPE_GLOBAL = 7,
- ADDR_TYPE_NIL = 8
- };
-
- enum RPCMode {
- RPC_DISABLED,
- RPC_ENABLED,
- RPC_SYNC,
- RPC_SYNC_MASTER,
- RPC_SYNC_SLAVE
+ ADDR_TYPE_NAMED_GLOBAL = 8,
+ ADDR_TYPE_NIL = 9
};
struct StackDebug {
@@ -121,6 +114,10 @@ private:
int _constant_count;
const StringName *_global_names_ptr;
int _global_names_count;
+#ifdef TOOLS_ENABLED
+ const StringName *_named_globals_ptr;
+ int _named_globals_count;
+#endif
const int *_default_arg_ptr;
int _default_arg_count;
const int *_code_ptr;
@@ -130,13 +127,16 @@ private:
int _call_size;
int _initial_line;
bool _static;
- ScriptInstance::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
GDScript *_script;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
Vector<int> default_arguments;
Vector<int> code;
@@ -222,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();
};
@@ -234,6 +234,7 @@ class GDScriptFunctionState : public Reference {
GDScriptFunction *function;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
+ Ref<GDScriptFunctionState> previous_state;
protected:
static void _bind_methods();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index eceec27814..cccf09c58e 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -120,8 +120,10 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"Color8",
"ColorN",
"print_stack",
+ "get_stack",
"instance_from_id",
"len",
+ "is_instance_valid",
};
return _names[p_func];
@@ -329,10 +331,24 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
} break;
case MATH_LERP: {
VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
VALIDATE_ARG_NUM(2);
- r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
+ const double t = (double)*p_args[2];
+ switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::REAL) {
+ case Variant::VECTOR2: {
+ r_ret = ((Vector2)*p_args[0]).linear_interpolate((Vector2)*p_args[1], t);
+ } break;
+ case Variant::VECTOR3: {
+ r_ret = ((Vector3)*p_args[0]).linear_interpolate((Vector3)*p_args[1], t);
+ } break;
+ case Variant::COLOR: {
+ r_ret = ((Color)*p_args[0]).linear_interpolate((Color)*p_args[1], t);
+ } break;
+ default: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t);
+ } break;
+ }
} break;
case MATH_INVERSE_LERP: {
VALIDATE_ARG_COUNT(3);
@@ -358,13 +374,16 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
} break;
case MATH_RANDOMIZE: {
+ VALIDATE_ARG_COUNT(0);
Math::randomize();
r_ret = Variant();
} break;
case MATH_RAND: {
+ VALIDATE_ARG_COUNT(0);
r_ret = Math::rand();
} break;
case MATH_RANDF: {
+ VALIDATE_ARG_COUNT(0);
r_ret = Math::randf();
} break;
case MATH_RANDOM: {
@@ -593,7 +612,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
r_ret = String(result);
} break;
case TEXT_STR: {
+ if (p_arg_count < 1) {
+ r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ r_ret = Variant();
+ return;
+ }
String str;
for (int i = 0; i < p_arg_count; i++) {
@@ -1180,6 +1205,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
} break;
case PRINT_STACK: {
+ VALIDATE_ARG_COUNT(0);
ScriptLanguage *script = GDScriptLanguage::get_singleton();
for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
@@ -1188,6 +1214,22 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
};
} break;
+ case GET_STACK: {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ Array ret;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ r_ret = ret;
+ } break;
+
case INSTANCE_FROM_ID: {
VALIDATE_ARG_COUNT(1);
@@ -1267,6 +1309,17 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
}
} break;
+ case IS_INSTANCE_VALID: {
+
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_ret = false;
+ } else {
+ Object *obj = *p_args[0];
+ r_ret = ObjectDB::instance_validate(obj);
+ }
+
+ } break;
case FUNC_MAX: {
ERR_FAIL();
@@ -1478,7 +1531,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_LERP: {
- MethodInfo mi("lerp", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight"));
+ MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::REAL, "weight"));
mi.return_val.type = Variant::REAL;
return mi;
} break;
@@ -1760,12 +1813,14 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
case COLOR8: {
MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8"));
+ mi.default_arguments.push_back(255);
mi.return_val.type = Variant::COLOR;
return mi;
} break;
case COLORN: {
MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::REAL, "alpha"));
+ mi.default_arguments.push_back(1.0f);
mi.return_val.type = Variant::COLOR;
return mi;
} break;
@@ -1775,6 +1830,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::NIL;
return mi;
} break;
+ case GET_STACK: {
+ MethodInfo mi("get_stack");
+ mi.return_val.type = Variant::NIL;
+ return mi;
+ } break;
case INSTANCE_FROM_ID: {
MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
@@ -1786,7 +1846,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::INT;
return mi;
} break;
-
+ case IS_INSTANCE_VALID: {
+ MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance"));
+ mi.return_val.type = Variant::BOOL;
+ return mi;
+ } break;
case FUNC_MAX: {
ERR_FAIL_V(MethodInfo());
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index 1d54006084..899a7ebd25 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -111,8 +111,10 @@ public:
COLOR8,
COLORN,
PRINT_STACK,
+ GET_STACK,
INSTANCE_FROM_ID,
LEN,
+ IS_INSTANCE_VALID,
FUNC_MAX
};
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index c67214638d..9650563ee6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -95,8 +95,6 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
int indent = tokenizer->get_token_line_indent();
int current = tab_level.back()->get();
if (indent <= current) {
- print_line("current: " + itos(current) + " indent: " + itos(indent));
- print_line("less than current");
return false;
}
@@ -458,23 +456,26 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (!validating) {
//this can be too slow for just validating code
- if (for_completion && ScriptCodeCompletionCache::get_singleton()) {
+ if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
- } else { // essential; see issue 15902
+ } else if (!for_completion || FileAccess::exists(path)) {
res = ResourceLoader::load(path);
}
- if (!res.is_valid()) {
- _set_error("Can't preload resource at path: " + path);
- return NULL;
- }
} else {
if (!FileAccess::exists(path)) {
_set_error("Can't preload resource at path: " + path);
return NULL;
+ } else if (ScriptCodeCompletionCache::get_singleton()) {
+ res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
}
}
+ if (!res.is_valid()) {
+ _set_error("Can't preload resource at path: " + path);
+ return NULL;
+ }
+
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected ')' after 'preload' path");
return NULL;
@@ -578,18 +579,47 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (identifier == StringName()) {
- _set_error("Built-in type constant expected after '.'");
+ _set_error("Built-in type constant or static function expected after '.'");
return NULL;
}
if (!Variant::has_numeric_constant(bi_type, identifier)) {
- _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + ".");
- return NULL;
- }
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN &&
+ Variant::is_method_const(bi_type, identifier) &&
+ Variant::get_method_return_type(bi_type, identifier) == bi_type) {
+
+ tokenizer->advance();
+
+ OperatorNode *construct = alloc_node<OperatorNode>();
+ construct->op = OperatorNode::OP_CALL;
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype = bi_type;
+ construct->arguments.push_back(tn);
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op = OperatorNode::OP_CALL;
+ op->arguments.push_back(construct);
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = identifier;
+ op->arguments.push_back(id);
+
+ if (!_parse_arguments(op, op->arguments, p_static, true))
+ return NULL;
+
+ expr = op;
+ } else {
+
+ _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + ".");
+ return NULL;
+ }
+ } else {
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = Variant::get_numeric_constant_value(bi_type, identifier);
- expr = cn;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = Variant::get_numeric_constant_value(bi_type, identifier);
+ expr = cn;
+ }
} else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_PARENTHESIS_OPEN && tokenizer->is_token_literal()) {
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
@@ -686,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) {
@@ -940,7 +978,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (!expr) {
- ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is..");
+ ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is...");
ERR_FAIL_COND_V(!expr, NULL);
}
@@ -1275,7 +1313,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression..");
+ _set_error("Unexpected end of expression...");
return NULL;
}
}
@@ -1294,7 +1332,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} else if (is_ternary) {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1313,7 +1351,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1350,7 +1388,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1360,7 +1398,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1518,11 +1556,11 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
String errwhere;
if (op->arguments[0]->type == Node::TYPE_TYPE) {
TypeNode *tn = static_cast<TypeNode *>(op->arguments[0]);
- errwhere = "'" + Variant::get_type_name(tn->vtype) + "'' constructor";
+ errwhere = "'" + Variant::get_type_name(tn->vtype) + "' constructor";
} else {
GDScriptFunctions::Function func = static_cast<BuiltInFunctionNode *>(op->arguments[0])->function;
- errwhere = String("'") + GDScriptFunctions::get_func_name(func) + "'' intrinsic function";
+ errwhere = String("'") + GDScriptFunctions::get_func_name(func) + "' intrinsic function";
}
switch (ce.error) {
@@ -3335,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);
@@ -3410,6 +3448,22 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
+
+ String hint_prefix = "";
+ bool is_arrayed = false;
+
+ while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE &&
+ tokenizer->get_token_type() == Variant::ARRAY &&
+ tokenizer->get_token(1) == GDScriptTokenizer::TK_COMMA) {
+ tokenizer->advance(); // Array
+ tokenizer->advance(); // Comma
+ if (is_arrayed) {
+ hint_prefix += itos(Variant::ARRAY) + ":";
+ } else {
+ is_arrayed = true;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
Variant::Type type = tokenizer->get_token_type();
@@ -3425,28 +3479,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
tokenizer->advance();
- String hint_prefix = "";
-
- if (type == Variant::ARRAY && tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
- type = tokenizer->get_token_type();
-
- tokenizer->advance();
-
- if (type == Variant::ARRAY) {
- hint_prefix += itos(Variant::ARRAY) + ":";
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- }
- } else {
- hint_prefix += itos(type);
- break;
- }
- }
- }
-
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
// hint expected next!
tokenizer->advance();
@@ -3800,13 +3832,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
}
}
- if (current_export.type == Variant::ARRAY && !hint_prefix.empty()) {
- if (current_export.hint) {
- hint_prefix += "/" + itos(current_export.hint);
- }
- current_export.hint_string = hint_prefix + ":" + current_export.hint_string;
- current_export.hint = PROPERTY_HINT_NONE;
- }
} else {
@@ -3893,13 +3918,23 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
+ if (is_arrayed) {
+ hint_prefix += itos(current_export.type);
+ if (current_export.hint) {
+ hint_prefix += "/" + itos(current_export.hint);
+ }
+ current_export.hint_string = hint_prefix + ":" + current_export.hint_string;
+ current_export.hint = PROPERTY_HINT_TYPE_STRING;
+ current_export.type = Variant::ARRAY;
+ }
+
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;
}
@@ -3932,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;
@@ -3953,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: {
@@ -3973,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
@@ -3988,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: {
@@ -4018,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) {
@@ -4027,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;
@@ -4060,7 +4126,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member._export.type=Variant::DICTIONARY;
- } else*/ {
+ } else*/
+ {
if (subexpr->type != Node::TYPE_CONSTANT) {
@@ -4071,7 +4138,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
if (cn->value.get_type() == Variant::NIL) {
- _set_error("Can't accept a null constant expression for infering export type.");
+ _set_error("Can't accept a null constant expression for inferring export type.");
return;
}
member._export.type = cn->value.get_type();
@@ -4207,7 +4274,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
case GDScriptTokenizer::TK_PR_ENUM: {
- //mutiple constant declarations..
+ //multiple constant declarations..
int last_assign = -1; // Incremented by 1 right before the assingment.
String enum_name;
@@ -4358,8 +4425,6 @@ Error GDScriptParser::_parse(const String &p_base_path) {
base_path = p_base_path;
- clear();
-
//assume class
ClassNode *main_class = alloc_node<ClassNode>();
main_class->initializer = alloc_node<BlockNode>();
@@ -4384,17 +4449,7 @@ Error GDScriptParser::_parse(const String &p_base_path) {
Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path, const String &p_self_path) {
- for_completion = false;
- validating = false;
- completion_type = COMPLETION_NONE;
- completion_node = NULL;
- completion_class = NULL;
- completion_function = NULL;
- completion_block = NULL;
- completion_found = false;
- current_block = NULL;
- current_class = NULL;
- current_function = NULL;
+ clear();
self_path = p_self_path;
GDScriptTokenizerBuffer *tb = memnew(GDScriptTokenizerBuffer);
@@ -4408,16 +4463,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St
Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion) {
- completion_type = COMPLETION_NONE;
- completion_node = NULL;
- completion_class = NULL;
- completion_function = NULL;
- completion_block = NULL;
- completion_found = false;
- current_block = NULL;
- current_class = NULL;
-
- current_function = NULL;
+ clear();
self_path = p_self_path;
GDScriptTokenizerText *tt = memnew(GDScriptTokenizerText);
@@ -4463,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/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 95efcda80f..422223370b 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -30,6 +30,7 @@
#include "register_types.h"
+#include "editor/gdscript_highlighter.h"
#include "gdscript.h"
#include "gdscript_tokenizer.h"
#include "io/file_access_encrypted.h"
@@ -92,6 +93,7 @@ void register_gdscript_types() {
ResourceSaver::add_resource_format_saver(resource_saver_gd);
#ifdef TOOLS_ENABLED
+ ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create);
EditorNode::add_init_callback(_editor_init);
#endif
}
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 44685220b3..d5f9563600 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GridMap" inherits="Spatial" category="Core" version="3.0-stable">
+<class name="GridMap" inherits="Spatial" category="Core" version="3.1">
<brief_description>
Node for 3D tile-based maps.
</brief_description>
@@ -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/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index b17fe43ae8..ed36751fc8 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -247,7 +247,7 @@ public:
int get_cell_item(int p_x, int p_y, int p_z) const;
int get_cell_item_orientation(int p_x, int p_y, int p_z) const;
- Vector3 world_to_map(const Vector3 &p_pos) const;
+ Vector3 world_to_map(const Vector3 &p_world_pos) const;
Vector3 map_to_world(int p_x, int p_y, int p_z) const;
void set_clip(bool p_enabled, bool p_clip_above = true, int p_floor = 0, Vector3::Axis p_axis = Vector3::AXIS_X);
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 7b7bc0fa2a..fc5972c810 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -99,6 +99,20 @@ void GridMapEditor::_menu_option(int p_option) {
int idx = options->get_popup()->get_item_index(MENU_OPTION_X_AXIS + i);
options->get_popup()->set_item_checked(idx, i == new_axis);
}
+
+ if (edit_axis != new_axis) {
+ int item1 = options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL);
+ int item2 = options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL);
+ if (edit_axis == Vector3::AXIS_Y) {
+ options->get_popup()->set_item_text(item1, TTR("Next Plane"));
+ options->get_popup()->set_item_text(item2, TTR("Previous Plane"));
+ spin_box_label->set_text(TTR("Plane:"));
+ } else if (new_axis == Vector3::AXIS_Y) {
+ options->get_popup()->set_item_text(item1, TTR("Next Floor"));
+ options->get_popup()->set_item_text(item2, TTR("Previous Floor"));
+ spin_box_label->set_text(TTR("Floor:"));
+ }
+ }
edit_axis = Vector3::Axis(new_axis);
update_grid();
_update_clip();
@@ -217,6 +231,13 @@ void GridMapEditor::_menu_option(int p_option) {
_delete_selection();
} break;
+ case MENU_OPTION_SELECTION_FILL: {
+ if (!selection.active)
+ return;
+
+ _fill_selection();
+
+ } break;
case MENU_OPTION_GRIDMAP_SETTINGS: {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50) * EDSCALE);
} break;
@@ -441,6 +462,29 @@ void GridMapEditor::_delete_selection() {
_validate_selection();
}
+void GridMapEditor::_fill_selection() {
+
+ if (!selection.active)
+ return;
+
+ undo_redo->create_action(TTR("GridMap Fill Selection"));
+ for (int i = selection.begin.x; i <= selection.end.x; i++) {
+
+ for (int j = selection.begin.y; j <= selection.end.y; j++) {
+
+ for (int k = selection.begin.z; k <= selection.end.z; k++) {
+
+ undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_pallete, cursor_rot);
+ undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ }
+ }
+ }
+ undo_redo->commit_action();
+
+ selection.active = false;
+ _validate_selection();
+}
+
void GridMapEditor::_update_duplicate_indicator() {
if (!selection.active || input_action != INPUT_DUPLICATE) {
@@ -627,12 +671,21 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
- if (pan_gesture->get_command() || pan_gesture->get_shift()) {
- const real_t delta = pan_gesture->get_delta().y;
- floor->set_value(floor->get_value() + SGN(delta));
+ if (pan_gesture->get_alt() && (pan_gesture->get_command() || pan_gesture->get_shift())) {
+ const real_t delta = pan_gesture->get_delta().y * 0.5;
+ accumulated_floor_delta += delta;
+ int step = 0;
+ if (ABS(accumulated_floor_delta) > 1.0) {
+ step = SGN(accumulated_floor_delta);
+ accumulated_floor_delta -= step;
+ }
+ if (step) {
+ floor->set_value(floor->get_value() + step);
+ }
return true;
}
}
+ accumulated_floor_delta = 0.0;
return false;
}
@@ -756,7 +809,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
set_process(true);
- Vector3 edited_floor = p_gridmap->get_meta("_editor_floor_");
+ Vector3 edited_floor = p_gridmap->has_meta("_editor_floor_") ? p_gridmap->get_meta("_editor_floor_") : Variant();
clip_mode = p_gridmap->has_meta("_editor_clip_") ? ClipMode(p_gridmap->get_meta("_editor_clip_").operator int()) : CLIP_DISABLED;
for (int i = 0; i < 3; i++) {
@@ -998,9 +1051,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
spatial_editor_hb->set_alignment(BoxContainer::ALIGN_END);
SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
- Label *fl = memnew(Label);
- fl->set_text(TTR("Floor:"));
- spatial_editor_hb->add_child(fl);
+ spin_box_label = memnew(Label);
+ spin_box_label->set_text(TTR("Floor:"));
+ spatial_editor_hb->add_child(spin_box_label);
floor = memnew(SpinBox);
floor->set_min(-32767);
@@ -1023,14 +1076,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, KEY_Q);
options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, KEY_E);
options->get_popup()->add_separator();
- options->get_popup()->add_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
+ options->get_popup()->add_radio_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true);
- options->get_popup()->add_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
- options->get_popup()->add_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
+ options->get_popup()->add_radio_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
+ options->get_popup()->add_radio_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
options->get_popup()->add_separator();
- options->get_popup()->add_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
- options->get_popup()->add_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
- options->get_popup()->add_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C);
+ options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
+ options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
+ options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, KEY_A);
@@ -1049,6 +1102,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C);
options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X);
+ options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS);
@@ -1131,9 +1185,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
}
@@ -1233,6 +1287,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
selection.active = false;
updating = false;
+ accumulated_floor_delta = 0.0;
}
GridMapEditor::~GridMapEditor() {
@@ -1245,9 +1300,10 @@ GridMapEditor::~GridMapEditor() {
VisualServer::get_singleton()->free(grid_instance[i]);
if (cursor_instance.is_valid())
VisualServer::get_singleton()->free(cursor_instance);
- if (selection_level_instance[i].is_valid()) {
+ if (selection_level_instance[i].is_valid())
VisualServer::get_singleton()->free(selection_level_instance[i]);
- }
+ if (selection_level_mesh[i].is_valid())
+ VisualServer::get_singleton()->free(selection_level_mesh[i]);
}
VisualServer::get_singleton()->free(selection_mesh);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 3fc92bf7aa..7c5feda125 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -76,12 +76,14 @@ class GridMapEditor : public VBoxContainer {
Panel *panel;
MenuButton *options;
SpinBox *floor;
+ double accumulated_floor_delta;
ToolButton *mode_thumbnail;
ToolButton *mode_list;
HBoxContainer *spatial_editor_hb;
ConfirmationDialog *settings_dialog;
VBoxContainer *settings_vbc;
SpinBox *settings_pick_distance;
+ Label *spin_box_label;
struct SetItem {
@@ -166,6 +168,7 @@ class GridMapEditor : public VBoxContainer {
MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
MENU_OPTION_SELECTION_DUPLICATE,
MENU_OPTION_SELECTION_CLEAR,
+ MENU_OPTION_SELECTION_FILL,
MENU_OPTION_REMOVE_AREA,
MENU_OPTION_GRIDMAP_SETTINGS
@@ -198,6 +201,7 @@ class GridMapEditor : public VBoxContainer {
void _floor_changed(float p_value);
void _delete_selection();
+ void _fill_selection();
EditorNode *editor;
bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click);
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/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index 3cc362b5d6..d592c19b97 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -42,14 +42,18 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
ERR_FAIL_COND_V(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED);
while (true) {
- String format = f->get_token();
+ String line = f->get_line();
ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_UNRECOGNIZED);
- if (format.begins_with("FORMAT=") && format != "FORMAT=32-bit_rle_rgbe") {
- ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for .hdr files.");
- return ERR_FILE_UNRECOGNIZED;
- }
- if (format == "FORMAT=32-bit_rle_rgbe")
+ if (line == "") // empty line indicates end of header
break;
+ if (line.begins_with("FORMAT=")) { // leave option to implement other commands
+ if (line != "FORMAT=32-bit_rle_rgbe") {
+ ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for HDR files.");
+ return ERR_FILE_UNRECOGNIZED;
+ }
+ } else if (!line.begins_with("#")) { // not comment
+ WARN_PRINTS("Ignoring unsupported header information in HDR : " + line);
+ }
}
String token = f->get_token();
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/SCsub b/modules/mbedtls/SCsub
new file mode 100755
index 0000000000..38198c9105
--- /dev/null
+++ b/modules/mbedtls/SCsub
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_mbed_tls = env_modules.Clone()
+
+if env['builtin_mbedtls']:
+ # Thirdparty source files
+ thirdparty_sources = [
+ "aes.c",
+ "aesni.c",
+ "arc4.c",
+ "asn1parse.c",
+ "asn1write.c",
+ "base64.c",
+ "bignum.c",
+ "blowfish.c",
+ "camellia.c",
+ "ccm.c",
+ "certs.c",
+ "cipher.c",
+ "cipher_wrap.c",
+ "cmac.c",
+ "ctr_drbg.c",
+ "debug.c",
+ "des.c",
+ "dhm.c",
+ "ecdh.c",
+ "ecdsa.c",
+ "ecjpake.c",
+ "ecp.c",
+ "ecp_curves.c",
+ "entropy.c",
+ "entropy_poll.c",
+ "error.c",
+ "gcm.c",
+ "havege.c",
+ "hmac_drbg.c",
+ "md2.c",
+ "md4.c",
+ "md5.c",
+ "md.c",
+ "md_wrap.c",
+ "memory_buffer_alloc.c",
+ "net_sockets.c",
+ "oid.c",
+ "padlock.c",
+ "pem.c",
+ "pk.c",
+ "pkcs11.c",
+ "pkcs12.c",
+ "pkcs5.c",
+ "pkparse.c",
+ "pk_wrap.c",
+ "pkwrite.c",
+ "platform.c",
+ "ripemd160.c",
+ "rsa.c",
+ "rsa_internal.c",
+ "sha1.c",
+ "sha256.c",
+ "sha512.c",
+ "ssl_cache.c",
+ "ssl_ciphersuites.c",
+ "ssl_cli.c",
+ "ssl_cookie.c",
+ "ssl_srv.c",
+ "ssl_ticket.c",
+ "ssl_tls.c",
+ "threading.c",
+ "timing.c",
+ "version.c",
+ "version_features.c",
+ "x509.c",
+ "x509_create.c",
+ "x509_crl.c",
+ "x509_crt.c",
+ "x509_csr.c",
+ "x509write_crt.c",
+ "x509write_csr.c",
+ "xtea.c"
+ ]
+
+ thirdparty_dir = "#thirdparty/mbedtls/library/"
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+ env_mbed_tls.add_source_files(env.modules_sources, thirdparty_sources)
+ env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"])
+
+# Module sources
+env_mbed_tls.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mbedtls/config.py b/modules/mbedtls/config.py
new file mode 100755
index 0000000000..1c8cd12a2d
--- /dev/null
+++ b/modules/mbedtls/config.py
@@ -0,0 +1,5 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/openssl/register_types.cpp b/modules/mbedtls/register_types.cpp
index 916acc260e..8548275eec 100644..100755
--- a/modules/openssl/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -30,15 +30,15 @@
#include "register_types.h"
-#include "stream_peer_openssl.h"
+#include "stream_peer_mbed_tls.h"
-void register_openssl_types() {
+void register_mbedtls_types() {
- ClassDB::register_class<StreamPeerOpenSSL>();
- StreamPeerOpenSSL::initialize_ssl();
+ ClassDB::register_class<StreamPeerMbedTLS>();
+ StreamPeerMbedTLS::initialize_ssl();
}
-void unregister_openssl_types() {
+void unregister_mbedtls_types() {
- StreamPeerOpenSSL::finalize_ssl();
+ StreamPeerMbedTLS::finalize_ssl();
}
diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h
new file mode 100755
index 0000000000..3da0b1f1a0
--- /dev/null
+++ b/modules/mbedtls/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_mbedtls_types();
+void unregister_mbedtls_types();
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
new file mode 100755
index 0000000000..a63e53ec1f
--- /dev/null
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -0,0 +1,307 @@
+/*************************************************************************/
+/* stream_peer_openssl.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_mbed_tls.h"
+
+static void my_debug(void *ctx, int level,
+ const char *file, int line,
+ const char *str) {
+
+ printf("%s:%04d: %s", file, line, str);
+ fflush(stdout);
+}
+
+void _print_error(int ret) {
+ printf("mbedtls error: returned -0x%x\n\n", -ret);
+ fflush(stdout);
+}
+
+int StreamPeerMbedTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
+
+ if (buf == NULL || len <= 0) return 0;
+
+ StreamPeerMbedTLS *sp = (StreamPeerMbedTLS *)ctx;
+
+ ERR_FAIL_COND_V(sp == NULL, 0);
+
+ int sent;
+ Error err = sp->base->put_partial_data((const uint8_t *)buf, len, sent);
+ if (err != OK) {
+ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+ }
+ if (sent == 0) {
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+ }
+ return sent;
+}
+
+int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
+
+ if (buf == NULL || len <= 0) return 0;
+
+ StreamPeerMbedTLS *sp = (StreamPeerMbedTLS *)ctx;
+
+ ERR_FAIL_COND_V(sp == NULL, 0);
+
+ int got;
+ Error err = sp->base->get_partial_data((uint8_t *)buf, len, got);
+ if (err != OK) {
+ return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+ }
+ if (got == 0) {
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ return got;
+}
+
+Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) {
+
+ base = p_base;
+ int ret = 0;
+ int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE;
+
+ mbedtls_ssl_init(&ssl);
+ mbedtls_ssl_config_init(&conf);
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
+ if (ret != 0) {
+ ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
+ return FAILED;
+ }
+
+ mbedtls_ssl_config_defaults(&conf,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT);
+
+ mbedtls_ssl_conf_authmode(&conf, authmode);
+ mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
+ mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
+ mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
+ mbedtls_ssl_setup(&ssl, &conf);
+ mbedtls_ssl_set_hostname(&ssl, p_for_hostname.utf8().get_data());
+
+ mbedtls_ssl_set_bio(&ssl, this, bio_send, bio_recv, NULL);
+
+ while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ERR_PRINTS("TLS handshake error: " + itos(ret));
+ _print_error(ret);
+ status = STATUS_ERROR_HOSTNAME_MISMATCH;
+ return FAILED;
+ }
+ }
+
+ connected = true;
+ status = STATUS_CONNECTED;
+
+ return OK;
+}
+
+Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base) {
+
+ return ERR_UNAVAILABLE;
+}
+
+Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
+
+ ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+
+ Error err;
+ int sent = 0;
+
+ while (p_bytes > 0) {
+ err = put_partial_data(p_data, p_bytes, sent);
+
+ if (err != OK) {
+ return err;
+ }
+
+ p_data += sent;
+ p_bytes -= sent;
+ }
+
+ return OK;
+}
+
+Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+
+ ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+
+ r_sent = 0;
+
+ if (p_bytes == 0)
+ return OK;
+
+ int ret = mbedtls_ssl_write(&ssl, p_data, p_bytes);
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ret = 0; // non blocking io
+ } else if (ret <= 0) {
+ _print_error(ret);
+ disconnect_from_stream();
+ return ERR_CONNECTION_ERROR;
+ }
+
+ r_sent = ret;
+ return OK;
+}
+
+Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
+
+ ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+
+ Error err;
+
+ int got = 0;
+ while (p_bytes > 0) {
+
+ err = get_partial_data(p_buffer, p_bytes, got);
+
+ if (err != OK) {
+ return err;
+ }
+
+ p_buffer += got;
+ p_bytes -= got;
+ }
+
+ return OK;
+}
+
+Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+
+ ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+
+ r_received = 0;
+
+ int ret = mbedtls_ssl_read(&ssl, p_buffer, p_bytes);
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ret = 0; // non blocking io
+ } else if (ret <= 0) {
+ _print_error(ret);
+ disconnect_from_stream();
+ return ERR_CONNECTION_ERROR;
+ }
+
+ r_received = ret;
+ return OK;
+}
+
+void StreamPeerMbedTLS::poll() {
+
+ ERR_FAIL_COND(!connected);
+ ERR_FAIL_COND(!base.is_valid());
+
+ int ret = mbedtls_ssl_read(&ssl, NULL, 0);
+
+ if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ _print_error(ret);
+ disconnect_from_stream();
+ return;
+ }
+}
+
+int StreamPeerMbedTLS::get_available_bytes() const {
+
+ ERR_FAIL_COND_V(!connected, 0);
+
+ return mbedtls_ssl_get_bytes_avail(&ssl);
+}
+StreamPeerMbedTLS::StreamPeerMbedTLS() {
+
+ connected = false;
+ status = STATUS_DISCONNECTED;
+}
+
+StreamPeerMbedTLS::~StreamPeerMbedTLS() {
+ disconnect_from_stream();
+}
+
+void StreamPeerMbedTLS::disconnect_from_stream() {
+
+ if (!connected)
+ return;
+
+ mbedtls_ssl_free(&ssl);
+ mbedtls_ssl_config_free(&conf);
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ base = Ref<StreamPeer>();
+ connected = false;
+ status = STATUS_DISCONNECTED;
+}
+
+StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
+
+ return status;
+}
+
+StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
+
+ return memnew(StreamPeerMbedTLS);
+}
+
+mbedtls_x509_crt StreamPeerMbedTLS::cacert;
+
+void StreamPeerMbedTLS::_load_certs(const PoolByteArray &p_array) {
+ int arr_len = p_array.size();
+ PoolByteArray::Read r = p_array.read();
+ int err = mbedtls_x509_crt_parse(&cacert, &r[0], arr_len);
+ if (err != 0) {
+ WARN_PRINTS("Error parsing some certificates: " + itos(err));
+ }
+}
+
+void StreamPeerMbedTLS::initialize_ssl() {
+
+ _create = _create_func;
+ load_certs_func = _load_certs;
+
+ mbedtls_x509_crt_init(&cacert);
+
+#ifdef DEBUG_ENABLED
+ mbedtls_debug_set_threshold(1);
+#endif
+
+ PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array();
+
+ if (cert_array.size() > 0)
+ _load_certs(cert_array);
+
+ available = true;
+}
+
+void StreamPeerMbedTLS::finalize_ssl() {
+
+ mbedtls_x509_crt_free(&cacert);
+}
diff --git a/modules/openssl/stream_peer_openssl.h b/modules/mbedtls/stream_peer_mbed_tls.h
index 29c8647e58..2b96a194a1 100644..100755
--- a/modules/openssl/stream_peer_openssl.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -32,66 +32,43 @@
#define STREAM_PEER_OPEN_SSL_H
#include "io/stream_peer_ssl.h"
-#include "os/file_access.h"
-#include "project_settings.h"
-#include "thirdparty/misc/curl_hostcheck.h"
-
-#include <openssl/bio.h> // BIO objects for I/O
-#include <openssl/err.h> // Error reporting
-#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
-#include <openssl/x509v3.h>
+#include "mbedtls/config.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/debug.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/net.h"
+#include "mbedtls/ssl.h"
#include <stdio.h>
+#include <stdlib.h>
-class StreamPeerOpenSSL : public StreamPeerSSL {
+class StreamPeerMbedTLS : public StreamPeerSSL {
private:
- static int _bio_create(BIO *b);
- static int _bio_destroy(BIO *b);
- static int _bio_read(BIO *b, char *buf, int len);
- static int _bio_write(BIO *b, const char *buf, int len);
- static long _bio_ctrl(BIO *b, int cmd, long num, void *ptr);
- static int _bio_gets(BIO *b, char *buf, int len);
- static int _bio_puts(BIO *b, const char *str);
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
- static BIO_METHOD *_bio_method;
-#else
- static BIO_METHOD _bio_method;
-#endif
- static BIO_METHOD *_get_bio_method();
-
- static bool _match_host_name(const char *name, const char *hostname);
- static Error _match_common_name(const char *hostname, const X509 *server_cert);
- static Error _match_subject_alternative_name(const char *hostname, const X509 *server_cert);
-
- static int _cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg);
-
Status status;
String hostname;
- int max_cert_chain_depth;
- SSL_CTX *ctx;
- SSL *ssl;
- BIO *bio;
+
bool connected;
- int flags;
- bool use_blocking;
- bool validate_certs;
- bool validate_hostname;
Ref<StreamPeer> base;
static StreamPeerSSL *_create_func();
- void _print_error(int err);
-
- static Vector<X509 *> certs;
-
static void _load_certs(const PoolByteArray &p_array);
+ static int bio_recv(void *ctx, unsigned char *buf, size_t len);
+ static int bio_send(void *ctx, const unsigned char *buf, size_t len);
+
protected:
+ static mbedtls_x509_crt cacert;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_ssl_context ssl;
+ mbedtls_ssl_config conf;
+
static void _bind_methods();
public:
+ virtual void poll();
virtual Error accept_stream(Ref<StreamPeer> p_base);
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String());
virtual Status get_status() const;
@@ -109,8 +86,8 @@ public:
static void initialize_ssl();
static void finalize_ssl();
- StreamPeerOpenSSL();
- ~StreamPeerOpenSSL();
+ StreamPeerMbedTLS();
+ ~StreamPeerMbedTLS();
};
#endif // STREAM_PEER_SSL_H
diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py
index 4e1155f0c6..4912457e2b 100644
--- a/modules/mobile_vr/config.py
+++ b/modules/mobile_vr/config.py
@@ -1,6 +1,6 @@
-def can_build(platform):
+def can_build(env, platform):
# should probably change this to only be true on iOS and Android
- return True
+ return False
def configure(env):
pass
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index 82300e707a..359d654433 100644
--- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml
+++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0-stable">
+<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.1">
<brief_description>
Generic mobile VR implementation
</brief_description>
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 320bbe7090..03e187e5b0 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -31,11 +31,18 @@ def make_cs_files_header(src, dst):
if i > 0:
header.write(', ')
header.write(byte_to_str(buf[buf_idx]))
- inserted_files += '\tr_files.insert(\"' + file + '\", ' \
+ inserted_files += '\tr_files.insert("' + file + '", ' \
'CompressedFile(_cs_' + name + '_compressed_size, ' \
'_cs_' + name + '_uncompressed_size, ' \
'_cs_' + name + '_compressed));\n'
header.write(' };\n')
+ version_file = os.path.join(src, 'VERSION.txt')
+ with open(version_file, 'r') as content_file:
+ try:
+ glue_version = int(content_file.read()) # make sure the format is valid
+ header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
+ except ValueError:
+ raise ValueError('Invalid C# glue version in: ' + version_file)
header.write('\nstruct CompressedFile\n' '{\n'
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
@@ -57,10 +64,10 @@ if env['tools']:
vars = Variables()
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
-vars.Update(env)
+vars.Update(env_mono)
# Glue sources
-if env['mono_glue']:
+if env_mono['mono_glue']:
env_mono.add_source_files(env.modules_sources, 'glue/*.cpp')
else:
env_mono.Append(CPPDEFINES=['MONO_GLUE_DISABLED'])
@@ -80,23 +87,23 @@ def find_msbuild_unix(filename):
import sys
hint_dirs = ['/opt/novell/mono/bin']
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path):
return hint_path
- elif os.path.isfile(hint_path + ".exe"):
- return hint_path + ".exe"
+ elif os.path.isfile(hint_path + '.exe'):
+ return hint_path + '.exe'
- for hint_dir in os.environ["PATH"].split(os.pathsep):
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, filename)
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
+ if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
+ return hint_path + '.exe'
return None
@@ -120,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
@@ -138,21 +154,28 @@ 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:
xbuild_fallback = env['xbuild_fallback']
if xbuild_fallback and os.name == 'nt':
- print("Option 'xbuild_fallback' not supported on Windows")
+ print('Option \'xbuild_fallback\' not supported on Windows')
xbuild_fallback = False
if xbuild_fallback:
@@ -176,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)
@@ -202,10 +219,16 @@ def mono_build_solution(source, target, env):
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
+output_dir = Dir('#bin').abspath
+assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
-mono_sln_builder = Builder(action = mono_build_solution)
+mono_sln_builder = Builder(action=mono_build_solution)
env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
env_mono.MonoBuildSolution(
- os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),
+ os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'),
'editor/GodotSharpTools/GodotSharpTools.sln'
)
+
+if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir):
+ rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir)
+ env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir})
diff --git a/modules/mono/config.py b/modules/mono/config.py
index b4e6433256..ebf8512fb6 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -2,8 +2,9 @@
import imp
import os
import sys
+import subprocess
-from SCons.Script import BoolVariable, Environment, 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')
@@ -18,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
@@ -29,20 +30,27 @@ def is_enabled():
return False
-def copy_file_no_replace(src_dir, dst_dir, name):
+def copy_file(src_dir, dst_dir, name):
from shutil import copyfile
src_path = os.path.join(src_dir, name)
dst_path = os.path.join(dst_dir, name)
- need_copy = True
if not os.path.isdir(dst_dir):
os.mkdir(dst_dir)
- elif os.path.exists(dst_path):
- need_copy = False
- if need_copy:
- copyfile(src_path, dst_path)
+ 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):
@@ -51,18 +59,17 @@ def configure(env):
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', custom_path_is_dir_create))
envvars.Update(env)
bits = env['bits']
mono_static = env['mono_static']
+ assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
if env['platform'] == 'windows':
- if mono_static:
- raise RuntimeError('mono-static: Not supported on Windows')
-
if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
@@ -82,28 +89,55 @@ def configure(env):
env.Append(LIBPATH=mono_lib_path)
env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
- mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+ if mono_static:
+ lib_suffix = Environment()['LIBSUFFIX']
+
+ if env.msvc:
+ mono_static_lib_name = 'libmono-static-sgen'
+ else:
+ mono_static_lib_name = 'libmonosgen-2.0'
+
+ if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)):
+ raise RuntimeError('Could not find static mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
- if not mono_lib_name:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+ env.Append(LINKFLAGS='Mincore' + lib_suffix)
+ env.Append(LINKFLAGS='msvcrt' + lib_suffix)
+ env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
+ env.Append(LINKFLAGS='Psapi' + lib_suffix)
+ else:
+ env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
- if os.getenv('VCINSTALLDIR'):
- env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ env.Append(LIBS='psapi')
+ env.Append(LIBS='version')
else:
- env.Append(LIBS=mono_lib_name)
+ mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+
+ if not mono_lib_name:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ else:
+ env.Append(LIBS=mono_lib_name)
- mono_bin_path = os.path.join(mono_root, 'bin')
+ mono_bin_path = os.path.join(mono_root, 'bin')
- mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
+ mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
- if not mono_dll_name:
- raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
+ if not mono_dll_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
- copy_file_no_replace(mono_bin_path, 'bin', mono_dll_name + '.dll')
+ copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+
+ copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
else:
sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
mono_root = ''
+ mono_lib_path = ''
if bits == '32':
if os.getenv('MONO32_PREFIX'):
@@ -112,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')
@@ -148,17 +198,20 @@ def configure(env):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
- copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+
+ 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')
mono_lib_path = ''
mono_so_name = ''
+ mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
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']:
@@ -171,7 +224,8 @@ def configure(env):
if not mono_so_name:
raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
- copy_file_no_replace(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+ copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
env.Append(LINKFLAGS='-rdynamic')
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 62f83ee430..2420cdb4af 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -118,6 +118,8 @@ void CSharpLanguage::init() {
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&gdsharp_editor_init_callback);
+
+ GLOBAL_DEF("mono/export/include_scripts_content", true);
#endif
}
@@ -176,7 +178,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
"fixed",
"float",
"for",
- "forech",
+ "foreach",
"goto",
"if",
"implicit",
@@ -222,14 +224,17 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
"ushort",
"using",
"virtual",
- "volatile",
"void",
+ "volatile",
"while",
// Contextual keywords. Not reserved words, but I guess we should include
// them because this seems to be used only for syntax highlighting.
"add",
+ "alias",
"ascending",
+ "async",
+ "await",
"by",
"descending",
"dynamic",
@@ -238,10 +243,10 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
"get",
"global",
"group",
- "in",
"into",
"join",
"let",
+ "nameof",
"on",
"orderby",
"partial",
@@ -250,6 +255,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
"set",
"value",
"var",
+ "when",
"where",
"yield",
0
@@ -276,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("@\" \""); // verbatim string literal
}
+static String get_base_class_name(const String &p_base_class_name, const String p_class_name) {
+
+ String base_class = p_base_class_name;
+ if (p_class_name == base_class) {
+ base_class = "Godot." + base_class;
+ }
+ return base_class;
+}
+
Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
String script_template = "using " BINDINGS_NAMESPACE ";\n"
@@ -283,26 +298,25 @@ 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";
- script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name)
+ String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name)
.replace("%CLASS_NAME%", p_class_name);
Ref<CSharpScript> script;
@@ -321,12 +335,24 @@ bool CSharpLanguage::is_using_templates() {
void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
String src = p_script->get_source_code();
- src = src.replace("%BASE%", p_base_class_name)
+ String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ src = src.replace("%BASE%", base_class_name)
.replace("%CLASS%", p_class_name)
.replace("%TS%", _get_indentation());
p_script->set_source_code(src);
}
+String CSharpLanguage::validate_path(const String &p_path) const {
+
+ String class_name = p_path.get_file().get_basename();
+ List<String> keywords;
+ get_reserved_words(&keywords);
+ if (keywords.find(class_name)) {
+ return TTR("Class name can't be a reserved keyword");
+ }
+ return "";
+}
+
Script *CSharpLanguage::create_script() const {
return memnew(CSharpScript);
@@ -418,7 +444,7 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name
s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0));
}
- s += ")\n{\n // Replace with function body\n}\n";
+ s += ")\n{\n // Replace with function body.\n}\n";
return s;
#else
@@ -450,7 +476,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
// Printing an error here will result in endless recursion, so we must be careful
- if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
+ if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
@@ -717,8 +743,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> scr = E->key();
+ scr->signals_invalidated = true;
scr->exports_invalidated = true;
scr->reload(p_soft_reload);
+ scr->update_signals();
scr->update_exports();
//restore state if saved
@@ -751,8 +779,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
//if instance states were saved, set them!
}
- if (Engine::get_singleton()->is_editor_hint())
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ if (Engine::get_singleton()->is_editor_hint()) {
+ EditorNode::get_singleton()->get_inspector()->update_tree();
+ NodeDock::singleton->update_lists();
+ }
}
#endif
@@ -947,19 +977,6 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
#endif
}
-void CSharpInstance::_ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *p_klass, const StringName &p_method, const Variant **p_args, int p_argcount) {
-
- GDMonoClass *base = p_klass->get_parent_class();
- if (base && base != script->native)
- _ml_call_reversed(p_mono_object, base, p_method, p_args, p_argcount);
-
- GDMonoMethod *method = p_klass->get_method(p_method, p_argcount);
-
- if (method) {
- method->invoke(p_mono_object, p_args);
- }
-}
-
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
CSharpInstance *instance = memnew(CSharpInstance);
@@ -1028,6 +1045,8 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true)
return true;
+
+ break;
}
top = top->get_parent_class();
@@ -1088,6 +1107,8 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
r_ret = GDMonoMarshal::mono_object_to_variant(ret);
return true;
}
+
+ break;
}
top = top->get_parent_class();
@@ -1190,8 +1211,10 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam
while (top && top != script->native) {
GDMonoMethod *method = top->get_method(p_method, p_argcount);
- if (method)
+ if (method) {
method->invoke(p_mono_object, p_args);
+ return;
+ }
top = top->get_parent_class();
}
@@ -1199,13 +1222,9 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam
void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (script.is_valid()) {
- MonoObject *mono_object = get_mono_object();
-
- ERR_FAIL_NULL(mono_object);
+ // Sorry, the method is the one that controls the call order
- _ml_call_reversed(mono_object, script->script_class, p_method, p_args, p_argcount);
- }
+ call_multilevel(p_method, p_args, p_argcount);
}
void CSharpInstance::_reference_owner_unsafe() {
@@ -1289,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;
@@ -1316,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;
@@ -1337,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) {
@@ -1552,6 +1577,74 @@ bool CSharpScript::_update_exports() {
return false;
}
+bool CSharpScript::_update_signals() {
+ if (!valid)
+ return false;
+
+ bool changed = false;
+
+ if (signals_invalidated) {
+ signals_invalidated = false;
+
+ GDMonoClass *top = script_class;
+
+ _signals.clear();
+ changed = true; // TODO Do a real check for change
+
+ while (top && top != native) {
+ const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
+ for (int i = delegates.size() - 1; i >= 0; --i) {
+ Vector<Argument> parameters;
+
+ GDMonoClass *delegate = delegates[i];
+
+ if (_get_signal(top, delegate, parameters)) {
+ _signals[delegate->get_name()] = parameters;
+ }
+ }
+
+ top = top->get_parent_class();
+ }
+ }
+
+ return changed;
+}
+
+bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
+ if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
+ MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate);
+
+ if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
+ // Arguments are accessibles as arguments of .Invoke method
+ GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1);
+
+ Vector<StringName> names;
+ Vector<ManagedType> types;
+ invoke->get_parameter_names(names);
+ invoke->get_parameter_types(types);
+
+ if (names.size() == types.size()) {
+ for (int i = 0; i < names.size(); ++i) {
+ Argument arg;
+ arg.name = names[i];
+ arg.type = GDMonoMarshal::managed_to_variant_type(types[i]);
+
+ if (arg.type == Variant::NIL) {
+ ERR_PRINTS("Unknown type of signal parameter: " + arg.name + " in " + p_class->get_full_name());
+ return false;
+ }
+
+ params.push_back(arg);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
#ifdef TOOLS_ENABLED
bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
@@ -1637,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) {
@@ -1865,20 +1964,34 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call
ScriptInstance *CSharpScript::instance_create(Object *p_this) {
- if (!valid)
- return NULL;
+ ERR_FAIL_COND_V(!valid, NULL);
if (!tool && !ScriptServer::is_scripting_enabled()) {
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
_update_exports();
+ _update_signals();
return si;
#else
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
+ ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
+ ERR_FAIL_V(NULL);
+ }
+
+ update_signals();
+
if (native) {
String native_name = native->get_name();
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -1953,20 +2066,15 @@ Error CSharpScript::reload(bool p_keep_state) {
if (project_assembly) {
script_class = project_assembly->get_object_derived_class(name);
- if (!script_class) {
- ERR_PRINTS("Cannot find class " + name + " for script " + get_path());
- }
+ valid = script_class != NULL;
+
+ if (script_class) {
#ifdef DEBUG_ENABLED
- else if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." +
script_class->get_name() + " for script " + get_path() + "\n")
.utf8());
- }
#endif
- valid = script_class != NULL;
-
- if (script_class) {
tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
native = GDMonoUtils::get_class_native_base(script_class);
@@ -2042,6 +2150,31 @@ void CSharpScript::update_exports() {
#endif
}
+bool CSharpScript::has_script_signal(const StringName &p_signal) const {
+ if (_signals.has(p_signal))
+ return true;
+
+ return false;
+}
+
+void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ for (const Map<StringName, Vector<Argument> >::Element *E = _signals.front(); E; E = E->next()) {
+ MethodInfo mi;
+
+ mi.name = E->key();
+ for (int i = 0; i < E->get().size(); i++) {
+ PropertyInfo arg;
+ arg.name = E->get()[i].name;
+ mi.arguments.push_back(arg);
+ }
+ r_signals->push_back(mi);
+ }
+}
+
+void CSharpScript::update_signals() {
+ _update_signals();
+}
+
Ref<Script> CSharpScript::get_base_script() const {
// TODO search in metadata file once we have it, not important any way?
@@ -2106,6 +2239,7 @@ CSharpScript::CSharpScript() :
#ifdef TOOLS_ENABLED
source_changed_cache = false;
exports_invalidated = true;
+ signals_invalidated = true;
#endif
_resource_path_changed();
@@ -2170,7 +2304,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
CRASH_COND(mono_domain_get() == NULL);
#endif
-#else
+#endif
+
+#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) {
CRASH_COND(Thread::get_caller_id() == Thread::get_main_id());
@@ -2179,14 +2315,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
// because this may be called by one of the editor's worker threads.
// Attach this thread temporarily to reload the script.
- MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
- CRASH_COND(mono_thread == NULL);
+ if (SCRIPTS_DOMAIN) {
+ MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
+ CRASH_COND(mono_thread == NULL);
+ script->reload();
+ mono_thread_detach(mono_thread);
+ }
+
+ } else { // just reload it normally
+#endif
script->reload();
- mono_thread_detach(mono_thread);
- } else // just reload it normally
+#ifdef TOOLS_ENABLED
+ }
#endif
- script->reload();
if (r_error)
*r_error = OK;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 1daa25b74e..cae2bbf40a 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -85,13 +85,20 @@ class CSharpScript : public Script {
SelfList<CSharpScript> script_list;
+ struct Argument {
+ String name;
+ Variant::Type type;
+ };
+
+ Map<StringName, Vector<Argument> > _signals;
+ bool signals_invalidated;
+
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
Set<PlaceHolderScriptInstance *> placeholders;
bool source_changed_cache;
bool exports_invalidated;
-
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
@@ -104,6 +111,9 @@ class CSharpScript : public Script {
void _clear();
+ bool _update_signals();
+ bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
+
bool _update_exports();
#ifdef TOOLS_ENABLED
bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
@@ -137,8 +147,9 @@ public:
virtual Error reload(bool p_keep_state = false);
- /* TODO */ virtual bool has_script_signal(const StringName &p_signal) const { return false; }
- /* TODO */ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const {}
+ virtual bool has_script_signal(const StringName &p_signal) const;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual void update_signals();
/* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
@@ -172,8 +183,6 @@ class CSharpInstance : public ScriptInstance {
bool base_ref;
bool ref_dying;
- void _ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount);
-
void _reference_owner_unsafe();
void _unreference_owner_unsafe();
@@ -183,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;
@@ -204,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);
@@ -285,6 +294,7 @@ public:
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
+ virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
index 0f33c76eb2..082bc30fd8 100644
--- a/modules/mono/doc_classes/@C#.xml
+++ b/modules/mono/doc_classes/@C#.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" category="Core" version="3.0-stable">
+<class name="@C#" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 3efe71f1b3..a1f7399653 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="CSharpScript" inherits="Script" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 1e5edf2a2a..985c66464b 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" category="Core" version="3.0-stable">
+<class name="GodotSharp" inherits="Object" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 04da0600cc..16beacb45c 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -16,24 +16,48 @@ namespace GodotSharpTools.Build
private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)]
- private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath);
+ private extern static string godot_icall_BuildInstance_get_MSBuildPath();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static string godot_icall_BuildInstance_get_FrameworkPath();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
- private struct MSBuildInfo
+ private static string GetMSBuildPath()
{
- public string path;
- public string frameworkPathOverride;
+ string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath();
+
+ if (msbuildPath == null)
+ throw new FileNotFoundException("Cannot find the MSBuild executable.");
+
+ return msbuildPath;
}
- private static MSBuildInfo GetMSBuildInfo()
+ private static string GetFrameworkPath()
{
- MSBuildInfo msbuildInfo = new MSBuildInfo();
+ return godot_icall_BuildInstance_get_FrameworkPath();
+ }
- godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride);
+ private static string MonoWindowsBinDir
+ {
+ get
+ {
+ string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir();
- if (msbuildInfo.path == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ if (monoWinBinDir == null)
+ throw new FileNotFoundException("Cannot find the Windows Mono binaries directory.");
- return msbuildInfo;
+ return monoWinBinDir;
+ }
+ }
+
+ private static bool UsingMonoMSBuildOnWindows
+ {
+ get
+ {
+ return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
+ }
}
private string solution;
@@ -54,25 +78,38 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- MSBuildInfo msbuildInfo = GetMSBuildInfo();
+ bool debugMSBuild = IsDebugMSBuildRequested();
List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- if (msbuildInfo.frameworkPathOverride != null)
- customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
+ string frameworkPath = GetFrameworkPath();
+
+ if (!string.IsNullOrEmpty(frameworkPath))
+ customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
- ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
+ ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
+
+ bool redirectOutput = !debugMSBuild;
- // No console output, thanks
- startInfo.RedirectStandardOutput = true;
- startInfo.RedirectStandardError = true;
+ startInfo.RedirectStandardOutput = redirectOutput;
+ startInfo.RedirectStandardError = redirectOutput;
startInfo.UseShellExecute = false;
+ if (UsingMonoMSBuildOnWindows)
+ {
+ // These environment variables are required for Mono's MSBuild to find the compilers.
+ // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
+ string monoWinBinDir = MonoWindowsBinDir;
+ startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
+ startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
+ startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
+ }
+
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -82,8 +119,11 @@ namespace GodotSharpTools.Build
process.Start();
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
+ if (redirectOutput)
+ {
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
process.WaitForExit();
@@ -95,28 +135,41 @@ 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");
- MSBuildInfo msbuildInfo = GetMSBuildInfo();
-
List<string> customPropertiesList = new List<string>();
if (customProperties != null)
customPropertiesList.AddRange(customProperties);
- if (msbuildInfo.frameworkPathOverride.Length > 0)
- customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
+ string frameworkPath = GetFrameworkPath();
+
+ if (!string.IsNullOrEmpty(frameworkPath))
+ customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath);
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
- ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
+ 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)
+ {
+ // These environment variables are required for Mono's MSBuild to find the compilers.
+ // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
+ string monoWinBinDir = MonoWindowsBinDir;
+ startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
+ startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
+ startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
+ }
+
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -127,8 +180,11 @@ namespace GodotSharpTools.Build
process.Start();
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
+ if (redirectOutput)
+ {
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
return true;
}
@@ -176,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/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
index 981083a3c2..1c8714e31d 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
@@ -11,7 +11,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
+ <DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
@@ -20,7 +20,7 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>full</DebugType>
+ <DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
index 6bf54a0156..1d863e6f61 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -70,7 +70,7 @@ namespace GodotSharpTools.Project
var toolsGroup = root.AddPropertyGroup();
toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
toolsGroup.AddProperty("DebugSymbols", "true");
- toolsGroup.AddProperty("DebugType", "full");
+ toolsGroup.AddProperty("DebugType", "portable");
toolsGroup.AddProperty("Optimize", "false");
toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;");
toolsGroup.AddProperty("ErrorReport", "prompt");
@@ -148,7 +148,7 @@ namespace GodotSharpTools.Project
var debugGroup = root.AddPropertyGroup();
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
debugGroup.AddProperty("DebugSymbols", "true");
- debugGroup.AddProperty("DebugType", "full");
+ debugGroup.AddProperty("DebugType", "portable");
debugGroup.AddProperty("Optimize", "false");
debugGroup.AddProperty("DefineConstants", "DEBUG;");
debugGroup.AddProperty("ErrorReport", "prompt");
@@ -157,7 +157,7 @@ namespace GodotSharpTools.Project
var releaseGroup = root.AddPropertyGroup();
releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
- releaseGroup.AddProperty("DebugType", "full");
+ releaseGroup.AddProperty("DebugType", "portable");
releaseGroup.AddProperty("Optimize", "true");
releaseGroup.AddProperty("ErrorReport", "prompt");
releaseGroup.AddProperty("WarningLevel", "4");
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 62c7a94755..4c598d4f37 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -70,8 +70,6 @@
#define LOCAL_RET "ret"
-#define CS_CLASS_NATIVECALLS "NativeCalls"
-#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
#define CS_FIELD_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
@@ -105,6 +103,8 @@
#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
+#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
+
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
bool BindingsGenerator::verbose_output = false;
@@ -248,14 +248,14 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
if (imethod.is_virtual)
continue;
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
String im_sig;
String im_unique_sig;
if (p_itype.is_object_type) {
im_sig += "IntPtr " CS_PARAM_METHODBIND ", ";
- im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr";
+ im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr";
}
im_sig += "IntPtr " CS_PARAM_INSTANCE;
@@ -263,7 +263,7 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
// Get arguments information
int i = 0;
for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(F->get().type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type);
im_sig += ", ";
im_sig += arg_type->im_type_in;
@@ -529,7 +529,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (!m_icall.editor_only) { \
@@ -551,7 +559,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (m_icall.editor_only) { \
@@ -648,7 +664,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -714,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(INDENT1 "public ");
- output.push_back(itype.is_singleton ? "static class " : "class ");
+ bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
+ output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class "));
output.push_back(itype.proxy_name);
if (itype.is_singleton) {
@@ -882,7 +899,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.push_back("\";\n");
output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." ICALL_PREFIX);
output.push_back(itype.name);
output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
@@ -912,7 +929,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." + ctor_method);
output.push_back("(this);\n" CLOSE_BLOCK_L2);
} else {
@@ -956,7 +973,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"if (disposed) return;\n" INDENT3
"if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
"if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
- " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
+ " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
"(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
"this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3
"GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
@@ -1052,12 +1069,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
if (getter && setter) {
- ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
+ ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
}
- StringName proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
+ const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
- const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
+ const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname));
@@ -1105,9 +1122,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.push_back(getter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = getter->arguments.front()->get();
- if (idx_arg.type != name_cache.type_int) {
+ if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
- const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type);
+ const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == NULL);
p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
} else {
@@ -1122,9 +1139,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.push_back(setter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = setter->arguments.front()->get();
- if (idx_arg.type != name_cache.type_int) {
+ if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
- const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type);
+ const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == NULL);
p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
} else {
@@ -1141,7 +1158,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) {
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
String method_bind_field = "method_bind_" + itos(p_method_bind_count);
@@ -1158,7 +1175,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
@@ -1229,7 +1246,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
{
if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) {
p_output.push_back(MEMBER_BEGIN "private static IntPtr ");
- p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
p_output.push_back(p_imethod.name);
p_output.push_back("\");\n");
}
@@ -1310,22 +1327,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const InternalCall *im_icall = match->value();
- String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
- im_call += "." + im_icall->name + "(" + icall_params + ");\n";
+ String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
+ im_call += "." + im_icall->name + "(" + icall_params + ")";
if (p_imethod.arguments.size())
p_output.push_back(cs_in_statements);
if (return_type->cname == name_cache.type_void) {
- p_output.push_back(im_call);
+ p_output.push_back(im_call + ";\n");
} else if (return_type->cs_out.empty()) {
- p_output.push_back("return " + im_call);
+ p_output.push_back("return " + im_call + ";\n");
} else {
- p_output.push_back(return_type->im_type_out);
- p_output.push_back(" " LOCAL_RET " = ");
- p_output.push_back(im_call);
p_output.push_back(INDENT3);
- p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
+ p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
+ p_output.push_back("\n");
}
p_output.push_back(CLOSE_BLOCK_L2);
@@ -1400,25 +1415,33 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
}
output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
+
output.push_back("uint64_t get_core_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+
output.push_back("#ifdef TOOLS_ENABLED\n"
"uint64_t get_editor_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) +
"; }\n#endif // TOOLS_ENABLED\n");
+
+ output.push_back("uint32_t get_bindings_version() { return ");
+ output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
+ output.push_back("uint32_t get_cs_glue_version() { return ");
+ output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
+
output.push_back("void register_generated_icalls() " OPEN_BLOCK);
output.push_back("\tgodot_register_header_icalls();");
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.push_back("\tmono_add_internal_call("); \
- output.push_back("\"" BINDINGS_NAMESPACE "."); \
- output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
- output.push_back("::"); \
- output.push_back(m_icall.name); \
- output.push_back("\", (void*)"); \
- output.push_back(m_icall.name); \
- output.push_back(");\n"); \
+#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
+ { \
+ output.push_back("\tmono_add_internal_call("); \
+ output.push_back("\"" BINDINGS_NAMESPACE "."); \
+ output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
+ output.push_back("::"); \
+ output.push_back(m_icall.name); \
+ output.push_back("\", (void*)"); \
+ output.push_back(m_icall.name); \
+ output.push_back(");\n"); \
}
bool tools_sequence = false;
@@ -1486,6 +1509,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
return OK;
}
+uint32_t BindingsGenerator::get_version() {
+ return BINDINGS_GENERATOR_VERSION;
+}
+
+uint32_t BindingsGenerator::get_cs_glue_version() {
+ return CS_GLUE_VERSION;
+}
+
Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
@@ -1507,9 +1538,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (p_imethod.is_virtual)
return OK; // Ignore
- bool ret_void = p_imethod.return_type == name_cache.type_void;
+ bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
String argc_str = itos(p_imethod.arguments.size());
@@ -1521,16 +1552,16 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
int i = 0;
for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
String c_param_name = "arg" + itos(i + 1);
if (p_imethod.is_vararg) {
if (i < p_imethod.arguments.size() - 1) {
c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, ";
- c_in_statements += sformat("&%s_in", c_param_name);
- c_in_statements += ");\n";
+ c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
+ c_in_statements += itos(i);
+ c_in_statements += sformat(", &%s_in);\n", c_param_name);
}
} else {
if (i > 0)
@@ -1662,42 +1693,49 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
return OK;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) {
+const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
- const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
if (builtin_type_match)
return &builtin_type_match->get();
- const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname);
+ const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
if (obj_type_match)
return &obj_type_match.get();
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname);
+ if (p_typeref.is_enum) {
+ const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
+
+ if (enum_match)
+ return &enum_match->get();
- if (enum_match)
- return &enum_match->get();
+ // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
+ const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
+ ERR_FAIL_NULL_V(int_match, NULL);
+ return &int_match->get();
+ }
return NULL;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const StringName &p_cname) {
+const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
- const TypeInterface *found = _get_type_by_name_or_null(p_cname);
+ const TypeInterface *found = _get_type_or_null(p_typeref);
if (found)
return found;
- ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_cname.operator String());
+ ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_typeref.cname.operator String());
- const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
if (match)
return &match->get();
TypeInterface placeholder;
- TypeInterface::create_placeholder_type(placeholder, p_cname);
+ TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
return &placeholder_types.insert(placeholder.cname, placeholder)->get();
}
@@ -1841,7 +1879,7 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// The method Object.free is registered as a virtual method, but without the virtual flag.
// This is because this method is not supposed to be overridden, but called.
// We assume the return type is void.
- imethod.return_type = name_cache.type_void;
+ imethod.return_type.cname = name_cache.type_void;
// Actually, more methods like this may be added in the future,
// which could actually will return something different.
@@ -1856,21 +1894,22 @@ void BindingsGenerator::_populate_object_type_interfaces() {
} else {
ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
}
- } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant?
- imethod.return_type = return_info.class_name;
+ } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ imethod.return_type.cname = return_info.class_name;
+ imethod.return_type.is_enum = true;
} else if (return_info.class_name != StringName()) {
- imethod.return_type = return_info.class_name;
+ imethod.return_type.cname = return_info.class_name;
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- imethod.return_type = return_info.hint_string;
+ imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
- imethod.return_type = name_cache.type_Variant;
+ imethod.return_type.cname = name_cache.type_Variant;
} else if (return_info.type == Variant::NIL) {
- imethod.return_type = name_cache.type_void;
+ imethod.return_type.cname = name_cache.type_void;
} else {
- imethod.return_type = Variant::get_type_name(return_info.type);
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
}
- if (!itype.requires_collections && imethod.return_type == name_cache.type_Dictionary)
+ if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
itype.requires_collections = true;
for (int i = 0; i < argc; i++) {
@@ -1879,21 +1918,22 @@ void BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = arginfo.name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant?
- iarg.type = arginfo.class_name;
+ if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ iarg.type.cname = arginfo.class_name;
+ iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
- iarg.type = arginfo.class_name;
+ iarg.type.cname = arginfo.class_name;
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- iarg.type = arginfo.hint_string;
+ iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
- iarg.type = name_cache.type_Variant;
+ iarg.type.cname = name_cache.type_Variant;
} else {
- iarg.type = Variant::get_type_name(arginfo.type);
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
}
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
- if (!itype.requires_collections && iarg.type == name_cache.type_Dictionary)
+ if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
itype.requires_collections = true;
if (m && m->has_default_argument(i)) {
@@ -1905,7 +1945,7 @@ void BindingsGenerator::_populate_object_type_interfaces() {
if (imethod.is_vararg) {
ArgumentInterface ivararg;
- ivararg.type = name_cache.type_VarArg;
+ ivararg.type.cname = name_cache.type_VarArg;
ivararg.name = "@args";
imethod.add_argument(ivararg);
}
@@ -1990,17 +2030,11 @@ void BindingsGenerator::_populate_object_type_interfaces() {
itype.enums.push_back(ienum);
TypeInterface enum_itype;
+ enum_itype.is_enum = true;
enum_itype.name = itype.name + "." + String(*k);
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.cs_type = enum_itype.proxy_name;
- enum_itype.im_type_in = enum_itype.proxy_name;
- enum_itype.im_type_out = enum_itype.proxy_name;
- enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[enum_itype.proxy_name];
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
}
@@ -2035,7 +2069,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
switch (p_val.get_type()) {
case Variant::NIL:
- if (ClassDB::class_exists(r_iarg.type)) {
+ if (ClassDB::class_exists(r_iarg.type.cname)) {
// Object type
r_iarg.default_argument = "null";
} else {
@@ -2048,7 +2082,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
r_iarg.default_argument = bool(p_val) ? "true" : "false";
break;
case Variant::INT:
- if (r_iarg.type != name_cache.type_int) {
+ if (r_iarg.type.cname != name_cache.type_int) {
r_iarg.default_argument = "(%s)" + r_iarg.default_argument;
}
break;
@@ -2109,7 +2143,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
default: {}
}
- if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == name_cache.type_Variant && r_iarg.default_argument != "null")
+ if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
}
@@ -2128,7 +2162,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_arg_in = "&%s_in"; \
itype.c_type_in = m_type_in; \
itype.cs_in = "ref %s"; \
- itype.cs_out = "return (" #m_type ")%0;"; \
+ itype.cs_out = "return (%1)%0;"; \
itype.im_type_out = "object"; \
builtin_types.insert(itype.cname, itype); \
}
@@ -2223,7 +2257,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new NodePath(%0);";
+ itype.cs_out = "return new %1(%0);";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
_populate_builtin_type(itype, Variant::NODE_PATH);
@@ -2231,7 +2265,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
"this." BINDINGS_PTR_FIELD " = NativeCalls.godot_icall_NodePath_Ctor(path);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator NodePath(string from)\n" OPEN_BLOCK_L2 "return new NodePath(from);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator string(NodePath from)\n" OPEN_BLOCK_L2
- "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2);
+ "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2
+ MEMBER_BEGIN "public override string ToString()\n" OPEN_BLOCK_L2 "return (string)this;\n" CLOSE_BLOCK_L2);
builtin_types.insert(itype.cname, itype);
// RID
@@ -2245,7 +2280,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new RID(%0);";
+ itype.cs_out = "return new %1(%0);";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
_populate_builtin_type(itype, Variant::_RID);
@@ -2365,7 +2400,7 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
imethod.name = mi.name;
imethod.cname = imethod.name;
- imethod.proxy_name = mi.name;
+ imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(mi.name));
for (int i = 0; i < mi.arguments.size(); i++) {
ArgumentInterface iarg;
@@ -2374,11 +2409,11 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
iarg.name = pi.name;
if (pi.type == Variant::NIL)
- iarg.type = name_cache.type_Variant;
+ iarg.type.cname = name_cache.type_Variant;
else
- iarg.type = Variant::get_type_name(pi.type);
+ iarg.type.cname = Variant::get_type_name(pi.type);
- if (!r_itype.requires_collections && iarg.type == name_cache.type_Dictionary)
+ if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
r_itype.requires_collections = true;
if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0)
@@ -2389,12 +2424,12 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
if (mi.return_val.type == Variant::NIL) {
if (mi.return_val.name != "")
- imethod.return_type = name_cache.type_Variant;
+ imethod.return_type.cname = name_cache.type_Variant;
} else {
- imethod.return_type = Variant::get_type_name(mi.return_val.type);
+ imethod.return_type.cname = Variant::get_type_name(mi.return_val.type);
}
- if (!r_itype.requires_collections && imethod.return_type == name_cache.type_Dictionary)
+ if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
r_itype.requires_collections = true;
if (r_itype.class_doc) {
@@ -2460,13 +2495,11 @@ void BindingsGenerator::_populate_global_constants() {
EnumInterface &ienum = E->get();
TypeInterface enum_itype;
- enum_itype = TypeInterface::create_value_type(ienum.cname);
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.im_type_in = enum_itype.name;
- enum_itype.im_type_out = enum_itype.name;
+ enum_itype.is_enum = true;
+ enum_itype.name = ienum.cname.operator String();
+ enum_itype.cname = ienum.cname;
+ enum_itype.proxy_name = enum_itype.name;
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
ienum.prefix = _determine_enum_prefix(ienum);
@@ -2487,15 +2520,13 @@ void BindingsGenerator::_populate_global_constants() {
hardcoded_enums.push_back("Vector3.Axis");
for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) {
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
- // Here, we are assuming core types do not begin with underscore
+ // Here, we assume core types do not begin with underscore
TypeInterface enum_itype;
- enum_itype = TypeInterface::create_value_type(E->get());
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.im_type_in = enum_itype.name;
- enum_itype.im_type_out = enum_itype.name;
+ enum_itype.is_enum = true;
+ enum_itype.name = E->get().operator String();
+ enum_itype.cname = E->get();
+ enum_itype.proxy_name = enum_itype.name;
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 4ab8d86cf3..5b33a0e53f 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -81,6 +81,15 @@ class BindingsGenerator {
const DocData::PropertyDoc *prop_doc;
};
+ struct TypeReference {
+ StringName cname;
+ bool is_enum;
+
+ TypeReference() {
+ is_enum = false;
+ }
+ };
+
struct ArgumentInterface {
enum DefaultParamMode {
CONSTANT,
@@ -88,7 +97,8 @@ class BindingsGenerator {
NULLABLE_REF
};
- StringName type;
+ TypeReference type;
+
String name;
String default_argument;
DefaultParamMode def_param_mode;
@@ -110,7 +120,7 @@ class BindingsGenerator {
/**
* [TypeInterface::name] of the return type
*/
- StringName return_type;
+ TypeReference return_type;
/**
* Determines if the method has a variable number of arguments (VarArg)
@@ -146,7 +156,7 @@ class BindingsGenerator {
}
MethodInterface() {
- return_type = BindingsGenerator::get_singleton()->name_cache.type_void;
+ return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void;
is_vararg = false;
is_virtual = false;
requires_object_call = false;
@@ -175,6 +185,7 @@ class BindingsGenerator {
ClassDB::APIType api_type;
+ bool is_enum;
bool is_object_type;
bool is_singleton;
bool is_reference;
@@ -276,7 +287,9 @@ class BindingsGenerator {
* One or more statements that determine how a variable of this type is returned from a method.
* It must contain the return statement(s).
* Formatting elements:
- * %0 or %s: name of the variable to be returned
+ * %0: internal method call statement
+ * %1: [cs_type] of the return type
+ * %2: [im_type_out] of the return type
*/
String cs_out;
@@ -293,8 +306,6 @@ class BindingsGenerator {
/**
* Type used for the return type of internal call methods.
- * If [cs_out] is not empty and the method return type is not void,
- * it is also used for the type of the return variable.
*/
String im_type_out;
@@ -379,10 +390,24 @@ class BindingsGenerator {
r_itype.im_type_out = r_itype.proxy_name;
}
+ static void postsetup_enum_type(TypeInterface &r_enum_itype) {
+ r_enum_itype.c_arg_in = "&%s";
+ r_enum_itype.c_type = "int";
+ r_enum_itype.c_type_in = "int";
+ r_enum_itype.c_type_out = "int";
+ r_enum_itype.cs_type = r_enum_itype.proxy_name;
+ r_enum_itype.cs_in = "(int)%s";
+ r_enum_itype.cs_out = "return (%1)%0;";
+ r_enum_itype.im_type_in = "int";
+ r_enum_itype.im_type_out = "int";
+ r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
+ }
+
TypeInterface() {
api_type = ClassDB::API_NONE;
+ is_enum = false;
is_object_type = false;
is_singleton = false;
is_reference = false;
@@ -492,6 +517,8 @@ class BindingsGenerator {
return "Ref";
else if (p_type.is_object_type)
return "Obj";
+ else if (p_type.is_enum)
+ return "int";
return p_type.name;
}
@@ -501,11 +528,11 @@ class BindingsGenerator {
void _generate_header_icalls();
void _generate_method_icalls(const TypeInterface &p_itype);
- const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname);
- const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname);
+ const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
+ const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
- void _default_argument_from_variant(const Variant &p_var, ArgumentInterface &r_iarg);
- void _populate_builtin_type(TypeInterface &r_type, Variant::Type vtype);
+ void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg);
+ void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype);
void _populate_object_type_interfaces();
void _populate_builtin_type_interfaces();
@@ -514,14 +541,14 @@ class BindingsGenerator {
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
- Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_prop_doc, List<String> &p_output);
+ Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output);
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output);
void _generate_global_constants(List<String> &p_output);
Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output);
- Error _save_file(const String &path, const List<String> &content);
+ Error _save_file(const String &p_path, const List<String> &p_content);
BindingsGenerator() {}
@@ -536,6 +563,9 @@ public:
Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
+ static uint32_t get_version();
+ static uint32_t get_cs_glue_version();
+
void initialize();
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 6b41b10981..43d58caad2 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -33,13 +33,16 @@
#include "main/main.h"
#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "bindings_generator.h"
#include "godotsharp_editor.h"
+#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
+#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)"
+#define PROP_NAME_XBUILD "xbuild (Deprecated)"
+
void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
@@ -77,46 +80,42 @@ String _find_build_engine_on_unix(const String &p_name) {
}
#endif
-void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) {
+MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
#if defined(WINDOWS_ENABLED)
switch (build_tool) {
- case GodotSharpBuilds::MSBUILD: {
+ case GodotSharpBuilds::MSBUILD_VS: {
static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
if (msbuild_tools_path.length()) {
if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\";
- // FrameworkPathOverride
- const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
- if (mono_reg_info.assembly_dir.length()) {
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
-
- String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
- *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path);
- } else {
- ERR_PRINT("Cannot find Mono's assemblies directory in the registry");
- }
-
- return;
+ return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
}
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
- } // fall through
+ OS::get_singleton()->print("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...\n");
+ } // FALL THROUGH
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
if (!FileAccess::exists(msbuild_path)) {
- WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path);
+ WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
}
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path);
+ return GDMonoMarshal::mono_string_from_godot(msbuild_path);
+ } break;
+ case GodotSharpBuilds::XBUILD: {
+ String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
+
+ if (!FileAccess::exists(xbuild_path)) {
+ WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
+ }
- return;
+ return GDMonoMarshal::mono_string_from_godot(xbuild_path);
} break;
default:
ERR_EXPLAIN("You don't deserve to live");
@@ -126,31 +125,74 @@ void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, Mono
static String msbuild_path = _find_build_engine_on_unix("msbuild");
static String xbuild_path = _find_build_engine_on_unix("xbuild");
- if (build_tool != GodotSharpBuilds::XBUILD) {
- if (msbuild_path.empty()) {
- WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
- return;
+ if (build_tool == GodotSharpBuilds::XBUILD) {
+ if (xbuild_path.empty()) {
+ WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
+ return NULL;
}
} else {
- if (xbuild_path.empty()) {
- WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool').");
- return;
+ if (msbuild_path.empty()) {
+ WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
+ return NULL;
}
}
- *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+ return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+#else
+ (void)build_tool; // UNUSED
+
+ ERR_EXPLAIN("Not implemented on this platform");
+ ERR_FAIL_V(NULL);
+#endif
+}
+
+MonoString *godot_icall_BuildInstance_get_FrameworkPath() {
+
+#if defined(WINDOWS_ENABLED)
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.assembly_dir.length()) {
+ String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
+ return GDMonoMarshal::mono_string_from_godot(framework_path);
+ }
- return;
+ ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry");
+ ERR_FAIL_V(NULL);
#else
- ERR_PRINT("Not implemented on this platform");
- return;
+ return NULL;
+#endif
+}
+
+MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
+
+#if defined(WINDOWS_ENABLED)
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.bin_dir.length()) {
+ return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir);
+ }
+
+ ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry");
+ ERR_FAIL_V(NULL);
+#else
+ return NULL;
+#endif
+}
+
+MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
+
+#if defined(WINDOWS_ENABLED)
+ return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO;
+#else
+ return false;
#endif
}
void GodotSharpBuilds::_register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
- mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@@ -178,13 +220,15 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
return true;
}
-bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) {
+bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
String assembly_file = p_assembly_name + ".dll";
String assembly_src = p_src_dir.plus_file(assembly_file);
String assembly_dst = p_dst_dir.plus_file(assembly_file);
- if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) {
+ if (!FileAccess::exists(assembly_dst) ||
+ FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
+ GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
@@ -200,36 +244,49 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
memdelete(da);
if (err != OK) {
- show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll");
+ show_build_error_dialog("Failed to copy " + assembly_file);
return false;
}
+
+ GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
}
return true;
}
-bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
+String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
+
+ uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
+ GDMono::get_singleton()->get_api_core_hash() :
+ GDMono::get_singleton()->get_api_editor_hash();
+ return String::num_uint64(api_hash) +
+ "_" + String::num_uint64(BindingsGenerator::get_version()) +
+ "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version());
+}
+
+bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
- String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+ String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
String api_build_config = "Release";
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
pr.step("Generating " + api_name + " solution");
- uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash();
- uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash();
+ String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_CORE))
+ .plus_file(API_ASSEMBLY_NAME);
+ String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
+ .plus_file(EDITOR_API_ASSEMBLY_NAME);
- String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash));
- String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash));
-
- String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir;
+ String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
String core_api_assembly;
- if (p_api_type == API_EDITOR) {
+ if (p_api_type == APIAssembly::API_EDITOR) {
core_api_assembly = core_api_sln_dir.plus_file("bin")
.plus_file(api_build_config)
.plus_file(API_ASSEMBLY_NAME ".dll");
@@ -242,7 +299,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
- Error err = p_api_type == API_CORE ?
+ Error err = p_api_type == APIAssembly::API_CORE ?
gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
@@ -275,7 +332,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
// Copy the built assembly to the assemblies directory
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
- if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name))
+ if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
return false;
pr.step("Done");
@@ -283,22 +340,22 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
return true;
}
-bool GodotSharpBuilds::build_project_blocking() {
+bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
pr.step("Building project solution");
- MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools");
+ MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
return false;
@@ -309,6 +366,11 @@ bool GodotSharpBuilds::build_project_blocking() {
return true;
}
+bool GodotSharpBuilds::editor_build_callback() {
+
+ return build_project_blocking("Tools");
+}
+
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
@@ -362,25 +424,19 @@ GodotSharpBuilds::GodotSharpBuilds() {
singleton = this;
- EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking);
+ EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
-#ifdef WINDOWS_ENABLED
- // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version
- EDITOR_DEF("mono/builds/build_tool", MSBUILD);
-#else
EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
-#endif
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
+ PROP_NAME_MSBUILD_MONO
#ifdef WINDOWS_ENABLED
- "MSBuild (Mono),MSBuild (System)"
-#else
- "MSBuild (Mono),xbuild (Deprecated)"
+ "," PROP_NAME_MSBUILD_VS
#endif
- ));
+ "," PROP_NAME_XBUILD));
}
GodotSharpBuilds::~GodotSharpBuilds() {
@@ -405,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);
}
@@ -432,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);
@@ -462,6 +518,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) {
exited = true;
+ GDMonoUtils::print_unhandled_exception(ex);
String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
@@ -470,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 };
@@ -482,6 +540,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) {
exited = true;
+ GDMonoUtils::print_unhandled_exception(ex);
String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
@@ -504,11 +563,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
}
}
-GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
-
- build_info = p_build_info;
- build_tab = NULL;
- exit_callback = p_callback;
- exited = true;
- exit_code = -1;
+GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
+ build_info(p_build_info),
+ build_tab(NULL),
+ exit_callback(p_callback),
+ exited(true),
+ exit_code(-1) {
}
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 5d2390ecd9..4afc284d45 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -31,6 +31,7 @@
#ifndef GODOTSHARP_BUILDS_H
#define GODOTSHARP_BUILDS_H
+#include "../mono_gd/gd_mono.h"
#include "mono_bottom_panel.h"
#include "mono_build_info.h"
@@ -56,24 +57,20 @@ private:
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
+ static String _api_folder_name(APIAssembly::Type p_api_type);
+
static GodotSharpBuilds *singleton;
friend class GDMono;
static void _register_internal_calls();
public:
- enum APIType {
- API_CORE,
- API_EDITOR
- };
-
enum BuildTool {
MSBUILD_MONO,
#ifdef WINDOWS_ENABLED
- MSBUILD
-#else
- XBUILD // Deprecated
+ MSBUILD_VS,
#endif
+ XBUILD // Deprecated
};
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
@@ -89,11 +86,13 @@ public:
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
- static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name);
+ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
+
+ static bool make_api_sln(APIAssembly::Type p_api_type);
- static bool make_api_sln(APIType p_api_type);
+ static bool build_project_blocking(const String &p_config);
- static bool build_project_blocking();
+ static bool editor_build_callback();
GodotSharpBuilds();
~GodotSharpBuilds();
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 0ef3adfdd0..998da8bda3 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -41,6 +41,7 @@
#include "../utils/path_utils.h"
#include "bindings_generator.h"
#include "csharp_project.h"
+#include "godotsharp_export.h"
#include "net_solution.h"
#ifdef WINDOWS_ENABLED
@@ -84,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() {
return false;
}
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
pr.step(TTR("Done"));
@@ -278,10 +279,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
String about_text =
String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
"It is at the alpha stage and thus not suitable for use in production.\n\n" +
- "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" +
+ "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " +
+ "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " +
+ "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
"The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " +
- "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" +
+ "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" +
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
@@ -315,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
EditorSettings *ed_settings = EditorSettings::get_singleton();
EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code"));
+
+ // Export plugin
+ Ref<GodotSharpExport> godotsharp_export;
+ godotsharp_export.instance();
+ EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
}
GodotSharpEditor::~GodotSharpEditor() {
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 81c49aec30..66da814c8b 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -32,7 +32,6 @@
#define GODOTSHARP_EDITOR_H
#include "godotsharp_builds.h"
-
#include "monodevelop_instance.h"
class GodotSharpEditor : public Node {
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
new file mode 100644
index 0000000000..cd09e6516a
--- /dev/null
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* godotsharp_export.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godotsharp_export.h"
+
+#include "../csharp_script.h"
+#include "../godotsharp_defs.h"
+#include "../godotsharp_dirs.h"
+#include "godotsharp_builds.h"
+
+void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
+
+ if (p_type != CSharpLanguage::get_singleton()->get_type())
+ return;
+
+ ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension());
+
+ // TODO what if the source file is not part of the game's C# project
+
+ if (!GLOBAL_GET("mono/export/include_scripts_content")) {
+ // We don't want to include the source code on exported games
+ add_file(p_path, Vector<uint8_t>(), false);
+ skip();
+ }
+}
+
+void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
+
+ // TODO right now there is no way to stop the export process with an error
+
+ ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
+ ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain());
+
+ String build_config = p_debug ? "Debug" : "Release";
+
+ ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
+
+ // Add API assemblies
+
+ String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll");
+ ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path));
+
+ String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path));
+
+ // Add project assembly
+
+ String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (project_dll_name.empty()) {
+ project_dll_name = "UnnamedProject";
+ }
+
+ String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll");
+ String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll");
+ ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path));
+
+ // Add dependencies
+
+ MonoDomain *prev_domain = mono_domain_get();
+ MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
+
+ ERR_FAIL_COND(!export_domain);
+ ERR_FAIL_COND(!mono_domain_set(export_domain, false));
+
+ Map<String, String> dependencies;
+ dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path());
+
+ GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true);
+
+ ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
+ ERR_FAIL_COND(!scripts_assembly);
+
+ Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies);
+
+ GDMono::get_singleton()->finalize_and_unload_domain(export_domain);
+ mono_domain_set(prev_domain, false);
+
+ ERR_FAIL_COND(depend_error != OK);
+
+ for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
+ String depend_src_path = E->value();
+ String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
+ ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
+ }
+}
+
+bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
+
+ FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
+ ERR_FAIL_COND_V(!f, false);
+
+ Vector<uint8_t> data;
+ data.resize(f->get_len());
+ f->get_buffer(data.ptrw(), data.size());
+
+ add_file(p_dst_path, data, false);
+
+ return true;
+}
+
+Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) {
+
+ MonoImage *image = p_assembly->get_image();
+
+ for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
+ MonoAssemblyName *ref_aname = aname_prealloc;
+ mono_assembly_get_assemblyref(image, i, ref_aname);
+ String ref_name = mono_assembly_name_get_name(ref_aname);
+
+ if (ref_name == "mscorlib" || r_dependencies.find(ref_name))
+ continue;
+
+ GDMonoAssembly *ref_assembly = NULL;
+ if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) {
+ ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name);
+ ERR_FAIL_V(ERR_CANT_RESOLVE);
+ }
+
+ r_dependencies.insert(ref_name, ref_assembly->get_path());
+
+ Error err = _get_assembly_dependencies(ref_assembly, r_dependencies);
+ if (err != OK)
+ return err;
+ }
+
+ return OK;
+}
+
+GodotSharpExport::GodotSharpExport() {
+ // MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves.
+ // There isn't any api to allocate an empty one either, so we need to do it this way.
+ aname_prealloc = mono_assembly_name_new("whatever");
+ mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included)
+}
+
+GodotSharpExport::~GodotSharpExport() {
+ if (aname_prealloc)
+ mono_free(aname_prealloc);
+}
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
new file mode 100644
index 0000000000..b38db9660c
--- /dev/null
+++ b/modules/mono/editor/godotsharp_export.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* godotsharp_export.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOTSHARP_EXPORT_H
+#define GODOTSHARP_EXPORT_H
+
+#include <mono/metadata/image.h>
+
+#include "editor/editor_export.h"
+
+#include "../mono_gd/gd_mono_header.h"
+
+class GodotSharpExport : public EditorExportPlugin {
+
+ MonoAssemblyName *aname_prealloc;
+
+ bool _add_assembly(const String &p_src_path, const String &p_dst_path);
+
+ Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies);
+
+protected:
+ virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
+ virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
+
+public:
+ GodotSharpExport();
+ ~GodotSharpExport();
+};
+
+#endif // GODOTSHARP_EXPORT_H
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 43689548b5..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) {
@@ -142,12 +148,28 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
void MonoBottomPanel::_build_project_pressed() {
- GodotSharpBuilds::get_singleton()->build_project_blocking();
+ GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
MonoReloadNode::get_singleton()->restart_reload_timer();
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) {
@@ -197,7 +220,7 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
panel_builds_tab->add_child(toolbar_hbc);
- ToolButton *build_project_btn = memnew(ToolButton);
+ Button *build_project_btn = memnew(Button);
build_project_btn->set_text(TTR("Build Project"));
build_project_btn->set_focus_mode(FOCUS_NONE);
build_project_btn->connect("pressed", this, "_build_project_pressed");
@@ -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);
}
}
@@ -335,16 +367,14 @@ void MonoBuildTab::_update_issues_list() {
Ref<Texture> MonoBuildTab::get_icon_texture() const {
- // FIXME these icons were removed... find something better
-
if (build_exited) {
if (build_result == RESULT_ERROR) {
- return get_icon("DependencyChangedHl", "EditorIcons");
+ return get_icon("StatusError", "EditorIcons");
} else {
- return get_icon("DependencyOkHl", "EditorIcons");
+ return get_icon("StatusSuccess", "EditorIcons");
}
} else {
- return get_icon("GraphTime", "EditorIcons");
+ return get_icon("Stop", "EditorIcons");
}
}
@@ -409,9 +439,14 @@ void MonoBuildTab::stop_build() {
void MonoBuildTab::_issue_activated(int p_idx) {
- ERR_FAIL_INDEX(p_idx, issues.size());
+ ERR_FAIL_INDEX(p_idx, issues_list->get_item_count());
- const BuildIssue &issue = issues[p_idx];
+ // Get correct issue idx from issue list
+ int issue_idx = this->issues_list->get_item_metadata(p_idx);
+
+ ERR_FAIL_INDEX(issue_idx, issues.size());
+
+ const BuildIssue &issue = issues[issue_idx];
if (issue.project_file.empty() && issue.file.empty())
return;
@@ -439,21 +474,16 @@ void MonoBuildTab::_bind_methods() {
ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
}
-MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) {
-
- build_info = p_build_info;
- logs_dir = p_logs_dir;
-
- build_exited = false;
-
- issues_list = memnew(ItemList);
+MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) :
+ build_info(p_build_info),
+ logs_dir(p_logs_dir),
+ build_exited(false),
+ issues_list(memnew(ItemList)),
+ error_count(0),
+ warning_count(0),
+ errors_visible(true),
+ warnings_visible(true) {
issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
issues_list->connect("item_activated", this, "_issue_activated");
add_child(issues_list);
-
- error_count = 0;
- warning_count = 0;
-
- errors_visible = true;
- warnings_visible = true;
}
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/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h
index 7e8a76b595..552c10a61d 100644
--- a/modules/mono/editor/monodevelop_instance.h
+++ b/modules/mono/editor/monodevelop_instance.h
@@ -43,7 +43,7 @@ class MonoDevelopInstance {
public:
void execute(const Vector<String> &p_files);
- void execute(const String &p_files);
+ void execute(const String &p_file);
MonoDevelopInstance(const String &p_solution);
};
diff --git a/modules/mono/glue/builtin_types_glue.h b/modules/mono/glue/builtin_types_glue.h
index 460de84b65..ef9f152682 100644
--- a/modules/mono/glue/builtin_types_glue.h
+++ b/modules/mono/glue/builtin_types_glue.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* builtin_types_glue.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 BUILTIN_TYPES_GLUE_H
#define BUILTIN_TYPES_GLUE_H
diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs
index e6e12f7ba3..39f2d2ed51 100644
--- a/modules/mono/glue/cs_files/AABB.cs
+++ b/modules/mono/glue/cs_files/AABB.cs
@@ -1,11 +1,15 @@
-using System;
-
// file: core/math/aabb.h
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/math/aabb.cpp
// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
+using System;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -45,12 +49,12 @@ namespace Godot
Vector3 dst_min = with.position;
Vector3 dst_max = with.position + with.size;
- return ((src_min.x <= dst_min.x) &&
- (src_max.x > dst_max.x) &&
- (src_min.y <= dst_min.y) &&
- (src_max.y > dst_max.y) &&
- (src_min.z <= dst_min.z) &&
- (src_max.z > dst_max.z));
+ return src_min.x <= dst_min.x &&
+ src_max.x > dst_max.x &&
+ src_min.y <= dst_min.y &&
+ src_max.y > dst_max.y &&
+ src_min.z <= dst_min.z &&
+ src_max.z > dst_max.z;
}
public AABB Expand(Vector3 to_point)
@@ -75,7 +79,7 @@ namespace Godot
return new AABB(begin, end - begin);
}
- public float GetArea()
+ public real_t GetArea()
{
return size.x * size.y * size.z;
}
@@ -107,8 +111,8 @@ namespace Godot
public Vector3 GetLongestAxis()
{
- Vector3 axis = new Vector3(1f, 0f, 0f);
- float max_size = size.x;
+ var axis = new Vector3(1f, 0f, 0f);
+ real_t max_size = size.x;
if (size.y > max_size)
{
@@ -119,7 +123,6 @@ namespace Godot
if (size.z > max_size)
{
axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
}
return axis;
@@ -127,8 +130,8 @@ namespace Godot
public Vector3.Axis GetLongestAxisIndex()
{
- Vector3.Axis axis = Vector3.Axis.X;
- float max_size = size.x;
+ var axis = Vector3.Axis.X;
+ real_t max_size = size.x;
if (size.y > max_size)
{
@@ -139,15 +142,14 @@ namespace Godot
if (size.z > max_size)
{
axis = Vector3.Axis.Z;
- max_size = size.z;
}
return axis;
}
- public float GetLongestAxisSize()
+ public real_t GetLongestAxisSize()
{
- float max_size = size.x;
+ real_t max_size = size.x;
if (size.y > max_size)
max_size = size.y;
@@ -160,8 +162,8 @@ namespace Godot
public Vector3 GetShortestAxis()
{
- Vector3 axis = new Vector3(1f, 0f, 0f);
- float max_size = size.x;
+ var axis = new Vector3(1f, 0f, 0f);
+ real_t max_size = size.x;
if (size.y < max_size)
{
@@ -172,7 +174,6 @@ namespace Godot
if (size.z < max_size)
{
axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
}
return axis;
@@ -180,8 +181,8 @@ namespace Godot
public Vector3.Axis GetShortestAxisIndex()
{
- Vector3.Axis axis = Vector3.Axis.X;
- float max_size = size.x;
+ var axis = Vector3.Axis.X;
+ real_t max_size = size.x;
if (size.y < max_size)
{
@@ -192,15 +193,14 @@ namespace Godot
if (size.z < max_size)
{
axis = Vector3.Axis.Z;
- max_size = size.z;
}
return axis;
}
- public float GetShortestAxisSize()
+ public real_t GetShortestAxisSize()
{
- float max_size = size.x;
+ real_t max_size = size.x;
if (size.y < max_size)
max_size = size.y;
@@ -217,14 +217,14 @@ namespace Godot
Vector3 ofs = position + half_extents;
return ofs + new Vector3(
- (dir.x > 0f) ? -half_extents.x : half_extents.x,
- (dir.y > 0f) ? -half_extents.y : half_extents.y,
- (dir.z > 0f) ? -half_extents.z : half_extents.z);
+ dir.x > 0f ? -half_extents.x : half_extents.x,
+ dir.y > 0f ? -half_extents.y : half_extents.y,
+ dir.z > 0f ? -half_extents.z : half_extents.z);
}
- public AABB Grow(float by)
+ public AABB Grow(real_t by)
{
- AABB res = this;
+ var res = this;
res.position.x -= by;
res.position.y -= by;
@@ -277,48 +277,42 @@ namespace Godot
{
return new AABB();
}
- else
- {
- min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
- max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
- }
+
+ min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x;
+ max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x;
if (src_min.y > dst_max.y || src_max.y < dst_min.y)
{
return new AABB();
}
- else
- {
- min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
- max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
- }
+
+ min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y;
+ max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y;
if (src_min.z > dst_max.z || src_max.z < dst_min.z)
{
return new AABB();
}
- else
- {
- min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
- max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
- }
+
+ min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z;
+ max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z;
return new AABB(min, max - min);
}
public bool Intersects(AABB with)
{
- if (position.x >= (with.position.x + with.size.x))
+ if (position.x >= with.position.x + with.size.x)
return false;
- if ((position.x + size.x) <= with.position.x)
+ if (position.x + size.x <= with.position.x)
return false;
- if (position.y >= (with.position.y + with.size.y))
+ if (position.y >= with.position.y + with.size.y)
return false;
- if ((position.y + size.y) <= with.position.y)
+ if (position.y + size.y <= with.position.y)
return false;
- if (position.z >= (with.position.z + with.size.z))
+ if (position.z >= with.position.z + with.size.z)
return false;
- if ((position.z + size.z) <= with.position.z)
+ if (position.z + size.z <= with.position.z)
return false;
return true;
@@ -335,7 +329,7 @@ namespace Godot
new Vector3(position.x + size.x, position.y, position.z),
new Vector3(position.x + size.x, position.y, position.z + size.z),
new Vector3(position.x + size.x, position.y + size.y, position.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z + size.z)
};
bool over = false;
@@ -354,23 +348,23 @@ namespace Godot
public bool IntersectsSegment(Vector3 from, Vector3 to)
{
- float min = 0f;
- float max = 1f;
+ real_t min = 0f;
+ real_t max = 1f;
for (int i = 0; i < 3; i++)
{
- float seg_from = from[i];
- float seg_to = to[i];
- float box_begin = position[i];
- float box_end = box_begin + size[i];
- float cmin, cmax;
+ real_t seg_from = from[i];
+ real_t seg_to = to[i];
+ real_t box_begin = position[i];
+ real_t box_end = box_begin + size[i];
+ real_t cmin, cmax;
if (seg_from < seg_to)
{
if (seg_from > box_end || seg_to < box_begin)
return false;
- float length = seg_to - seg_from;
+ real_t length = seg_to - seg_from;
cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f;
cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f;
}
@@ -379,7 +373,7 @@ namespace Godot
if (seg_to > box_end || seg_from < box_begin)
return false;
- float length = seg_to - seg_from;
+ real_t length = seg_to - seg_from;
cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f;
cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f;
}
@@ -402,24 +396,25 @@ namespace Godot
{
Vector3 beg_1 = position;
Vector3 beg_2 = with.position;
- Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
- Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
+ var end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
+ var end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
- Vector3 min = new Vector3(
- (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
- (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
- (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
+ var min = new Vector3(
+ beg_1.x < beg_2.x ? beg_1.x : beg_2.x,
+ beg_1.y < beg_2.y ? beg_1.y : beg_2.y,
+ beg_1.z < beg_2.z ? beg_1.z : beg_2.z
);
- Vector3 max = new Vector3(
- (end_1.x > end_2.x) ? end_1.x : end_2.x,
- (end_1.y > end_2.y) ? end_1.y : end_2.y,
- (end_1.z > end_2.z) ? end_1.z : end_2.z
+ var max = new Vector3(
+ end_1.x > end_2.x ? end_1.x : end_2.x,
+ end_1.y > end_2.y ? end_1.y : end_2.y,
+ end_1.z > end_2.z ? end_1.z : end_2.z
);
return new AABB(min, max - min);
}
-
+
+ // Constructors
public AABB(Vector3 position, Vector3 size)
{
this.position = position;
@@ -460,8 +455,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.position.ToString(),
- this.size.ToString()
+ position.ToString(),
+ size.ToString()
});
}
@@ -469,8 +464,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.position.ToString(format),
- this.size.ToString(format)
+ position.ToString(format),
+ size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index ea92b1641b..aa49a5e04f 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -1,5 +1,10 @@
using System;
using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -13,8 +18,7 @@ namespace Godot
new Vector3(0f, 0f, 1f)
);
- private static readonly Basis[] orthoBases = new Basis[24]
- {
+ private static readonly Basis[] orthoBases = {
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f),
@@ -41,9 +45,27 @@ namespace Godot
new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f)
};
- public Vector3 x;
- public Vector3 y;
- public Vector3 z;
+ public Vector3 x
+ {
+ get { return GetAxis(0); }
+ set { SetAxis(0, value); }
+ }
+
+ public Vector3 y
+ {
+ get { return GetAxis(1); }
+ set { SetAxis(1, value); }
+ }
+
+ public Vector3 z
+ {
+ get { return GetAxis(2); }
+ set { SetAxis(2, value); }
+ }
+
+ private Vector3 _x;
+ private Vector3 _y;
+ private Vector3 _z;
public static Basis Identity
{
@@ -70,11 +92,11 @@ namespace Godot
switch (index)
{
case 0:
- return x;
+ return _x;
case 1:
- return y;
+ return _y;
case 2:
- return z;
+ return _z;
default:
throw new IndexOutOfRangeException();
}
@@ -84,13 +106,13 @@ namespace Godot
switch (index)
{
case 0:
- x = value;
+ _x = value;
return;
case 1:
- y = value;
+ _y = value;
return;
case 2:
- z = value;
+ _z = value;
return;
default:
throw new IndexOutOfRangeException();
@@ -98,18 +120,18 @@ namespace Godot
}
}
- public float this[int index, int axis]
+ public real_t this[int index, int axis]
{
get
{
switch (index)
{
case 0:
- return x[axis];
+ return _x[axis];
case 1:
- return y[axis];
+ return _y[axis];
case 2:
- return z[axis];
+ return _z[axis];
default:
throw new IndexOutOfRangeException();
}
@@ -119,13 +141,13 @@ namespace Godot
switch (index)
{
case 0:
- x[axis] = value;
+ _x[axis] = value;
return;
case 1:
- y[axis] = value;
+ _y[axis] = value;
return;
case 2:
- z[axis] = value;
+ _z[axis] = value;
return;
default:
throw new IndexOutOfRangeException();
@@ -143,7 +165,7 @@ namespace Godot
);
}
- public float Determinant()
+ public real_t Determinant()
{
return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) -
this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) +
@@ -155,14 +177,21 @@ namespace Godot
return new Vector3(this[0, axis], this[1, axis], this[2, axis]);
}
+ public void SetAxis(int axis, Vector3 value)
+ {
+ this[0, axis] = value.x;
+ this[1, axis] = value.y;
+ this[2, axis] = value.z;
+ }
+
public Vector3 GetEuler()
{
- Basis m = this.Orthonormalized();
+ Basis m = Orthonormalized();
Vector3 euler;
euler.z = 0.0f;
- float mxy = m.y[2];
+ real_t mxy = m[1, 2];
if (mxy < 1.0f)
@@ -170,19 +199,19 @@ namespace Godot
if (mxy > -1.0f)
{
euler.x = Mathf.Asin(-mxy);
- euler.y = Mathf.Atan2(m.x[2], m.z[2]);
- euler.z = Mathf.Atan2(m.y[0], m.y[1]);
+ euler.y = Mathf.Atan2(m[0, 2], m[2, 2]);
+ euler.z = Mathf.Atan2(m[1, 0], m[1, 1]);
}
else
{
- euler.x = Mathf.PI * 0.5f;
- euler.y = -Mathf.Atan2(-m.x[1], m.x[0]);
+ euler.x = Mathf.Pi * 0.5f;
+ euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
}
else
{
- euler.x = -Mathf.PI * 0.5f;
- euler.y = -Mathf.Atan2(m.x[1], m.x[0]);
+ euler.x = -Mathf.Pi * 0.5f;
+ euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
return euler;
@@ -190,13 +219,13 @@ namespace Godot
public int GetOrthogonalIndex()
{
- Basis orth = this;
+ var orth = this;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
- float v = orth[i, j];
+ real_t v = orth[i, j];
if (v > 0.5f)
v = 1.0f;
@@ -220,28 +249,27 @@ namespace Godot
public Basis Inverse()
{
- Basis inv = this;
+ var inv = this;
- float[] co = new float[3]
- {
+ real_t[] co = {
inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1],
inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2],
inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0]
};
- float det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2];
+ real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2];
if (det == 0)
{
return new Basis
(
- float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN
+ real_t.NaN, real_t.NaN, real_t.NaN,
+ real_t.NaN, real_t.NaN, real_t.NaN,
+ real_t.NaN, real_t.NaN, real_t.NaN
);
}
- float s = 1.0f / det;
+ real_t s = 1.0f / det;
inv = new Basis
(
@@ -266,22 +294,22 @@ namespace Godot
Vector3 zAxis = GetAxis(2);
xAxis.Normalize();
- yAxis = (yAxis - xAxis * (xAxis.Dot(yAxis)));
+ yAxis = yAxis - xAxis * xAxis.Dot(yAxis);
yAxis.Normalize();
- zAxis = (zAxis - xAxis * (xAxis.Dot(zAxis)) - yAxis * (yAxis.Dot(zAxis)));
+ zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis);
zAxis.Normalize();
- return Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ return CreateFromAxes(xAxis, yAxis, zAxis);
}
- public Basis Rotated(Vector3 axis, float phi)
+ public Basis Rotated(Vector3 axis, real_t phi)
{
return new Basis(axis, phi) * this;
}
public Basis Scaled(Vector3 scale)
{
- Basis m = this;
+ var m = this;
m[0, 0] *= scale.x;
m[0, 1] *= scale.x;
@@ -296,26 +324,26 @@ namespace Godot
return m;
}
- public float Tdotx(Vector3 with)
+ public real_t Tdotx(Vector3 with)
{
return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2];
}
- public float Tdoty(Vector3 with)
+ public real_t Tdoty(Vector3 with)
{
return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2];
}
- public float Tdotz(Vector3 with)
+ public real_t Tdotz(Vector3 with)
{
return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2];
}
public Basis Transposed()
{
- Basis tr = this;
+ var tr = this;
- float temp = this[0, 1];
+ real_t temp = this[0, 1];
this[0, 1] = this[1, 0];
this[1, 0] = temp;
@@ -344,98 +372,122 @@ namespace Godot
{
return new Vector3
(
- (this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z),
- (this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z),
- (this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z)
+ this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z,
+ this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z,
+ this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z
);
}
public Quat Quat() {
- float trace = x[0] + y[1] + z[2];
+ real_t trace = _x[0] + _y[1] + _z[2];
if (trace > 0.0f) {
- float s = Mathf.Sqrt(trace + 1.0f) * 2f;
- float inv_s = 1f / s;
+ real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
return new Quat(
- (z[1] - y[2]) * inv_s,
- (x[2] - z[0]) * inv_s,
- (y[0] - x[1]) * inv_s,
+ (_z[1] - _y[2]) * inv_s,
+ (_x[2] - _z[0]) * inv_s,
+ (_y[0] - _x[1]) * inv_s,
s * 0.25f
);
- } else if (x[0] > y[1] && x[0] > z[2]) {
- float s = Mathf.Sqrt(x[0] - y[1] - z[2] + 1.0f) * 2f;
- float inv_s = 1f / s;
- return new Quat(
- s * 0.25f,
- (x[1] + y[0]) * inv_s,
- (x[2] + z[0]) * inv_s,
- (z[1] - y[2]) * inv_s
- );
- } else if (y[1] > z[2]) {
- float s = Mathf.Sqrt(-x[0] + y[1] - z[2] + 1.0f) * 2f;
- float inv_s = 1f / s;
- return new Quat(
- (x[1] + y[0]) * inv_s,
- s * 0.25f,
- (y[2] + z[1]) * inv_s,
- (x[2] - z[0]) * inv_s
- );
- } else {
- float s = Mathf.Sqrt(-x[0] - y[1] + z[2] + 1.0f) * 2f;
- float inv_s = 1f / s;
- return new Quat(
- (x[2] + z[0]) * inv_s,
- (y[2] + z[1]) * inv_s,
- s * 0.25f,
- (y[0] - x[1]) * inv_s
- );
}
+
+ if (_x[0] > _y[1] && _x[0] > _z[2]) {
+ real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ s * 0.25f,
+ (_x[1] + _y[0]) * inv_s,
+ (_x[2] + _z[0]) * inv_s,
+ (_z[1] - _y[2]) * inv_s
+ );
+ }
+
+ if (_y[1] > _z[2]) {
+ real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ (_x[1] + _y[0]) * inv_s,
+ s * 0.25f,
+ (_y[2] + _z[1]) * inv_s,
+ (_x[2] - _z[0]) * inv_s
+ );
+ } else {
+ real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ (_x[2] + _z[0]) * inv_s,
+ (_y[2] + _z[1]) * inv_s,
+ s * 0.25f,
+ (_y[0] - _x[1]) * inv_s
+ );
+ }
}
public Basis(Quat quat)
{
- float s = 2.0f / quat.LengthSquared();
+ real_t s = 2.0f / quat.LengthSquared();
+
+ real_t xs = quat.x * s;
+ real_t ys = quat.y * s;
+ real_t zs = quat.z * s;
+ real_t wx = quat.w * xs;
+ real_t wy = quat.w * ys;
+ real_t wz = quat.w * zs;
+ real_t xx = quat.x * xs;
+ real_t xy = quat.x * ys;
+ real_t xz = quat.x * zs;
+ real_t yy = quat.y * ys;
+ real_t yz = quat.y * zs;
+ real_t zz = quat.z * zs;
+
+ _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
+ _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
+ _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
+ }
+
+ public Basis(Vector3 euler)
+ {
+ real_t c;
+ real_t s;
+
+ c = Mathf.Cos(euler.x);
+ s = Mathf.Sin(euler.x);
+ var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
+
+ c = Mathf.Cos(euler.y);
+ s = Mathf.Sin(euler.y);
+ var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
- float xs = quat.x * s;
- float ys = quat.y * s;
- float zs = quat.z * s;
- float wx = quat.w * xs;
- float wy = quat.w * ys;
- float wz = quat.w * zs;
- float xx = quat.x * xs;
- float xy = quat.x * ys;
- float xz = quat.x * zs;
- float yy = quat.y * ys;
- float yz = quat.y * zs;
- float zz = quat.z * zs;
+ c = Mathf.Cos(euler.z);
+ s = Mathf.Sin(euler.z);
+ var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
- this.x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
- this.y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
- this.z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
+ this = ymat * xmat * zmat;
}
- public Basis(Vector3 axis, float phi)
+ public Basis(Vector3 axis, real_t phi)
{
- Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
+ var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- float cosine = Mathf.Cos(phi);
- float sine = Mathf.Sin(phi);
+ real_t cosine = Mathf.Cos( phi);
+ real_t sine = Mathf.Sin( phi);
- this.x = new Vector3
+ _x = new Vector3
(
axis_sq.x + cosine * (1.0f - axis_sq.x),
axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
axis.z * axis.x * (1.0f - cosine) + axis.y * sine
);
- this.y = new Vector3
+ _y = new Vector3
(
axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
axis_sq.y + cosine * (1.0f - axis_sq.y),
axis.y * axis.z * (1.0f - cosine) - axis.x * sine
);
- this.z = new Vector3
+ _z = new Vector3
(
axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
@@ -445,16 +497,16 @@ namespace Godot
public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
- this.x = xAxis;
- this.y = yAxis;
- this.z = zAxis;
+ _x = xAxis;
+ _y = yAxis;
+ _z = zAxis;
}
- public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz)
+ public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
{
- this.x = new Vector3(xx, xy, xz);
- this.y = new Vector3(yx, yy, yz);
- this.z = new Vector3(zx, zy, zz);
+ _x = new Vector3(xx, xy, xz);
+ _y = new Vector3(yx, yy, yz);
+ _z = new Vector3(zx, zy, zz);
}
public static Basis operator *(Basis left, Basis right)
@@ -489,21 +541,21 @@ namespace Godot
public bool Equals(Basis other)
{
- return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z);
+ return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]);
}
public override int GetHashCode()
{
- return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
+ return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(),
- this.y.ToString(),
- this.z.ToString()
+ _x.ToString(),
+ _y.ToString(),
+ _z.ToString()
});
}
@@ -511,9 +563,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format)
+ _x.ToString(format),
+ _y.ToString(format),
+ _z.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index f9e31e9703..e0d6d27840 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -45,8 +45,8 @@ namespace Godot
{
get
{
- float max = Mathf.Max(r, Mathf.Max(g, b));
- float min = Mathf.Min(r, Mathf.Min(g, b));
+ float max = Math.Max(r, Math.Max(g, b));
+ float min = Math.Min(r, Math.Min(g, b));
float delta = max - min;
@@ -79,8 +79,8 @@ namespace Godot
{
get
{
- float max = Mathf.Max(r, Mathf.Max(g, b));
- float min = Mathf.Min(r, Mathf.Min(g, b));
+ float max = Math.Max(r, Math.Max(g, b));
+ float min = Math.Min(r, Math.Min(g, b));
float delta = max - min;
@@ -96,7 +96,7 @@ namespace Godot
{
get
{
- return Mathf.Max(r, Mathf.Max(g, b));
+ return Math.Max(r, Math.Max(g, b));
}
set
{
@@ -104,7 +104,7 @@ namespace Godot
}
}
- private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
+ private static readonly Color black = new Color(0f, 0f, 0f);
public Color Black
{
@@ -180,7 +180,7 @@ namespace Godot
hue += 1.0f;
}
- saturation = (max == 0) ? 0 : 1f - (1f * min / max);
+ saturation = max == 0 ? 0 : 1f - 1f * min / max;
value = max / 255f;
}
@@ -232,12 +232,10 @@ namespace Godot
{
return new Color(0, 0, 0, 0);
}
- else
- {
- res.r = (r * a * sa + over.r * over.a) / res.a;
- res.g = (g * a * sa + over.g * over.a) / res.a;
- res.b = (b * a * sa + over.b * over.a) / res.a;
- }
+
+ res.r = (r * a * sa + over.r * over.a) / res.a;
+ res.g = (g * a * sa + over.g * over.a) / res.a;
+ res.b = (b * a * sa + over.b * over.a) / res.a;
return res;
}
@@ -251,6 +249,15 @@ namespace Godot
);
}
+ public Color Darkened(float amount)
+ {
+ Color res = this;
+ res.r = res.r * (1.0f - amount);
+ res.g = res.g * (1.0f - amount);
+ res.b = res.b * (1.0f - amount);
+ return res;
+ }
+
public float Gray()
{
return (r + g + b) / 3.0f;
@@ -265,27 +272,36 @@ namespace Godot
);
}
- public Color LinearInterpolate(Color b, float t)
+ public Color Lightened(float amount)
{
Color res = this;
+ res.r = res.r + (1.0f - res.r) * amount;
+ res.g = res.g + (1.0f - res.g) * amount;
+ res.b = res.b + (1.0f - res.b) * amount;
+ return res;
+ }
- res.r += (t * (b.r - this.r));
- res.g += (t * (b.g - this.g));
- res.b += (t * (b.b - this.b));
- res.a += (t * (b.a - this.a));
+ public Color LinearInterpolate(Color c, float t)
+ {
+ var res = this;
+
+ res.r += t * (c.r - r);
+ res.g += t * (c.g - g);
+ res.b += t * (c.b - b);
+ res.a += t * (c.a - a);
return res;
}
- public int To32()
+ public int ToRgba32()
{
- int c = (byte)(a * 255);
- c <<= 8;
- c |= (byte)(r * 255);
+ int c = (byte)(r * 255);
c <<= 8;
c |= (byte)(g * 255);
c <<= 8;
c |= (byte)(b * 255);
+ c <<= 8;
+ c |= (byte)(a * 255);
return c;
}
@@ -305,7 +321,7 @@ namespace Godot
public string ToHtml(bool include_alpha = true)
{
- String txt = string.Empty;
+ var txt = string.Empty;
txt += _to_hex(r);
txt += _to_hex(g);
@@ -316,7 +332,8 @@ namespace Godot
return txt;
}
-
+
+ // Constructors
public Color(float r, float g, float b, float a = 1.0f)
{
this.r = r;
@@ -327,13 +344,13 @@ namespace Godot
public Color(int rgba)
{
- this.a = (rgba & 0xFF) / 255.0f;
+ a = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.b = (rgba & 0xFF) / 255.0f;
+ b = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.g = (rgba & 0xFF) / 255.0f;
+ g = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.r = (rgba & 0xFF) / 255.0f;
+ r = (rgba & 0xFF) / 255.0f;
}
private static int _parse_col(string str, int ofs)
@@ -343,7 +360,7 @@ namespace Godot
for (int i = 0; i < 2; i++)
{
int c = str[i + ofs];
- int v = 0;
+ int v;
if (c >= '0' && c <= '9')
{
@@ -375,9 +392,9 @@ namespace Godot
private String _to_hex(float val)
{
- int v = (int)Mathf.Clamp(val * 255.0f, 0, 255);
+ var v = (int) Mathf.Clamp(val * 255.0f, 0, 255);
- string ret = string.Empty;
+ var ret = string.Empty;
for (int i = 0; i < 2; i++)
{
@@ -404,7 +421,7 @@ namespace Godot
if (color[0] == '#')
color = color.Substring(1, color.Length - 1);
- bool alpha = false;
+ bool alpha;
if (color.Length == 8)
alpha = true;
@@ -433,7 +450,7 @@ namespace Godot
public static Color Color8(byte r8, byte g8, byte b8, byte a8)
{
- return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
+ return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
}
public Color(string rgba)
@@ -450,7 +467,7 @@ namespace Godot
if (rgba[0] == '#')
rgba = rgba.Substring(1);
- bool alpha = false;
+ bool alpha;
if (rgba.Length == 8)
{
@@ -512,14 +529,11 @@ namespace Godot
if (left.g == right.g)
{
if (left.b == right.b)
- return (left.a < right.a);
- else
- return (left.b < right.b);
- }
- else
- {
- return left.g < right.g;
+ return left.a < right.a;
+ return left.b < right.b;
}
+
+ return left.g < right.g;
}
return left.r < right.r;
@@ -532,14 +546,11 @@ namespace Godot
if (left.g == right.g)
{
if (left.b == right.b)
- return (left.a > right.a);
- else
- return (left.b > right.b);
- }
- else
- {
- return left.g > right.g;
+ return left.a > right.a;
+ return left.b > right.b;
}
+
+ return left.g > right.g;
}
return left.r > right.r;
@@ -567,24 +578,12 @@ namespace Godot
public override string ToString()
{
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(),
- this.g.ToString(),
- this.b.ToString(),
- this.a.ToString()
- });
+ return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString());
}
public string ToString(string format)
{
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(format),
- this.g.ToString(format),
- this.b.ToString(format),
- this.a.ToString(format)
- });
+ return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format));
}
}
}
diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/cs_files/DebuggingUtils.cs
index ffaaf00837..b27816084e 100644
--- a/modules/mono/glue/cs_files/DebuggingUtils.cs
+++ b/modules/mono/glue/cs_files/DebuggingUtils.cs
@@ -14,7 +14,7 @@ namespace Godot
else if (type == typeof(void))
sb.Append("void");
else
- sb.Append(type.ToString());
+ sb.Append(type);
sb.Append(" ");
}
@@ -32,7 +32,7 @@ namespace Godot
return;
}
- StringBuilder sb = new StringBuilder();
+ var sb = new StringBuilder();
if (methodBase is MethodInfo)
sb.AppendTypeName(((MethodInfo)methodBase).ReturnType);
diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs
index b335ef55e4..ec1534cb9a 100644
--- a/modules/mono/glue/cs_files/GD.cs
+++ b/modules/mono/glue/cs_files/GD.cs
@@ -1,5 +1,7 @@
using System;
+// TODO: Add comments describing what this class does. It is not obvious.
+
namespace Godot
{
public static partial class GD
@@ -89,7 +91,7 @@ namespace Godot
public static int[] Range(int length)
{
- int[] ret = new int[length];
+ var ret = new int[length];
for (int i = 0; i < length; i++)
{
@@ -104,7 +106,7 @@ namespace Godot
if (to < from)
return new int[0];
- int[] ret = new int[to - from];
+ var ret = new int[to - from];
for (int i = from; i < to; i++)
{
@@ -122,14 +124,14 @@ namespace Godot
return new int[0];
// Calculate count
- int count = 0;
+ int count;
if (increment > 0)
- count = ((to - from - 1) / increment) + 1;
+ count = (to - from - 1) / increment + 1;
else
- count = ((from - to - 1) / -increment) + 1;
+ count = (from - to - 1) / -increment + 1;
- int[] ret = new int[count];
+ var ret = new int[count];
if (increment > 0)
{
diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/GodotMethodAttribute.cs
index 21333c8dab..55848769d5 100644
--- a/modules/mono/glue/cs_files/GodotMethodAttribute.cs
+++ b/modules/mono/glue/cs_files/GodotMethodAttribute.cs
@@ -2,7 +2,7 @@ using System;
namespace Godot
{
- [AttributeUsage(AttributeTargets.Method, Inherited = true)]
+ [AttributeUsage(AttributeTargets.Method)]
internal class GodotMethodAttribute : Attribute
{
private string methodName;
diff --git a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
index eb4d0bed1c..da3c7bac83 100644
--- a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
index f587645a49..3d23ec10f1 100644
--- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs
+++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
@@ -14,6 +14,7 @@ namespace Godot
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
+ SynchronizationContext.SetSynchronizationContext(Context);
}
protected sealed override void QueueTask(Task task)
@@ -36,7 +37,7 @@ namespace Godot
TryDequeue(task);
}
- return base.TryExecuteTask(task);
+ return TryExecuteTask(task);
}
protected sealed override bool TryDequeue(Task task)
@@ -57,7 +58,6 @@ namespace Godot
public void Activate()
{
- SynchronizationContext.SetSynchronizationContext(Context);
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs
index 73c71b5634..b5aa1a5389 100644
--- a/modules/mono/glue/cs_files/IAwaiter.cs
+++ b/modules/mono/glue/cs_files/IAwaiter.cs
@@ -1,4 +1,3 @@
-using System;
using System.Runtime.CompilerServices;
namespace Godot
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index 2bdfb95c51..ff4477cc6c 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -7,7 +7,7 @@ namespace Godot
{
private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
{
- Dictionary<object, object> ret = new Dictionary<object, object>();
+ var ret = new Dictionary<object, object>();
for (int i = 0; i < keys.Length; i++)
{
@@ -19,11 +19,11 @@ namespace Godot
private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
{
- Dictionary<object, object>.KeyCollection keys = from.Keys;
+ var keys = from.Keys;
keysTo = new object[keys.Count];
keys.CopyTo(keysTo, 0);
- Dictionary<object, object>.ValueCollection values = from.Values;
+ var values = from.Values;
valuesTo = new object[values.Count];
values.CopyTo(valuesTo, 0);
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index 6951ace4fc..0d20a12563 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -1,77 +1,87 @@
using System;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
- public static class Mathf
+ public static partial class Mathf
{
- public const float PI = 3.14159274f;
- public const float Epsilon = 1e-06f;
+ // Define constants with Decimal precision and cast down to double or float.
- private const float Deg2RadConst = 0.0174532924f;
- private const float Rad2DegConst = 57.29578f;
+ public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+ public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ public const real_t Inf = real_t.PositiveInfinity;
+ public const real_t NaN = real_t.NaN;
- public static float Abs(float s)
+ private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
+ private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
+
+ public static real_t Abs(real_t s)
{
return Math.Abs(s);
}
- public static float Acos(float s)
+ public static int Abs(int s)
{
- return (float)Math.Acos(s);
+ return Math.Abs(s);
}
- public static float Asin(float s)
+ public static real_t Acos(real_t s)
{
- return (float)Math.Asin(s);
+ return (real_t)Math.Acos(s);
}
- public static float Atan(float s)
+ public static real_t Asin(real_t s)
{
- return (float)Math.Atan(s);
+ return (real_t)Math.Asin(s);
}
- public static float Atan2(float x, float y)
+ public static real_t Atan(real_t s)
{
- return (float)Math.Atan2(x, y);
+ return (real_t)Math.Atan(s);
}
- public static Vector2 Cartesian2Polar(float x, float y)
- {
- return new Vector2(Sqrt(x * x + y * y), Atan2(y, x));
- }
+ public static real_t Atan2(real_t x, real_t y)
+ {
+ return (real_t)Math.Atan2(x, y);
+ }
- public static float Ceil(float s)
+ public static Vector2 Cartesian2Polar(real_t x, real_t y)
{
- return (float)Math.Ceiling(s);
+ return new Vector2(Sqrt(x * x + y * y), Atan2(y, x));
}
- public static float Clamp(float val, float min, float max)
+ public static real_t Ceil(real_t s)
{
- if (val < min)
- {
- return min;
- }
- else if (val > max)
- {
- return max;
- }
+ return (real_t)Math.Ceiling(s);
+ }
- return val;
+ public static int Clamp(int value, int min, int max)
+ {
+ return value < min ? min : value > max ? max : value;
}
- public static float Cos(float s)
+ public static real_t Clamp(real_t value, real_t min, real_t max)
{
- return (float)Math.Cos(s);
+ return value < min ? min : value > max ? max : value;
}
- public static float Cosh(float s)
+ public static real_t Cos(real_t s)
{
- return (float)Math.Cosh(s);
+ return (real_t)Math.Cos(s);
}
- public static int Decimals(float step)
+ public static real_t Cosh(real_t s)
{
- return Decimals(step);
+ return (real_t)Math.Cosh(s);
+ }
+
+ public static int Decimals(real_t step)
+ {
+ return Decimals((decimal)step);
}
public static int Decimals(decimal step)
@@ -79,12 +89,12 @@ namespace Godot
return BitConverter.GetBytes(decimal.GetBits(step)[3])[2];
}
- public static float Deg2Rad(float deg)
+ public static real_t Deg2Rad(real_t deg)
{
return deg * Deg2RadConst;
}
- public static float Ease(float s, float curve)
+ public static real_t Ease(real_t s, real_t curve)
{
if (s < 0f)
{
@@ -104,7 +114,8 @@ namespace Godot
return Pow(s, curve);
}
- else if (curve < 0f)
+
+ if (curve < 0f)
{
if (s < 0.5f)
{
@@ -117,111 +128,129 @@ namespace Godot
return 0f;
}
- public static float Exp(float s)
+ public static real_t Exp(real_t s)
{
- return (float)Math.Exp(s);
+ return (real_t)Math.Exp(s);
}
- public static float Floor(float s)
+ public static real_t Floor(real_t s)
{
- return (float)Math.Floor(s);
+ return (real_t)Math.Floor(s);
}
- public static float Fposmod(float x, float y)
+ public static real_t Fposmod(real_t x, real_t y)
{
if (x >= 0f)
{
return x % y;
}
- else
- {
- return y - (-x % y);
- }
+
+ return y - -x % y;
}
- public static float Lerp(float from, float to, float weight)
+ public static real_t InverseLerp(real_t from, real_t to, real_t weight)
+ {
+ return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ }
+
+ public static bool IsInf(real_t s)
+ {
+ return real_t.IsInfinity(s);
+ }
+
+ public static bool IsNaN(real_t s)
+ {
+ return real_t.IsNaN(s);
+ }
+
+ public static real_t Lerp(real_t from, real_t to, real_t weight)
{
return from + (to - from) * Clamp(weight, 0f, 1f);
}
- public static float Log(float s)
+ public static real_t Log(real_t s)
{
- return (float)Math.Log(s);
+ return (real_t)Math.Log(s);
}
public static int Max(int a, int b)
{
- return (a > b) ? a : b;
+ return a > b ? a : b;
}
- public static float Max(float a, float b)
+ public static real_t Max(real_t a, real_t b)
{
- return (a > b) ? a : b;
+ return a > b ? a : b;
}
public static int Min(int a, int b)
{
- return (a < b) ? a : b;
+ return a < b ? a : b;
}
- public static float Min(float a, float b)
+ public static real_t Min(real_t a, real_t b)
{
- return (a < b) ? a : b;
+ return a < b ? a : b;
}
- public static int NearestPo2(int val)
+ public static int NearestPo2(int value)
{
- val--;
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- val++;
- return val;
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value++;
+ return value;
}
- public static Vector2 Polar2Cartesian(float r, float th)
- {
- return new Vector2(r * Cos(th), r * Sin(th));
- }
+ public static Vector2 Polar2Cartesian(real_t r, real_t th)
+ {
+ return new Vector2(r * Cos(th), r * Sin(th));
+ }
- public static float Pow(float x, float y)
+ public static real_t Pow(real_t x, real_t y)
{
- return (float)Math.Pow(x, y);
+ return (real_t)Math.Pow(x, y);
}
- public static float Rad2Deg(float rad)
+ public static real_t Rad2Deg(real_t rad)
{
return rad * Rad2DegConst;
}
- public static float Round(float s)
+ public static real_t Round(real_t s)
{
- return (float)Math.Round(s);
+ return (real_t)Math.Round(s);
}
- public static float Sign(float s)
+ public static int Sign(int s)
{
- return (s < 0f) ? -1f : 1f;
+ return s < 0 ? -1 : 1;
}
- public static float Sin(float s)
+ public static real_t Sign(real_t s)
{
- return (float)Math.Sin(s);
+ return s < 0f ? -1f : 1f;
}
- public static float Sinh(float s)
+ public static real_t Sin(real_t s)
{
- return (float)Math.Sinh(s);
+ return (real_t)Math.Sin(s);
}
- public static float Sqrt(float s)
+ public static real_t Sinh(real_t s)
{
- return (float)Math.Sqrt(s);
+ return (real_t)Math.Sinh(s);
}
- public static float Stepify(float s, float step)
+ public static real_t Sqrt(real_t s)
+ {
+ return (real_t)Math.Sqrt(s);
+ }
+
+ public static real_t Stepify(real_t s, real_t step)
{
if (step != 0f)
{
@@ -231,14 +260,26 @@ namespace Godot
return s;
}
- public static float Tan(float s)
+ public static real_t Tan(real_t s)
+ {
+ return (real_t)Math.Tan(s);
+ }
+
+ public static real_t Tanh(real_t s)
+ {
+ return (real_t)Math.Tanh(s);
+ }
+
+ public static int Wrap(int value, int min, int max)
{
- return (float)Math.Tan(s);
+ int rng = max - min;
+ return min + ((value - min) % rng + rng) % rng;
}
- public static float Tanh(float s)
+ public static real_t Wrap(real_t value, real_t min, real_t max)
{
- return (float)Math.Tanh(s);
+ real_t rng = max - min;
+ return min + ((value - min) % rng + rng) % rng;
}
}
}
diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/cs_files/MathfEx.cs
new file mode 100644
index 0000000000..739b7fb568
--- /dev/null
+++ b/modules/mono/glue/cs_files/MathfEx.cs
@@ -0,0 +1,39 @@
+using System;
+
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+
+namespace Godot
+{
+ public static partial class Mathf
+ {
+ // Define constants with Decimal precision and cast down to double or float.
+
+ public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
+ public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+
+#if REAL_T_IS_DOUBLE
+ public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
+#else
+ public const real_t Epsilon = 1e-06f;
+#endif
+
+ public static int CeilToInt(real_t s)
+ {
+ return (int)Math.Ceiling(s);
+ }
+
+ public static int FloorToInt(real_t s)
+ {
+ return (int)Math.Floor(s);
+ }
+
+ public static int RoundToInt(real_t s)
+ {
+ return (int)Math.Round(s);
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index b347c0835a..1020f06bf5 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -1,95 +1,106 @@
using System;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
public struct Plane : IEquatable<Plane>
{
- Vector3 normal;
+ private Vector3 _normal;
- public float x
+ public Vector3 Normal
+ {
+ get { return _normal; }
+ set { _normal = value; }
+ }
+
+ public real_t x
{
get
{
- return normal.x;
+ return _normal.x;
}
set
{
- normal.x = value;
+ _normal.x = value;
}
}
- public float y
+ public real_t y
{
get
{
- return normal.y;
+ return _normal.y;
}
set
{
- normal.y = value;
+ _normal.y = value;
}
}
- public float z
+ public real_t z
{
get
{
- return normal.z;
+ return _normal.z;
}
set
{
- normal.z = value;
+ _normal.z = value;
}
}
- float d;
+ public real_t D { get; set; }
public Vector3 Center
{
get
{
- return normal * d;
+ return _normal * D;
}
}
- public float DistanceTo(Vector3 point)
+ public real_t DistanceTo(Vector3 point)
{
- return normal.Dot(point) - d;
+ return _normal.Dot(point) - D;
}
public Vector3 GetAnyPoint()
{
- return normal * d;
+ return _normal * D;
}
- public bool HasPoint(Vector3 point, float epsilon = Mathf.Epsilon)
+ public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
{
- float dist = normal.Dot(point) - d;
+ real_t dist = _normal.Dot(point) - D;
return Mathf.Abs(dist) <= epsilon;
}
public Vector3 Intersect3(Plane b, Plane c)
{
- float denom = normal.Cross(b.normal).Dot(c.normal);
+ real_t denom = _normal.Cross(b._normal).Dot(c._normal);
if (Mathf.Abs(denom) <= Mathf.Epsilon)
return new Vector3();
- Vector3 result = (b.normal.Cross(c.normal) * this.d) +
- (c.normal.Cross(normal) * b.d) +
- (normal.Cross(b.normal) * c.d);
+ Vector3 result = b._normal.Cross(c._normal) * D +
+ c._normal.Cross(_normal) * b.D +
+ _normal.Cross(b._normal) * c.D;
return result / denom;
}
public Vector3 IntersectRay(Vector3 from, Vector3 dir)
{
- float den = normal.Dot(dir);
+ real_t den = _normal.Dot(dir);
if (Mathf.Abs(den) <= Mathf.Epsilon)
return new Vector3();
- float dist = (normal.Dot(from) - d) / den;
+ real_t dist = (_normal.Dot(from) - D) / den;
// This is a ray, before the emitting pos (from) does not exist
if (dist > Mathf.Epsilon)
@@ -101,14 +112,14 @@ namespace Godot
public Vector3 IntersectSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
- float den = normal.Dot(segment);
+ real_t den = _normal.Dot(segment);
if (Mathf.Abs(den) <= Mathf.Epsilon)
return new Vector3();
- float dist = (normal.Dot(begin) - d) / den;
+ real_t dist = (_normal.Dot(begin) - D) / den;
- if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
+ if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
return new Vector3();
return begin + segment * -dist;
@@ -116,46 +127,46 @@ namespace Godot
public bool IsPointOver(Vector3 point)
{
- return normal.Dot(point) > d;
+ return _normal.Dot(point) > D;
}
public Plane Normalized()
{
- float len = normal.Length();
+ real_t len = _normal.Length();
if (len == 0)
return new Plane(0, 0, 0, 0);
- return new Plane(normal / len, d / len);
+ return new Plane(_normal / len, D / len);
}
public Vector3 Project(Vector3 point)
{
- return point - normal * DistanceTo(point);
+ return point - _normal * DistanceTo(point);
}
-
- public Plane(float a, float b, float c, float d)
+
+ // Constructors
+ public Plane(real_t a, real_t b, real_t c, real_t d)
{
- normal = new Vector3(a, b, c);
- this.d = d;
+ _normal = new Vector3(a, b, c);
+ this.D = d;
}
-
- public Plane(Vector3 normal, float d)
+ public Plane(Vector3 normal, real_t d)
{
- this.normal = normal;
- this.d = d;
+ this._normal = normal;
+ this.D = d;
}
public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
{
- normal = (v1 - v3).Cross(v1 - v2);
- normal.Normalize();
- d = normal.Dot(v1);
+ _normal = (v1 - v3).Cross(v1 - v2);
+ _normal.Normalize();
+ D = _normal.Dot(v1);
}
public static Plane operator -(Plane plane)
{
- return new Plane(-plane.normal, -plane.d);
+ return new Plane(-plane._normal, -plane.D);
}
public static bool operator ==(Plane left, Plane right)
@@ -180,20 +191,20 @@ namespace Godot
public bool Equals(Plane other)
{
- return normal == other.normal && d == other.d;
+ return _normal == other._normal && D == other.D;
}
public override int GetHashCode()
{
- return normal.GetHashCode() ^ d.GetHashCode();
+ return _normal.GetHashCode() ^ D.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
- this.normal.ToString(),
- this.d.ToString()
+ _normal.ToString(),
+ D.ToString()
});
}
@@ -201,8 +212,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.normal.ToString(format),
- this.d.ToString(format)
+ _normal.ToString(format),
+ D.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs
index c0ac41c5d7..c69c55d997 100644
--- a/modules/mono/glue/cs_files/Quat.cs
+++ b/modules/mono/glue/cs_files/Quat.cs
@@ -1,5 +1,10 @@
using System;
using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -8,17 +13,17 @@ namespace Godot
{
private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f);
- public float x;
- public float y;
- public float z;
- public float w;
+ public real_t x;
+ public real_t y;
+ public real_t z;
+ public real_t w;
public static Quat Identity
{
get { return identity; }
}
- public float this[int index]
+ public real_t this[int index]
{
get
{
@@ -58,15 +63,15 @@ namespace Godot
}
}
- public Quat CubicSlerp(Quat b, Quat preA, Quat postB, float t)
+ public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
{
- float t2 = (1.0f - t) * t * 2f;
+ real_t t2 = (1.0f - t) * t * 2f;
Quat sp = Slerp(b, t);
Quat sq = preA.Slerpni(postB, t);
return sp.Slerpni(sq, t2);
}
- public float Dot(Quat b)
+ public real_t Dot(Quat b)
{
return x * b.x + y * b.y + z * b.z + w * b.w;
}
@@ -76,12 +81,12 @@ namespace Godot
return new Quat(-x, -y, -z, w);
}
- public float Length()
+ public real_t Length()
{
return Mathf.Sqrt(LengthSquared());
}
- public float LengthSquared()
+ public real_t LengthSquared()
{
return Dot(this);
}
@@ -91,20 +96,27 @@ namespace Godot
return this / Length();
}
- public void Set(float x, float y, float z, float w)
+ public void Set(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
+ public void Set(Quat q)
+ {
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
+ }
- public Quat Slerp(Quat b, float t)
+ public Quat Slerp(Quat b, real_t t)
{
// Calculate cosine
- float cosom = x * b.x + y * b.y + z * b.z + w * b.w;
+ real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
- float[] to1 = new float[4];
+ var to1 = new real_t[4];
// Adjust signs if necessary
if (cosom < 0.0)
@@ -122,13 +134,13 @@ namespace Godot
to1[3] = b.w;
}
- float sinom, scale0, scale1;
+ real_t sinom, scale0, scale1;
// Calculate coefficients
- if ((1.0 - cosom) > Mathf.Epsilon)
+ if (1.0 - cosom > Mathf.Epsilon)
{
// Standard case (Slerp)
- float omega = Mathf.Acos(cosom);
+ real_t omega = Mathf.Acos(cosom);
sinom = Mathf.Sin(omega);
scale0 = Mathf.Sin((1.0f - t) * omega) / sinom;
scale1 = Mathf.Sin(t * omega) / sinom;
@@ -150,47 +162,56 @@ namespace Godot
);
}
- public Quat Slerpni(Quat b, float t)
+ public Quat Slerpni(Quat b, real_t t)
{
- float dot = this.Dot(b);
+ real_t dot = Dot(b);
if (Mathf.Abs(dot) > 0.9999f)
{
return this;
}
- float theta = Mathf.Acos(dot);
- float sinT = 1.0f / Mathf.Sin(theta);
- float newFactor = Mathf.Sin(t * theta) * sinT;
- float invFactor = Mathf.Sin((1.0f - t) * theta) * sinT;
+ real_t theta = Mathf.Acos(dot);
+ real_t sinT = 1.0f / Mathf.Sin(theta);
+ real_t newFactor = Mathf.Sin(t * theta) * sinT;
+ real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT;
return new Quat
(
- invFactor * this.x + newFactor * b.x,
- invFactor * this.y + newFactor * b.y,
- invFactor * this.z + newFactor * b.z,
- invFactor * this.w + newFactor * b.w
+ invFactor * x + newFactor * b.x,
+ invFactor * y + newFactor * b.y,
+ invFactor * z + newFactor * b.z,
+ invFactor * w + newFactor * b.w
);
}
public Vector3 Xform(Vector3 v)
{
Quat q = this * v;
- q *= this.Inverse();
+ q *= Inverse();
return new Vector3(q.x, q.y, q.z);
}
- public Quat(float x, float y, float z, float w)
+ // Constructors
+ public Quat(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
+ }
+ public Quat(Quat q)
+ {
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
}
-
- public Quat(Vector3 axis, float angle)
+
+ public Quat(Vector3 axis, real_t angle)
{
- float d = axis.Length();
+ real_t d = axis.Length();
+ real_t angle_t = angle;
if (d == 0f)
{
@@ -201,12 +222,12 @@ namespace Godot
}
else
{
- float s = Mathf.Sin(angle * 0.5f) / d;
+ real_t s = Mathf.Sin(angle_t * 0.5f) / d;
x = axis.x * s;
y = axis.y * s;
z = axis.z * s;
- w = Mathf.Cos(angle * 0.5f);
+ w = Mathf.Cos(angle_t * 0.5f);
}
}
@@ -258,17 +279,17 @@ namespace Godot
);
}
- public static Quat operator *(Quat left, float right)
+ public static Quat operator *(Quat left, real_t right)
{
return new Quat(left.x * right, left.y * right, left.z * right, left.w * right);
}
- public static Quat operator *(float left, Quat right)
+ public static Quat operator *(real_t left, Quat right)
{
return new Quat(right.x * left, right.y * left, right.z * left, right.w * left);
}
- public static Quat operator /(Quat left, float right)
+ public static Quat operator /(Quat left, real_t right)
{
return left * (1.0f / right);
}
@@ -305,24 +326,12 @@ namespace Godot
public override string ToString()
{
- return String.Format("({0}, {1}, {2}, {3})", new object[]
- {
- this.x.ToString(),
- this.y.ToString(),
- this.z.ToString(),
- this.w.ToString()
- });
+ return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString());
}
public string ToString(string format)
{
- return String.Format("({0}, {1}, {2}, {3})", new object[]
- {
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format),
- this.w.ToString(format)
- });
+ return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
}
}
}
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/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs
index e1fbb65da5..6f16656573 100644
--- a/modules/mono/glue/cs_files/Rect2.cs
+++ b/modules/mono/glue/cs_files/Rect2.cs
@@ -1,5 +1,10 @@
using System;
using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -26,14 +31,14 @@ namespace Godot
get { return position + size; }
}
- public float Area
+ public real_t Area
{
get { return GetArea(); }
}
public Rect2 Clip(Rect2 b)
{
- Rect2 newRect = b;
+ var newRect = b;
if (!Intersects(newRect))
return new Rect2();
@@ -52,14 +57,14 @@ namespace Godot
public bool Encloses(Rect2 b)
{
- return (b.position.x >= position.x) && (b.position.y >= position.y) &&
- ((b.position.x + b.size.x) < (position.x + size.x)) &&
- ((b.position.y + b.size.y) < (position.y + size.y));
+ return b.position.x >= position.x && b.position.y >= position.y &&
+ b.position.x + b.size.x < position.x + size.x &&
+ b.position.y + b.size.y < position.y + size.y;
}
public Rect2 Expand(Vector2 to)
{
- Rect2 expanded = this;
+ var expanded = this;
Vector2 begin = expanded.position;
Vector2 end = expanded.position + expanded.size;
@@ -80,14 +85,14 @@ namespace Godot
return expanded;
}
- public float GetArea()
+ public real_t GetArea()
{
return size.x * size.y;
}
- public Rect2 Grow(float by)
+ public Rect2 Grow(real_t by)
{
- Rect2 g = this;
+ var g = this;
g.position.x -= by;
g.position.y -= by;
@@ -97,9 +102,9 @@ namespace Godot
return g;
}
- public Rect2 GrowIndividual(float left, float top, float right, float bottom)
+ public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
- Rect2 g = this;
+ var g = this;
g.position.x -= left;
g.position.y -= top;
@@ -109,14 +114,14 @@ namespace Godot
return g;
}
- public Rect2 GrowMargin(Margin margin, float by)
+ public Rect2 GrowMargin(Margin margin, real_t by)
{
- Rect2 g = this;
+ var g = this;
- g.GrowIndividual((Margin.Left == margin) ? by : 0,
- (Margin.Top == margin) ? by : 0,
- (Margin.Right == margin) ? by : 0,
- (Margin.Bottom == margin) ? by : 0);
+ g.GrowIndividual(Margin.Left == margin ? by : 0,
+ Margin.Top == margin ? by : 0,
+ Margin.Right == margin ? by : 0,
+ Margin.Bottom == margin ? by : 0);
return g;
}
@@ -133,9 +138,9 @@ namespace Godot
if (point.y < position.y)
return false;
- if (point.x >= (position.x + size.x))
+ if (point.x >= position.x + size.x)
return false;
- if (point.y >= (position.y + size.y))
+ if (point.y >= position.y + size.y)
return false;
return true;
@@ -143,13 +148,13 @@ namespace Godot
public bool Intersects(Rect2 b)
{
- if (position.x > (b.position.x + b.size.x))
+ if (position.x > b.position.x + b.size.x)
return false;
- if ((position.x + size.x) < b.position.x)
+ if (position.x + size.x < b.position.x)
return false;
- if (position.y > (b.position.y + b.size.y))
+ if (position.y > b.position.y + b.size.y)
return false;
- if ((position.y + size.y) < b.position.y)
+ if (position.y + size.y < b.position.y)
return false;
return true;
@@ -169,17 +174,27 @@ namespace Godot
return newRect;
}
-
+
+ // Constructors
public Rect2(Vector2 position, Vector2 size)
{
this.position = position;
this.size = size;
}
-
- public Rect2(float x, float y, float width, float height)
+ public Rect2(Vector2 position, real_t width, real_t height)
+ {
+ this.position = position;
+ size = new Vector2(width, height);
+ }
+ public Rect2(real_t x, real_t y, Vector2 size)
+ {
+ position = new Vector2(x, y);
+ this.size = size;
+ }
+ public Rect2(real_t x, real_t y, real_t width, real_t height)
{
- this.position = new Vector2(x, y);
- this.size = new Vector2(width, height);
+ position = new Vector2(x, y);
+ size = new Vector2(width, height);
}
public static bool operator ==(Rect2 left, Rect2 right)
@@ -216,8 +231,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.position.ToString(),
- this.size.ToString()
+ position.ToString(),
+ size.ToString()
});
}
@@ -225,8 +240,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.position.ToString(format),
- this.size.ToString(format)
+ position.ToString(format),
+ size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/SignalAttribute.cs
new file mode 100644
index 0000000000..3957387be9
--- /dev/null
+++ b/modules/mono/glue/cs_files/SignalAttribute.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Delegate)]
+ public class SignalAttribute : Attribute
+ {
+ }
+}
diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/cs_files/SignalAwaiter.cs
index 19ccc26e79..c06f6b05c9 100644
--- a/modules/mono/glue/cs_files/SignalAwaiter.cs
+++ b/modules/mono/glue/cs_files/SignalAwaiter.cs
@@ -4,15 +4,15 @@ namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
{
- private bool completed = false;
- private object[] result = null;
- private Action action = null;
+ private bool completed;
+ private object[] result;
+ private Action action;
- public SignalAwaiter(Godot.Object source, string signal, Godot.Object target)
+ public SignalAwaiter(Object source, string signal, Object target)
{
NativeCalls.godot_icall_Object_connect_signal_awaiter(
- Godot.Object.GetPtr(source),
- signal, Godot.Object.GetPtr(target), this
+ Object.GetPtr(source),
+ signal, Object.GetPtr(target), this
);
}
diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs
index 5c3ceff97d..eaeed7b37b 100644
--- a/modules/mono/glue/cs_files/StringExtensions.cs
+++ b/modules/mono/glue/cs_files/StringExtensions.cs
@@ -1,4 +1,5 @@
//using System;
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -43,11 +44,9 @@ namespace Godot
{
return instance.Substring(prev, i - prev);
}
- else
- {
- count++;
- prev = i + 1;
- }
+
+ count++;
+ prev = i + 1;
}
i++;
@@ -83,7 +82,7 @@ namespace Godot
// </summary>
public static string[] Bigrams(this string instance)
{
- string[] b = new string[instance.Length - 1];
+ var b = new string[instance.Length - 1];
for (int i = 0; i < b.Length; i++)
{
@@ -98,7 +97,7 @@ namespace Godot
// </summary>
public static string CEscape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@@ -120,7 +119,7 @@ namespace Godot
// </summary>
public static string CUnescape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@@ -143,7 +142,7 @@ namespace Godot
public static string Capitalize(this string instance)
{
string aux = instance.Replace("_", " ").ToLower();
- string cap = string.Empty;
+ var cap = string.Empty;
for (int i = 0; i < aux.GetSliceCount(" "); i++)
{
@@ -178,13 +177,13 @@ namespace Godot
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
- else if (instance[instance_idx] == 0)
+ if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
- else if (to[to_idx] == 0)
+ if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller...
- else if (instance[instance_idx] < to[to_idx]) // More than
+ if (instance[instance_idx] < to[to_idx]) // More than
return -1;
- else if (instance[instance_idx] > to[to_idx]) // Less than
+ if (instance[instance_idx] > to[to_idx]) // Less than
return 1;
instance_idx++;
@@ -226,7 +225,7 @@ namespace Godot
if (pos < 0)
return instance;
- return instance.Substring(pos + 1, instance.Length);
+ return instance.Substring(pos + 1);
}
// <summary>
@@ -260,12 +259,12 @@ namespace Godot
{
int basepos = instance.Find("://");
- string rs = string.Empty;
- string @base = string.Empty;
+ string rs;
+ var @base = string.Empty;
if (basepos != -1)
{
- int end = basepos + 3;
+ var end = basepos + 3;
rs = instance.Substring(end, instance.Length);
@base = instance.Substring(0, end);
}
@@ -287,7 +286,7 @@ namespace Godot
if (sep == -1)
return @base;
- return @base + rs.substr(0, sep);
+ return @base + rs.Substr(0, sep);
}
// <summary>
@@ -312,8 +311,8 @@ namespace Godot
int hashv = 5381;
int c;
- while ((c = (int)instance[index++]) != 0)
- hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c
+ while ((c = instance[index++]) != 0)
+ hashv = (hashv << 5) + hashv + c; // hash * 33 + c
return hashv;
}
@@ -379,7 +378,7 @@ namespace Godot
while (instance[src] != 0 && text[tgt] != 0)
{
- bool match = false;
+ bool match;
if (case_insensitive)
{
@@ -455,7 +454,10 @@ namespace Godot
return false; // Don't start with number plz
}
- bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_';
+ bool valid_char = instance[i] >= '0' &&
+ instance[i] <= '9' || instance[i] >= 'a' &&
+ instance[i] <= 'z' || instance[i] >= 'A' &&
+ instance[i] <= 'Z' || instance[i] == '_';
if (!valid_char)
return false;
@@ -478,7 +480,7 @@ namespace Godot
// </summary>
public static bool IsValidIpAddress(this string instance)
{
- string[] ip = instance.split(".");
+ string[] ip = instance.Split(".");
if (ip.Length != 4)
return false;
@@ -489,7 +491,7 @@ namespace Godot
if (!n.IsValidInteger())
return false;
- int val = n.to_int();
+ int val = n.ToInt();
if (val < 0 || val > 255)
return false;
}
@@ -502,7 +504,7 @@ namespace Godot
// </summary>
public static string JsonEscape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@@ -551,7 +553,7 @@ namespace Godot
case '\0':
return instance[0] == 0;
case '*':
- return ExprMatch(expr + 1, instance, caseSensitive) || (instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive));
+ return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive);
case '?':
return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive);
default:
@@ -571,7 +573,7 @@ namespace Godot
// <summary>
// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
- public static bool matchn(this string instance, string expr)
+ public static bool Matchn(this string instance, string expr)
{
return instance.ExprMatch(expr, false);
}
@@ -610,13 +612,13 @@ namespace Godot
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
- else if (instance[instance_idx] == 0)
+ if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
- else if (to[to_idx] == 0)
+ if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller..
- else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than
+ if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than
return -1;
- else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than
+ if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than
return 1;
instance_idx++;
@@ -724,8 +726,7 @@ namespace Godot
{
if (instance.Length > 0 && instance[instance.Length - 1] == '/')
return instance + file;
- else
- return instance + "/" + file;
+ return instance + "/" + file;
}
// <summary>
@@ -771,7 +772,7 @@ namespace Godot
if (pos < 0)
return string.Empty;
- return instance.Substring(pos, (instance.Length - pos));
+ return instance.Substring(pos, instance.Length - pos);
}
public static byte[] Sha256Buffer(this string instance)
@@ -824,23 +825,23 @@ namespace Godot
}
}
- return (2.0f * inter) / sum;
+ return 2.0f * inter / sum;
}
// <summary>
// Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
// </summary>
- public static string[] split(this string instance, string divisor, bool allow_empty = true)
+ public static string[] Split(this string instance, string divisor, bool allow_empty = true)
{
- return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
+ return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
}
// <summary>
// Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",".
// </summary>
- public static float[] split_floats(this string instance, string divisor, bool allow_empty = true)
+ public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true)
{
- List<float> ret = new List<float>();
+ var ret = new List<float>();
int from = 0;
int len = instance.Length;
@@ -849,7 +850,7 @@ namespace Godot
int end = instance.Find(divisor, from);
if (end < 0)
end = len;
- if (allow_empty || (end > from))
+ if (allow_empty || end > from)
ret.Add(float.Parse(instance.Substring(from)));
if (end == len)
break;
@@ -872,25 +873,22 @@ namespace Godot
// <summary>
// Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
// </summary>
- public static string strip_edges(this string instance, bool left = true, bool right = true)
+ public static string StripEdges(this string instance, bool left = true, bool right = true)
{
if (left)
{
if (right)
return instance.Trim(non_printable);
- else
- return instance.TrimStart(non_printable);
- }
- else
- {
- return instance.TrimEnd(non_printable);
+ return instance.TrimStart(non_printable);
}
+
+ return instance.TrimEnd(non_printable);
}
// <summary>
// Return part of the string from the position [code]from[/code], with length [code]len[/code].
// </summary>
- public static string substr(this string instance, int from, int len)
+ public static string Substr(this string instance, int from, int len)
{
return instance.Substring(from, len);
}
@@ -898,7 +896,7 @@ namespace Godot
// <summary>
// Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters.
// </summary>
- public static byte[] to_ascii(this string instance)
+ public static byte[] ToAscii(this string instance)
{
return Encoding.ASCII.GetBytes(instance);
}
@@ -906,7 +904,7 @@ namespace Godot
// <summary>
// Convert a string, containing a decimal number, into a [code]float[/code].
// </summary>
- public static float to_float(this string instance)
+ public static float ToFloat(this string instance)
{
return float.Parse(instance);
}
@@ -914,7 +912,7 @@ namespace Godot
// <summary>
// Convert a string, containing an integer number, into an [code]int[/code].
// </summary>
- public static int to_int(this string instance)
+ public static int ToInt(this string instance)
{
return int.Parse(instance);
}
@@ -922,7 +920,7 @@ namespace Godot
// <summary>
// Return the string converted to lowercase.
// </summary>
- public static string to_lower(this string instance)
+ public static string ToLower(this string instance)
{
return instance.ToLower();
}
@@ -930,7 +928,7 @@ namespace Godot
// <summary>
// Return the string converted to uppercase.
// </summary>
- public static string to_upper(this string instance)
+ public static string ToUpper(this string instance)
{
return instance.ToUpper();
}
@@ -938,7 +936,7 @@ namespace Godot
// <summary>
// Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii().
// </summary>
- public static byte[] to_utf8(this string instance)
+ public static byte[] ToUtf8(this string instance)
{
return Encoding.UTF8.GetBytes(instance);
}
@@ -946,7 +944,7 @@ namespace Godot
// <summary>
// Return a copy of the string with special characters escaped using the XML standard.
// </summary>
- public static string xml_escape(this string instance)
+ public static string XmlEscape(this string instance)
{
return SecurityElement.Escape(instance);
}
@@ -954,7 +952,7 @@ namespace Godot
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the XML standard.
// </summary>
- public static string xml_unescape(this string instance)
+ public static string XmlUnescape(this string instance)
{
return SecurityElement.FromString(instance).Text;
}
diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs
index 5214100d36..d1b247a552 100644
--- a/modules/mono/glue/cs_files/Transform.cs
+++ b/modules/mono/glue/cs_files/Transform.cs
@@ -1,5 +1,10 @@
using System;
using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -23,8 +28,8 @@ namespace Godot
public Transform LookingAt(Vector3 target, Vector3 up)
{
- Transform t = this;
- t.set_look_at(origin, target, up);
+ var t = this;
+ t.SetLookAt(origin, target, up);
return t;
}
@@ -33,7 +38,7 @@ namespace Godot
return new Transform(basis.Orthonormalized(), origin);
}
- public Transform Rotated(Vector3 axis, float phi)
+ public Transform Rotated(Vector3 axis, real_t phi)
{
return new Transform(new Basis(axis, phi), new Vector3()) * this;
}
@@ -43,7 +48,7 @@ namespace Godot
return new Transform(basis.Scaled(scale), origin * scale);
}
- public void set_look_at(Vector3 eye, Vector3 target, Vector3 up)
+ public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
// Z vector
@@ -92,21 +97,22 @@ namespace Godot
return new Vector3
(
- (basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z),
- (basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z),
- (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z)
+ basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z,
+ basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z,
+ basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z
);
}
-
+
+ // Constructors
public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
{
- this.basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
this.origin = origin;
}
public Transform(Quat quat, Vector3 origin)
{
- this.basis = new Basis(quat);
+ basis = new Basis(quat);
this.origin = origin;
}
@@ -157,8 +163,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.basis.ToString(),
- this.origin.ToString()
+ basis.ToString(),
+ origin.ToString()
});
}
@@ -166,8 +172,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.basis.ToString(format),
- this.origin.ToString(format)
+ basis.ToString(format),
+ origin.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs
index fe7c5b5706..ff5259178b 100644
--- a/modules/mono/glue/cs_files/Transform2D.cs
+++ b/modules/mono/glue/cs_files/Transform2D.cs
@@ -1,5 +1,10 @@
using System;
using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -27,7 +32,7 @@ namespace Godot
get { return o; }
}
- public float Rotation
+ public real_t Rotation
{
get { return Mathf.Atan2(y.x, o.y); }
}
@@ -73,7 +78,7 @@ namespace Godot
}
- public float this[int index, int axis]
+ public real_t this[int index, int axis]
{
get
{
@@ -105,9 +110,9 @@ namespace Godot
public Transform2D AffineInverse()
{
- Transform2D inv = this;
+ var inv = this;
- float det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1];
+ real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1];
if (det == 0)
{
@@ -119,9 +124,9 @@ namespace Godot
);
}
- float idet = 1.0f / det;
+ real_t idet = 1.0f / det;
- float temp = this[0, 0];
+ real_t temp = this[0, 0];
this[0, 0] = this[1, 1];
this[1, 1] = temp;
@@ -143,24 +148,24 @@ namespace Godot
return new Vector2(x.Dot(v), y.Dot(v));
}
- public Transform2D InterpolateWith(Transform2D m, float c)
+ public Transform2D InterpolateWith(Transform2D m, real_t c)
{
- float r1 = Rotation;
- float r2 = m.Rotation;
+ real_t r1 = Rotation;
+ real_t r2 = m.Rotation;
Vector2 s1 = Scale;
Vector2 s2 = m.Scale;
// Slerp rotation
- Vector2 v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
- Vector2 v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
+ var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
+ var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
- float dot = v1.Dot(v2);
+ real_t dot = v1.Dot(v2);
// Clamp dot to [-1, 1]
- dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot);
+ dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot);
- Vector2 v = new Vector2();
+ Vector2 v;
if (dot > 0.9995f)
{
@@ -169,7 +174,7 @@ namespace Godot
}
else
{
- float angle = c * Mathf.Acos(dot);
+ real_t angle = c * Mathf.Acos(dot);
Vector2 v3 = (v2 - v1 * dot).Normalized();
v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
}
@@ -179,7 +184,7 @@ namespace Godot
Vector2 p2 = m.Origin;
// Construct matrix
- Transform2D res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
+ var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
Vector2 scale = s1.LinearInterpolate(s2, c);
res.x *= scale;
res.y *= scale;
@@ -189,10 +194,10 @@ namespace Godot
public Transform2D Inverse()
{
- Transform2D inv = this;
+ var inv = this;
// Swap
- float temp = inv.x.y;
+ real_t temp = inv.x.y;
inv.x.y = inv.y.x;
inv.y.x = temp;
@@ -203,13 +208,13 @@ namespace Godot
public Transform2D Orthonormalized()
{
- Transform2D on = this;
+ var on = this;
Vector2 onX = on.x;
Vector2 onY = on.y;
onX.Normalize();
- onY = onY - onX * (onX.Dot(onY));
+ onY = onY - onX * onX.Dot(onY);
onY.Normalize();
on.x = onX;
@@ -218,33 +223,33 @@ namespace Godot
return on;
}
- public Transform2D Rotated(float phi)
+ public Transform2D Rotated(real_t phi)
{
return this * new Transform2D(phi, new Vector2());
}
public Transform2D Scaled(Vector2 scale)
{
- Transform2D copy = this;
+ var copy = this;
copy.x *= scale;
copy.y *= scale;
copy.o *= scale;
return copy;
}
- private float Tdotx(Vector2 with)
+ private real_t Tdotx(Vector2 with)
{
return this[0, 0] * with[0] + this[1, 0] * with[1];
}
- private float Tdoty(Vector2 with)
+ private real_t Tdoty(Vector2 with)
{
return this[0, 1] * with[0] + this[1, 1] * with[1];
}
public Transform2D Translated(Vector2 offset)
{
- Transform2D copy = this;
+ var copy = this;
copy.o += copy.BasisXform(offset);
return copy;
}
@@ -259,24 +264,26 @@ namespace Godot
Vector2 vInv = v - o;
return new Vector2(x.Dot(vInv), y.Dot(vInv));
}
-
+
+ // Constructors
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
{
- this.x = xAxis;
- this.y = yAxis;
- this.o = origin;
+ x = xAxis;
+ y = yAxis;
+ o = origin;
}
- public Transform2D(float xx, float xy, float yx, float yy, float ox, float oy)
+
+ public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
- this.x = new Vector2(xx, xy);
- this.y = new Vector2(yx, yy);
- this.o = new Vector2(ox, oy);
+ x = new Vector2(xx, xy);
+ y = new Vector2(yx, yy);
+ o = new Vector2(ox, oy);
}
- public Transform2D(float rot, Vector2 pos)
+ public Transform2D(real_t rot, Vector2 pos)
{
- float cr = Mathf.Cos(rot);
- float sr = Mathf.Sin(rot);
+ real_t cr = Mathf.Cos(rot);
+ real_t sr = Mathf.Sin(rot);
x.x = cr;
y.y = cr;
x.y = -sr;
@@ -288,7 +295,7 @@ namespace Godot
{
left.o = left.Xform(right.o);
- float x0, x1, y0, y1;
+ real_t x0, x1, y0, y1;
x0 = left.Tdotx(right.x);
x1 = left.Tdoty(right.x);
@@ -337,9 +344,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(),
- this.y.ToString(),
- this.o.ToString()
+ x.ToString(),
+ y.ToString(),
+ o.ToString()
});
}
@@ -347,9 +354,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format),
- this.o.ToString(format)
+ x.ToString(format),
+ y.ToString(format),
+ o.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
new file mode 100755
index 0000000000..00750edc07
--- /dev/null
+++ b/modules/mono/glue/cs_files/VERSION.txt
@@ -0,0 +1 @@
+3
diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs
index 238775bda2..c274364895 100644
--- a/modules/mono/glue/cs_files/Vector2.cs
+++ b/modules/mono/glue/cs_files/Vector2.cs
@@ -1,22 +1,26 @@
-using System;
-using System.Runtime.InteropServices;
-
// file: core/math/math_2d.h
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/math/math_2d.cpp
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
+using System;
+using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector2 : IEquatable<Vector2>
{
- public float x;
- public float y;
+ public real_t x;
+ public real_t y;
- public float this[int index]
+ public real_t this[int index]
{
get
{
@@ -48,7 +52,7 @@ namespace Godot
internal void Normalize()
{
- float length = x * x + y * y;
+ real_t length = x * x + y * y;
if (length != 0f)
{
@@ -58,7 +62,7 @@ namespace Godot
}
}
- private float Cross(Vector2 b)
+ public real_t Cross(Vector2 b)
{
return x * b.y - y * b.x;
}
@@ -68,22 +72,22 @@ namespace Godot
return new Vector2(Mathf.Abs(x), Mathf.Abs(y));
}
- public float Angle()
+ public real_t Angle()
{
return Mathf.Atan2(y, x);
}
- public float AngleTo(Vector2 to)
+ public real_t AngleTo(Vector2 to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
- public float AngleToPoint(Vector2 to)
+ public real_t AngleToPoint(Vector2 to)
{
return Mathf.Atan2(x - to.x, y - to.y);
}
- public float Aspect()
+ public real_t Aspect()
{
return x / y;
}
@@ -93,10 +97,15 @@ namespace Godot
return -Reflect(n);
}
- public Vector2 Clamped(float length)
+ public Vector2 Ceil()
{
- Vector2 v = this;
- float l = this.Length();
+ return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y));
+ }
+
+ public Vector2 Clamped(real_t length)
+ {
+ var v = this;
+ real_t l = Length();
if (l > 0 && length < l)
{
@@ -107,33 +116,33 @@ namespace Godot
return v;
}
- public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, float t)
+ public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
{
- Vector2 p0 = preA;
- Vector2 p1 = this;
- Vector2 p2 = b;
- Vector2 p3 = postB;
+ var p0 = preA;
+ var p1 = this;
+ var p2 = b;
+ var p3 = postB;
- float t2 = t * t;
- float t3 = t2 * t;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
- return 0.5f * ((p1 * 2.0f) +
+ return 0.5f * (p1 * 2.0f +
(-p0 + p2) * t +
(2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
- public float DistanceSquaredTo(Vector2 to)
+ public real_t DistanceSquaredTo(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
}
- public float DistanceTo(Vector2 to)
+ public real_t DistanceTo(Vector2 to)
{
return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y));
}
- public float Dot(Vector2 with)
+ public real_t Dot(Vector2 with)
{
return x * with.x + y * with.y;
}
@@ -148,29 +157,29 @@ namespace Godot
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
- public float Length()
+ public real_t Length()
{
return Mathf.Sqrt(x * x + y * y);
}
- public float LengthSquared()
+ public real_t LengthSquared()
{
return x * x + y * y;
}
- public Vector2 LinearInterpolate(Vector2 b, float t)
+ public Vector2 LinearInterpolate(Vector2 b, real_t t)
{
- Vector2 res = this;
+ var res = this;
- res.x += (t * (b.x - x));
- res.y += (t * (b.y - y));
+ res.x += t * (b.x - x);
+ res.y += t * (b.y - y);
return res;
}
public Vector2 Normalized()
{
- Vector2 result = this;
+ var result = this;
result.Normalize();
return result;
}
@@ -180,12 +189,34 @@ namespace Godot
return 2.0f * n * Dot(n) - this;
}
- public Vector2 Rotated(float phi)
+ public Vector2 Rotated(real_t phi)
{
- float rads = Angle() + phi;
+ real_t rads = Angle() + phi;
return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length();
}
+ public Vector2 Round()
+ {
+ return new Vector2(Mathf.Round(x), Mathf.Round(y));
+ }
+
+ public void Set(real_t x, real_t y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+ public void Set(Vector2 v)
+ {
+ x = v.x;
+ y = v.y;
+ }
+
+ public Vector2 Slerp(Vector2 b, real_t t)
+ {
+ real_t theta = AngleTo(b);
+ return Rotated(theta * t);
+ }
+
public Vector2 Slide(Vector2 n)
{
return this - n * Dot(n);
@@ -200,12 +231,36 @@ namespace Godot
{
return new Vector2(y, -x);
}
-
- public Vector2(float x, float y)
+
+ private static readonly Vector2 zero = new Vector2 (0, 0);
+ private static readonly Vector2 one = new Vector2 (1, 1);
+ private static readonly Vector2 negOne = new Vector2 (-1, -1);
+
+ private static readonly Vector2 up = new Vector2 (0, 1);
+ private static readonly Vector2 down = new Vector2 (0, -1);
+ private static readonly Vector2 right = new Vector2 (1, 0);
+ private static readonly Vector2 left = new Vector2 (-1, 0);
+
+ public static Vector2 Zero { get { return zero; } }
+ public static Vector2 One { get { return one; } }
+ public static Vector2 NegOne { get { return negOne; } }
+
+ public static Vector2 Up { get { return up; } }
+ public static Vector2 Down { get { return down; } }
+ public static Vector2 Right { get { return right; } }
+ public static Vector2 Left { get { return left; } }
+
+ // Constructors
+ public Vector2(real_t x, real_t y)
{
this.x = x;
this.y = y;
}
+ public Vector2(Vector2 v)
+ {
+ x = v.x;
+ y = v.y;
+ }
public static Vector2 operator +(Vector2 left, Vector2 right)
{
@@ -228,14 +283,14 @@ namespace Godot
return vec;
}
- public static Vector2 operator *(Vector2 vec, float scale)
+ public static Vector2 operator *(Vector2 vec, real_t scale)
{
vec.x *= scale;
vec.y *= scale;
return vec;
}
- public static Vector2 operator *(float scale, Vector2 vec)
+ public static Vector2 operator *(real_t scale, Vector2 vec)
{
vec.x *= scale;
vec.y *= scale;
@@ -249,7 +304,7 @@ namespace Godot
return left;
}
- public static Vector2 operator /(Vector2 vec, float scale)
+ public static Vector2 operator /(Vector2 vec, real_t scale)
{
vec.x /= scale;
vec.y /= scale;
@@ -279,10 +334,8 @@ namespace Godot
{
return left.y < right.y;
}
- else
- {
- return left.x < right.x;
- }
+
+ return left.x < right.x;
}
public static bool operator >(Vector2 left, Vector2 right)
@@ -291,10 +344,8 @@ namespace Godot
{
return left.y > right.y;
}
- else
- {
- return left.x > right.x;
- }
+
+ return left.x > right.x;
}
public static bool operator <=(Vector2 left, Vector2 right)
@@ -303,10 +354,8 @@ namespace Godot
{
return left.y <= right.y;
}
- else
- {
- return left.x <= right.x;
- }
+
+ return left.x <= right.x;
}
public static bool operator >=(Vector2 left, Vector2 right)
@@ -315,10 +364,8 @@ namespace Godot
{
return left.y >= right.y;
}
- else
- {
- return left.x >= right.x;
- }
+
+ return left.x >= right.x;
}
public override bool Equals(object obj)
@@ -345,8 +392,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.x.ToString(),
- this.y.ToString()
+ x.ToString(),
+ y.ToString()
});
}
@@ -354,8 +401,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format)
+ x.ToString(format),
+ y.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs
index 190caa4b53..085a4f0043 100644
--- a/modules/mono/glue/cs_files/Vector3.cs
+++ b/modules/mono/glue/cs_files/Vector3.cs
@@ -1,12 +1,16 @@
-using System;
-using System.Runtime.InteropServices;
-
// file: core/math/vector3.h
// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
// file: core/math/vector3.cpp
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
+using System;
+using System.Runtime.InteropServices;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
namespace Godot
{
@@ -20,11 +24,11 @@ namespace Godot
Z
}
- public float x;
- public float y;
- public float z;
+ public real_t x;
+ public real_t y;
+ public real_t z;
- public float this[int index]
+ public real_t this[int index]
{
get
{
@@ -61,7 +65,7 @@ namespace Godot
internal void Normalize()
{
- float length = this.Length();
+ real_t length = Length();
if (length == 0f)
{
@@ -80,7 +84,7 @@ namespace Godot
return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
- public float AngleTo(Vector3 to)
+ public real_t AngleTo(Vector3 to)
{
return Mathf.Atan2(Cross(to).Length(), Dot(to));
}
@@ -99,40 +103,40 @@ namespace Godot
{
return new Vector3
(
- (y * b.z) - (z * b.y),
- (z * b.x) - (x * b.z),
- (x * b.y) - (y * b.x)
+ y * b.z - z * b.y,
+ z * b.x - x * b.z,
+ x * b.y - y * b.x
);
}
- public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, float t)
+ public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t)
{
- Vector3 p0 = preA;
- Vector3 p1 = this;
- Vector3 p2 = b;
- Vector3 p3 = postB;
+ var p0 = preA;
+ var p1 = this;
+ var p2 = b;
+ var p3 = postB;
- float t2 = t * t;
- float t3 = t2 * t;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
return 0.5f * (
- (p1 * 2.0f) + (-p0 + p2) * t +
+ p1 * 2.0f + (-p0 + p2) * t +
(2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3
);
}
- public float DistanceSquaredTo(Vector3 b)
+ public real_t DistanceSquaredTo(Vector3 b)
{
return (b - this).LengthSquared();
}
- public float DistanceTo(Vector3 b)
+ public real_t DistanceTo(Vector3 b)
{
return (b - this).Length();
}
- public float Dot(Vector3 b)
+ public real_t Dot(Vector3 b)
{
return x * b.x + y * b.y + z * b.z;
}
@@ -152,31 +156,31 @@ namespace Godot
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
- public float Length()
+ public real_t Length()
{
- float x2 = x * x;
- float y2 = y * y;
- float z2 = z * z;
+ real_t x2 = x * x;
+ real_t y2 = y * y;
+ real_t z2 = z * z;
return Mathf.Sqrt(x2 + y2 + z2);
}
- public float LengthSquared()
+ public real_t LengthSquared()
{
- float x2 = x * x;
- float y2 = y * y;
- float z2 = z * z;
+ real_t x2 = x * x;
+ real_t y2 = y * y;
+ real_t z2 = z * z;
return x2 + y2 + z2;
}
- public Vector3 LinearInterpolate(Vector3 b, float t)
+ public Vector3 LinearInterpolate(Vector3 b, real_t t)
{
return new Vector3
(
- x + (t * (b.x - x)),
- y + (t * (b.y - y)),
- z + (t * (b.z - z))
+ x + t * (b.x - x),
+ y + t * (b.y - y),
+ z + t * (b.z - z)
);
}
@@ -192,7 +196,7 @@ namespace Godot
public Vector3 Normalized()
{
- Vector3 v = this;
+ var v = this;
v.Normalize();
return v;
}
@@ -215,11 +219,35 @@ namespace Godot
return 2.0f * n * Dot(n) - this;
}
- public Vector3 Rotated(Vector3 axis, float phi)
+ public Vector3 Round()
+ {
+ return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
+ }
+
+ public Vector3 Rotated(Vector3 axis, real_t phi)
{
return new Basis(axis, phi).Xform(this);
}
+ public void Set(real_t x, real_t y, real_t z)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+ public void Set(Vector3 v)
+ {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ }
+
+ public Vector3 Slerp(Vector3 b, real_t t)
+ {
+ real_t theta = AngleTo(b);
+ return Rotated(Cross(b), theta * t);
+ }
+
public Vector3 Slide(Vector3 n)
{
return this - n * Dot(n);
@@ -243,13 +271,42 @@ namespace Godot
0f, 0f, z
);
}
-
- public Vector3(float x, float y, float z)
+
+ private static readonly Vector3 zero = new Vector3 (0, 0, 0);
+ private static readonly Vector3 one = new Vector3 (1, 1, 1);
+ private static readonly Vector3 negOne = new Vector3 (-1, -1, -1);
+
+ private static readonly Vector3 up = new Vector3 (0, 1, 0);
+ private static readonly Vector3 down = new Vector3 (0, -1, 0);
+ private static readonly Vector3 right = new Vector3 (1, 0, 0);
+ private static readonly Vector3 left = new Vector3 (-1, 0, 0);
+ private static readonly Vector3 forward = new Vector3 (0, 0, -1);
+ private static readonly Vector3 back = new Vector3 (0, 0, 1);
+
+ public static Vector3 Zero { get { return zero; } }
+ public static Vector3 One { get { return one; } }
+ public static Vector3 NegOne { get { return negOne; } }
+
+ public static Vector3 Up { get { return up; } }
+ public static Vector3 Down { get { return down; } }
+ public static Vector3 Right { get { return right; } }
+ public static Vector3 Left { get { return left; } }
+ public static Vector3 Forward { get { return forward; } }
+ public static Vector3 Back { get { return back; } }
+
+ // Constructors
+ public Vector3(real_t x, real_t y, real_t z)
{
this.x = x;
this.y = y;
this.z = z;
}
+ public Vector3(Vector3 v)
+ {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ }
public static Vector3 operator +(Vector3 left, Vector3 right)
{
@@ -275,7 +332,7 @@ namespace Godot
return vec;
}
- public static Vector3 operator *(Vector3 vec, float scale)
+ public static Vector3 operator *(Vector3 vec, real_t scale)
{
vec.x *= scale;
vec.y *= scale;
@@ -283,7 +340,7 @@ namespace Godot
return vec;
}
- public static Vector3 operator *(float scale, Vector3 vec)
+ public static Vector3 operator *(real_t scale, Vector3 vec)
{
vec.x *= scale;
vec.y *= scale;
@@ -299,7 +356,7 @@ namespace Godot
return left;
}
- public static Vector3 operator /(Vector3 vec, float scale)
+ public static Vector3 operator /(Vector3 vec, real_t scale)
{
vec.x /= scale;
vec.y /= scale;
@@ -331,8 +388,7 @@ namespace Godot
{
if (left.y == right.y)
return left.z < right.z;
- else
- return left.y < right.y;
+ return left.y < right.y;
}
return left.x < right.x;
@@ -344,8 +400,7 @@ namespace Godot
{
if (left.y == right.y)
return left.z > right.z;
- else
- return left.y > right.y;
+ return left.y > right.y;
}
return left.x > right.x;
@@ -357,8 +412,7 @@ namespace Godot
{
if (left.y == right.y)
return left.z <= right.z;
- else
- return left.y < right.y;
+ return left.y < right.y;
}
return left.x < right.x;
@@ -370,8 +424,7 @@ namespace Godot
{
if (left.y == right.y)
return left.z >= right.z;
- else
- return left.y > right.y;
+ return left.y > right.y;
}
return left.x > right.x;
@@ -401,9 +454,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(),
- this.y.ToString(),
- this.z.ToString()
+ x.ToString(),
+ y.ToString(),
+ z.ToString()
});
}
@@ -411,9 +464,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format)
+ x.ToString(format),
+ y.ToString(format),
+ z.ToString(format)
});
}
}
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 4c26c3e6bd..f604464e8f 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -39,4 +39,7 @@
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
+#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
+#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
+
#endif // GODOTSHARP_DEFS_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index f5febd415b..0646580eaa 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -42,11 +42,15 @@
#include "project_settings.h"
#include "../csharp_script.h"
+#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
+#include "gd_mono_class.h"
+#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"
#ifdef TOOLS_ENABLED
#include "../editor/godotsharp_editor.h"
+#include "main/main.h"
#endif
void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
@@ -70,7 +74,31 @@ void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
GDMono *GDMono::singleton = NULL;
+namespace {
+
+void setup_runtime_main_args() {
+ CharString execpath = OS::get_singleton()->get_executable_path().utf8();
+
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+
+ List<CharString> cmdline_args_utf8;
+ Vector<char *> main_args;
+ main_args.resize(cmdline_args.size() + 1);
+
+ main_args[0] = execpath.ptrw();
+
+ int i = 1;
+ for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
+ CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get();
+ main_args[i] = stored.ptrw();
+ i++;
+ }
+
+ mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
+}
+
#ifdef DEBUG_ENABLED
+
static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
do {
@@ -92,24 +120,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
return mono_is_debugger_attached();
}
-#endif
-
-#ifdef TOOLS_ENABLED
-// temporary workaround. should be provided from Main::setup/setup2 instead
-bool _is_project_manager_requested() {
-
- List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
- for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
- const String &arg = E->get();
- if (arg == "-p" || arg == "--project-manager")
- return true;
- }
-
- return false;
-}
-#endif
-#ifdef DEBUG_ENABLED
void gdmono_debug_init() {
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
@@ -121,7 +132,7 @@ void gdmono_debug_init() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() ||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
- _is_project_manager_requested()) {
+ Main::is_project_manager()) {
return;
}
#endif
@@ -136,8 +147,11 @@ void gdmono_debug_init() {
};
mono_jit_parse_options(2, (char **)options);
}
+
#endif
+} // namespace
+
void GDMono::initialize() {
ERR_FAIL_NULL(Engine::get_singleton());
@@ -190,6 +204,8 @@ void GDMono::initialize() {
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
+ setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
+
runtime_initialized = true;
OS::get_singleton()->print("Mono: Runtime initialized\n");
@@ -222,7 +238,46 @@ void GDMono::initialize() {
_register_internal_calls();
// The following assemblies are not required at initialization
- _load_all_script_assemblies();
+#ifndef MONO_GLUE_DISABLED
+ if (_load_api_assemblies()) {
+ if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ // Everything is fine with the api assemblies, load the project assembly
+ _load_project_assembly();
+ } else {
+#ifdef TOOLS_ENABLED
+ // The assembly was successfuly loaded, but the full api could not be cached.
+ // This is most likely an outdated assembly loaded because of an invalid version in the metadata,
+ // so we invalidate the version in the metadata and unload the script domain.
+
+ if (core_api_assembly_out_of_sync) {
+ ERR_PRINT("The loaded Core API assembly is out of sync");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ }
+
+ if (editor_api_assembly_out_of_sync) {
+ ERR_PRINT("The loaded Editor API assembly is out of sync");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
+ }
+
+ OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n");
+
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ WARN_PRINT("Mono: Failed to unload scripts domain");
+ }
+#else
+ ERR_PRINT("The loaded API assembly is invalid");
+ CRASH_NOW();
+#endif
+ }
+ }
+#else
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+#endif
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
@@ -233,7 +288,11 @@ void GDMono::initialize() {
namespace GodotSharpBindings {
uint64_t get_core_api_hash();
+#ifdef TOOLS_ENABLED
uint64_t get_editor_api_hash();
+#endif // TOOLS_ENABLED
+uint32_t get_bindings_version();
+uint32_t get_cs_glue_version();
void register_generated_icalls();
} // namespace GodotSharpBindings
@@ -285,43 +344,84 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
return assemblies[domain_id].getptr(p_name);
}
-bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
+bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
+
+ CRASH_COND(!r_assembly);
+
+ MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
+ bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
+ mono_assembly_name_free(aname);
+ mono_free(aname);
+
+ return result;
+}
+
+bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
CRASH_COND(!r_assembly);
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8());
+ OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\n").utf8());
MonoImageOpenStatus status = MONO_IMAGE_OK;
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
- mono_assembly_name_free(aname);
+ MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
if (!assembly)
return false;
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
+
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
- ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
ERR_FAIL_COND_V(stored_assembly == NULL, false);
-
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
+
*r_assembly = *stored_assembly;
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
+ OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
return true;
}
+APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
+ APIAssembly::Version api_assembly_version;
+
+ const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ?
+ BINDINGS_CLASS_NATIVECALLS :
+ BINDINGS_CLASS_NATIVECALLS_EDITOR;
+
+ GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
+
+ if (nativecalls_klass) {
+ GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
+ if (api_hash_field)
+ api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL));
+
+ GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
+ if (binds_ver_field)
+ api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL));
+
+ GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
+ if (cs_glue_ver_field)
+ api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL));
+ }
+
+ return api_assembly_version;
+}
+
+String APIAssembly::to_string(APIAssembly::Type p_type) {
+ return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR";
+}
+
bool GDMono::_load_corlib_assembly() {
if (corlib_assembly)
return true;
- bool success = _load_assembly("mscorlib", &corlib_assembly);
+ bool success = load_assembly("mscorlib", &corlib_assembly);
if (success)
GDMonoUtils::update_corlib_cache();
@@ -331,13 +431,25 @@ bool GDMono::_load_corlib_assembly() {
bool GDMono::_load_core_api_assembly() {
- if (api_assembly)
+ if (core_api_assembly)
return true;
- bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
+#ifdef TOOLS_ENABLED
+ if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
+ return false;
+#endif
- if (success)
+ bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly);
+
+ if (success) {
+#ifndef MONO_GLUE_DISABLED
+ APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
+ core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+#endif
GDMonoUtils::update_godot_api_cache();
+ }
return success;
}
@@ -348,7 +460,23 @@ bool GDMono::_load_editor_api_assembly() {
if (editor_api_assembly)
return true;
- return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+#ifdef TOOLS_ENABLED
+ if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
+ return false;
+#endif
+
+ bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+
+ if (success) {
+#ifndef MONO_GLUE_DISABLED
+ APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
+ editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+#endif
+ }
+
+ return success;
}
#endif
@@ -360,7 +488,7 @@ bool GDMono::_load_editor_tools_assembly() {
_GDMONO_SCOPE_DOMAIN_(tools_domain)
- return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
+ return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
}
#endif
@@ -374,17 +502,20 @@ bool GDMono::_load_project_assembly() {
name = "UnnamedProject";
}
- bool success = _load_assembly(name, &project_assembly);
+ bool success = load_assembly(name, &project_assembly);
- if (success)
+ if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
+ } else {
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
+ }
return success;
}
-bool GDMono::_load_all_script_assemblies() {
+bool GDMono::_load_api_assemblies() {
-#ifndef MONO_GLUE_DISABLED
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
@@ -399,20 +530,72 @@ bool GDMono::_load_all_script_assemblies() {
#endif
}
- if (!_load_project_assembly()) {
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
- return false;
+ return true;
+}
+
+#ifdef TOOLS_ENABLED
+String GDMono::_get_api_assembly_metadata_path() {
+
+ return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg");
+}
+
+void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) {
+
+ String section = APIAssembly::to_string(p_api_type);
+ String path = _get_api_assembly_metadata_path();
+
+ Ref<ConfigFile> metadata;
+ metadata.instance();
+ metadata->load(path);
+
+ metadata->set_value(section, "invalidated", p_invalidated);
+
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
+ .plus_file(p_api_type == APIAssembly::API_CORE ?
+ API_ASSEMBLY_NAME ".dll" :
+ EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ ERR_FAIL_COND(!FileAccess::exists(assembly_path));
+
+ uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
+
+ metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time));
+
+ String dir = path.get_base_dir();
+ if (!DirAccess::exists(dir)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND(!da);
+ Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir));
+ ERR_FAIL_COND(err != OK);
}
- return true;
-#else
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n");
+ Error save_err = metadata->save(path);
+ ERR_FAIL_COND(save_err != OK);
+}
- return true;
-#endif
+bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) {
+
+ String section = APIAssembly::to_string(p_api_type);
+
+ Ref<ConfigFile> metadata;
+ metadata.instance();
+ metadata->load(_get_api_assembly_metadata_path());
+
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
+ .plus_file(p_api_type == APIAssembly::API_CORE ?
+ API_ASSEMBLY_NAME ".dll" :
+ EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(assembly_path))
+ return false;
+
+ uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
+
+ uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0);
+
+ return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
}
+#endif
Error GDMono::_load_scripts_domain() {
@@ -455,12 +638,15 @@ Error GDMono::_unload_scripts_domain() {
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
- api_assembly = NULL;
+ core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
#endif
+ core_api_assembly_out_of_sync = false;
+ editor_api_assembly_out_of_sync = false;
+
MonoDomain *domain = scripts_domain;
scripts_domain = NULL;
@@ -515,16 +701,80 @@ Error GDMono::reload_scripts_domain() {
return err;
}
- if (!_load_all_script_assemblies()) {
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n");
+#ifndef MONO_GLUE_DISABLED
+ if (!_load_api_assemblies()) {
return ERR_CANT_OPEN;
}
+ if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ // Everything is fine with the api assemblies, load the project assembly
+ _load_project_assembly();
+ } else {
+ // The assembly was successfuly loaded, but the full api could not be cached.
+ // This is most likely an outdated assembly loaded because of an invalid version in the metadata,
+ // so we invalidate the version in the metadata and unload the script domain.
+
+ if (core_api_assembly_out_of_sync) {
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
+ ERR_PRINT("Core API assembly is in sync, but the cache update failed");
+ metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
+ }
+
+ if (editor_api_assembly_out_of_sync) {
+ metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
+ }
+
+ Error err = _unload_scripts_domain();
+ if (err != OK) {
+ WARN_PRINT("Mono: Failed to unload scripts domain");
+ }
+
+ return ERR_CANT_RESOLVE;
+ }
+
+ if (!_load_project_assembly())
+ return ERR_CANT_OPEN;
+#else
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+#endif
+
return OK;
}
#endif
+Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
+
+ CRASH_COND(p_domain == NULL);
+
+ String domain_name = mono_domain_get_friendly_name(p_domain);
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8());
+ }
+
+ if (mono_domain_get() != root_domain)
+ mono_domain_set(root_domain, true);
+
+ mono_gc_collect(mono_gc_max_generation());
+ mono_domain_finalize(p_domain, 2000);
+ mono_gc_collect(mono_gc_max_generation());
+
+ _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
+
+ MonoObject *ex = NULL;
+ mono_domain_try_unload(p_domain, &ex);
+
+ if (ex) {
+ ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
+ mono_print_unhandled_exception(ex);
+ return FAILED;
+ }
+
+ return OK;
+}
+
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
MonoImage *image = mono_class_get_image(p_raw_class);
@@ -576,8 +826,11 @@ GDMono::GDMono() {
tools_domain = NULL;
#endif
+ core_api_assembly_out_of_sync = false;
+ editor_api_assembly_out_of_sync = false;
+
corlib_assembly = NULL;
- api_assembly = NULL;
+ core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 67251778c6..5e01152870 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -31,6 +31,8 @@
#ifndef GD_MONO_H
#define GD_MONO_H
+#include "core/io/config_file.h"
+
#include "../godotsharp_defs.h"
#include "gd_mono_assembly.h"
#include "gd_mono_log.h"
@@ -39,6 +41,43 @@
#include "../utils/mono_reg_utils.h"
#endif
+namespace APIAssembly {
+enum Type {
+ API_CORE,
+ API_EDITOR
+};
+
+struct Version {
+ uint64_t godot_api_hash;
+ uint32_t bindings_version;
+ uint32_t cs_glue_version;
+
+ bool operator==(const Version &p_other) const {
+ return godot_api_hash == p_other.godot_api_hash &&
+ bindings_version == p_other.bindings_version &&
+ cs_glue_version == p_other.cs_glue_version;
+ }
+
+ Version() :
+ godot_api_hash(0),
+ bindings_version(0),
+ cs_glue_version(0) {
+ }
+
+ Version(uint64_t p_godot_api_hash,
+ uint32_t p_bindings_version,
+ uint32_t p_cs_glue_version) :
+ godot_api_hash(p_godot_api_hash),
+ bindings_version(p_bindings_version),
+ cs_glue_version(p_cs_glue_version) {
+ }
+
+ static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
+};
+
+String to_string(Type p_type);
+} // namespace APIAssembly
+
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
#ifdef TOOLS_ENABLED
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
@@ -55,8 +94,11 @@ class GDMono {
MonoDomain *tools_domain;
#endif
+ bool core_api_assembly_out_of_sync;
+ bool editor_api_assembly_out_of_sync;
+
GDMonoAssembly *corlib_assembly;
- GDMonoAssembly *api_assembly;
+ GDMonoAssembly *core_api_assembly;
GDMonoAssembly *project_assembly;
#ifdef TOOLS_ENABLED
GDMonoAssembly *editor_api_assembly;
@@ -75,7 +117,11 @@ class GDMono {
#endif
bool _load_project_assembly();
- bool _load_all_script_assemblies();
+ bool _load_api_assemblies();
+
+#ifdef TOOLS_ENABLED
+ String _get_api_assembly_metadata_path();
+#endif
void _register_internal_calls();
@@ -94,8 +140,6 @@ class GDMono {
void _initialize_and_check_api_hashes();
#endif
- bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
-
GDMonoLog *gdmono_log;
#ifdef WINDOWS_ENABLED
@@ -113,6 +157,11 @@ public:
#endif
#endif
+#ifdef TOOLS_ENABLED
+ void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
+ bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
+#endif
+
static GDMono *get_singleton() { return singleton; }
// Do not use these, unless you know what you're doing
@@ -128,7 +177,7 @@ public:
#endif
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
@@ -145,6 +194,10 @@ public:
Error reload_scripts_domain();
#endif
+ bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
+ bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
+ Error finalize_and_unload_domain(MonoDomain *p_domain);
+
void initialize();
GDMono();
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index ba56ed6ed5..9d3bee2176 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -33,9 +33,10 @@
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
-#include "list.h"
-#include "os/file_access.h"
-#include "os/os.h"
+#include "core/list.h"
+#include "core/os/file_access.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_class.h"
@@ -43,7 +44,23 @@
bool GDMonoAssembly::no_search = false;
Vector<String> GDMonoAssembly::search_dirs;
-MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) {
+MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
+ return GDMonoAssembly::_search_hook(aname, user_data, false);
+}
+
+MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
+ return GDMonoAssembly::_search_hook(aname, user_data, true);
+}
+
+MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
+ return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
+}
+
+MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
+ return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
+}
+
+MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) {
(void)user_data; // UNUSED
@@ -60,7 +77,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
no_search = true; // Avoid the recursion madness
String path;
- MonoAssembly *res = NULL;
+ GDMonoAssembly *res = NULL;
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
@@ -68,44 +85,49 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
if (has_extension) {
path = search_dir.plus_file(name);
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name.get_basename(), path);
- break;
+ res = _load_assembly_from(name.get_basename(), path, refonly);
+ if (res != NULL)
+ break;
}
} else {
path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name, path);
- break;
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ break;
}
path = search_dir.plus_file(name + ".exe");
if (FileAccess::exists(path)) {
- res = _load_assembly_from(name, path);
- break;
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ break;
}
}
}
no_search = false;
- return res;
+ return res ? res->get_assembly() : NULL;
}
-MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
+MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
(void)user_data; // UNUSED
if (search_dirs.empty()) {
-#ifdef TOOLS_DOMAIN
search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
-#endif
search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
search_dirs.push_back(OS::get_singleton()->get_resource_dir());
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
+#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR
+ search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path());
+#endif
const char *rootdir = mono_assembly_getrootdir();
if (rootdir) {
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
+ search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades"));
}
if (assemblies_path) {
@@ -116,53 +138,90 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
}
}
+ String name = mono_assembly_name_get_name(aname);
+ bool has_extension = name.ends_with(".dll");
+
+ if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
+ GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
+ if (stored_assembly)
+ return (*stored_assembly)->get_assembly();
+
+ String path;
+ GDMonoAssembly *res = NULL;
+
+ for (int i = 0; i < search_dirs.size(); i++) {
+ const String &search_dir = search_dirs[i];
+
+ if (has_extension) {
+ path = search_dir.plus_file(name);
+ if (FileAccess::exists(path)) {
+ res = _load_assembly_from(name.get_basename(), path, refonly);
+ if (res != NULL)
+ break;
+ }
+ } else {
+ path = search_dir.plus_file(name + ".dll");
+ if (FileAccess::exists(path)) {
+ res = _load_assembly_from(name, path, refonly);
+ if (res != NULL)
+ break;
+ }
+ }
+ }
+
+ return res ? res->get_assembly() : NULL;
+ }
+
return NULL;
}
-MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) {
+GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
- MonoDomain *domain = mono_domain_get();
-
- Error err = assembly->load(domain);
+ Error err = assembly->load(p_refonly);
if (err != OK) {
memdelete(assembly);
ERR_FAIL_V(NULL);
}
+ MonoDomain *domain = mono_domain_get();
GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
- return assembly->get_assembly();
+ return assembly;
}
void GDMonoAssembly::initialize() {
- // TODO refonly as well?
- mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL);
- mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL);
+ mono_install_assembly_search_hook(&assembly_search_hook, NULL);
+ mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
+ mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
+ mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
}
-Error GDMonoAssembly::load(MonoDomain *p_domain) {
+Error GDMonoAssembly::load(bool p_refonly) {
ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
+ refonly = p_refonly;
+
uint64_t last_modified_time = FileAccess::get_modified_time(path);
Vector<uint8_t> data = FileAccess::get_file_as_array(path);
ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
- String image_filename(path);
+ String image_filename = ProjectSettings::get_singleton()->globalize_path(path);
- MonoImageOpenStatus status;
+ MonoImageOpenStatus status = MONO_IMAGE_OK;
image = mono_image_open_from_data_with_name(
(char *)&data[0], data.size(),
- true, &status, false,
+ true, &status, refonly,
image_filename.utf8().get_data());
- ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_COND_V(status != MONO_IMAGE_OK, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN);
#ifdef DEBUG_ENABLED
String pdb_path(path + ".pdb");
@@ -182,15 +241,10 @@ no_pdb:
#endif
- assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false);
+ assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly);
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
- if (p_domain && mono_image_get_entry_point(image)) {
- // TODO should this be removed? do we want to call main? what other effects does this have?
- mono_jit_exec(p_domain, assembly, 0, NULL);
- }
-
loaded = true;
modified_time = last_modified_time;
@@ -342,12 +396,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
return match;
}
+GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
+
+ GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
+ if (loaded_asm)
+ return *loaded_asm;
+
+ no_search = true;
+ GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
+ no_search = false;
+
+ return res;
+}
+
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
loaded = false;
gdobject_class_cache_updated = false;
name = p_name;
path = p_path;
+ refonly = false;
modified_time = 0;
assembly = NULL;
image = NULL;
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 8e7aa701bf..5cf744a5a2 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -71,6 +71,7 @@ class GDMonoAssembly {
MonoAssembly *assembly;
MonoImage *image;
+ bool refonly;
bool loaded;
String name;
@@ -90,19 +91,25 @@ class GDMonoAssembly {
static bool no_search;
static Vector<String> search_dirs;
- static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data);
- static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
+ static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
+ static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
+ static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
+ static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
- static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path);
+ static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
+ static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
+
+ static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
friend class GDMono;
static void initialize();
public:
- Error load(MonoDomain *p_domain);
+ Error load(bool p_refonly);
Error wrapper_for_image(MonoImage *p_image);
void unload();
+ _FORCE_INLINE_ bool is_refonly() const { return refonly; }
_FORCE_INLINE_ bool is_loaded() const { return loaded; }
_FORCE_INLINE_ MonoImage *get_image() const { return image; }
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
@@ -110,11 +117,13 @@ public:
_FORCE_INLINE_ String get_path() const { return path; }
_FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
- GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class);
+ GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
GDMonoClass *get_class(MonoClass *p_mono_class);
GDMonoClass *get_object_derived_class(const StringName &p_class);
+ static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
+
GDMonoAssembly(const String &p_name, const String &p_path = String());
~GDMonoAssembly();
};
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index b826352f02..66339d7ae6 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -404,6 +404,33 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
return properties_list;
}
+const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
+ if (delegates_fetched)
+ return delegates_list;
+
+ void *iter = NULL;
+ MonoClass *raw_class = NULL;
+ while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != NULL) {
+ if (mono_class_is_delegate(raw_class)) {
+ StringName name = mono_class_get_name(raw_class);
+
+ Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
+
+ if (match) {
+ delegates_list.push_back(match->get());
+ } else {
+ GDMonoClass *delegate = memnew(GDMonoClass(mono_class_get_namespace(raw_class), mono_class_get_name(raw_class), raw_class, assembly));
+ delegates.insert(name, delegate);
+ delegates_list.push_back(delegate);
+ }
+ }
+ }
+
+ delegates_fetched = true;
+
+ return delegates_list;
+}
+
GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
namespace_name = p_namespace;
@@ -417,6 +444,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name
methods_fetched = false;
fields_fetched = false;
properties_fetched = false;
+ delegates_fetched = false;
}
GDMonoClass::~GDMonoClass() {
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index f5895be144..417c138594 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -90,6 +90,10 @@ class GDMonoClass {
Map<StringName, GDMonoProperty *> properties;
Vector<GDMonoProperty *> properties_list;
+ bool delegates_fetched;
+ Map<StringName, GDMonoClass *> delegates;
+ Vector<GDMonoClass *> delegates_list;
+
friend class GDMonoAssembly;
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
@@ -125,7 +129,7 @@ public:
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
- GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace);
+ GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
GDMonoField *get_field(const StringName &p_name);
const Vector<GDMonoField *> &get_all_fields();
@@ -133,6 +137,8 @@ public:
GDMonoProperty *get_property(const StringName &p_name);
const Vector<GDMonoProperty *> &get_all_properties();
+ const Vector<GDMonoClass *> &get_all_delegates();
+
~GDMonoClass();
};
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index aa1a8e39c7..c04fcca962 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -606,11 +606,14 @@ 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);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_array, MonoObject *, i);
- ret.push_back(mono_object_to_variant(elem));
+ ret[i] = mono_object_to_variant(elem);
}
return ret;
@@ -630,11 +633,13 @@ 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++) {
int32_t elem = mono_array_get(p_array, int32_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -652,11 +657,14 @@ 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);
for (int i = 0; i < length; i++) {
uint8_t elem = mono_array_get(p_array, uint8_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -674,11 +682,14 @@ 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);
for (int i = 0; i < length; i++) {
real_t elem = mono_array_get(p_array, real_t, i);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -697,11 +708,14 @@ 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);
for (int i = 0; i < length; i++) {
MonoString *elem = mono_array_get(p_array, MonoString *, i);
- ret.push_back(mono_string_to_godot(elem));
+ ret.set(i, mono_string_to_godot(elem));
}
return ret;
@@ -728,12 +742,15 @@ 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);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i);
MARSHALLED_IN(Color, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -758,12 +775,15 @@ 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);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i);
MARSHALLED_IN(Vector2, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -789,12 +809,15 @@ 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);
for (int i = 0; i < length; i++) {
real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i);
MARSHALLED_IN(Vector3, raw_elem, elem);
- ret.push_back(elem);
+ ret.set(i, elem);
}
return ret;
@@ -828,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_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 1f8e9a1926..ad52904945 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -229,6 +229,23 @@ String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
return res;
}
+void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
+ if (params_count > 0) {
+ const char **_names = memnew_arr(const char *, params_count);
+ mono_method_get_param_names(mono_method, _names);
+ for (int i = 0; i < params_count; ++i) {
+ names.push_back(StringName(_names[i]));
+ }
+ memdelete_arr(_names);
+ }
+}
+
+void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
+ for (int i = 0; i < param_types.size(); ++i) {
+ types.push_back(param_types[i]);
+ }
+}
+
GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
name = p_name;
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index 14df8dcfb4..a173af83f4 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -80,6 +80,9 @@ public:
String get_ret_type_full_name() const;
String get_signature_desc(bool p_namespaces = false) const;
+ void get_parameter_names(Vector<StringName> &names) const;
+ void get_parameter_types(Vector<ManagedType> &types) const;
+
GDMonoMethod(StringName p_name, MonoMethod *p_method);
~GDMonoMethod();
};
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index a2f0819a72..fbdbeaa3f7 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -114,11 +114,15 @@ void MonoCache::clear_members() {
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hintString = NULL;
+ class_SignalAttribute = NULL;
class_ToolAttribute = NULL;
class_RemoteAttribute = NULL;
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;
@@ -142,7 +146,7 @@ void MonoCache::cleanup() {
godot_api_cache_updated = false;
}
-#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
+#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
void update_corlib_cache() {
@@ -201,11 +205,15 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
+ CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
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"));
@@ -243,7 +251,7 @@ void update_godot_api_cache() {
mono_runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
- mono_cache.corlib_cache_updated = true;
+ mono_cache.godot_api_cache_updated = true;
}
void clear_cache() {
@@ -303,7 +311,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) {
if (class_name[0] == '_')
class_name = class_name.substr(1, class_name.length());
- GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
+ GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
#ifdef TOOLS_ENABLED
if (!klass) {
@@ -319,7 +327,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
do {
const GDMonoAssembly *assembly = klass->get_assembly();
- if (assembly == GDMono::get_singleton()->get_api_assembly())
+ if (assembly == GDMono::get_singleton()->get_core_api_assembly())
return klass;
#ifdef TOOLS_ENABLED
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
@@ -418,37 +426,56 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
if (!ScriptDebugger::get_singleton())
return;
- GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
+ ScriptLanguage::StackInfo separator;
+ separator.file = "";
+ separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
+ separator.line = 0;
+
+ Vector<ScriptLanguage::StackInfo> si;
+ String exc_msg = "";
+
+ while (p_exc != NULL) {
+ GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
+ MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
- MonoBoolean need_file_info = true;
- void *ctor_args[2] = { p_exc, &need_file_info };
+ MonoBoolean need_file_info = true;
+ void *ctor_args[2] = { p_exc, &need_file_info };
- MonoObject *unexpected_exc = NULL;
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
+ MonoObject *unexpected_exc = NULL;
+ CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
- if (unexpected_exc != NULL) {
- mono_print_unhandled_exception(unexpected_exc);
+ if (unexpected_exc != NULL) {
+ mono_print_unhandled_exception(unexpected_exc);
- if (p_recursion_caution) {
- // Called from CSharpLanguage::get_current_stack_info,
- // so printing an error here could result in endless recursion
- OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
- return;
- } else {
- ERR_FAIL();
+ if (p_recursion_caution) {
+ // Called from CSharpLanguage::get_current_stack_info,
+ // so printing an error here could result in endless recursion
+ OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
+ return;
+ } else {
+ ERR_FAIL();
+ }
}
- }
- Vector<ScriptLanguage::StackInfo> si;
- if (stack_trace != NULL && !p_recursion_caution)
- si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
+ Vector<ScriptLanguage::StackInfo> _si;
+ if (stack_trace != NULL && !p_recursion_caution) {
+ _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
+ for (int i = _si.size() - 1; i >= 0; i--)
+ si.insert(0, _si[i]);
+ }
+
+ exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
+
+ GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException");
+ p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL;
+ if (p_exc != NULL)
+ si.insert(0, separator);
+ }
String file = si.size() ? si[0].file : __FILE__;
String func = si.size() ? si[0].func : FUNCTION_STR;
int line = si.size() ? si[0].line : __LINE__;
String error_msg = "Unhandled exception";
- String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc);
ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
#endif
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 259da46c31..fc13a00e85 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -108,9 +108,13 @@ struct MonoCache {
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
+ GDMonoClass *class_SignalAttribute;
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 8ddddb3a24..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 ''
@@ -75,7 +75,7 @@ def find_msbuild_tools_path_reg():
vswhere = os.getenv('PROGRAMFILES')
vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
- vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild']
+ vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild']
try:
lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 9bb8da8ac0..7b23cd7579 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -174,6 +174,8 @@ String find_msbuild_tools_path() {
List<String> vswhere_args;
vswhere_args.push_back("-latest");
+ vswhere_args.push_back("-products");
+ vswhere_args.push_back("*");
vswhere_args.push_back("-requires");
vswhere_args.push_back("Microsoft.Component.MSBuild");
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/openssl/SCsub b/modules/openssl/SCsub
deleted file mode 100644
index 84c5e68439..0000000000
--- a/modules/openssl/SCsub
+++ /dev/null
@@ -1,696 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-Import('env_modules')
-
-env_openssl = env_modules.Clone()
-
-# Thirdparty source files
-if env['builtin_openssl']:
- thirdparty_dir = "#thirdparty/openssl/"
-
- thirdparty_sources = [
- "ssl/t1_lib.c",
- "ssl/t1_ext.c",
- "ssl/s3_srvr.c",
- "ssl/t1_enc.c",
- "ssl/t1_meth.c",
- "ssl/s23_clnt.c",
- "ssl/ssl_asn1.c",
- "ssl/tls_srp.c",
- "ssl/kssl.c",
- "ssl/d1_both.c",
- "ssl/t1_clnt.c",
- "ssl/bio_ssl.c",
- "ssl/d1_srtp.c",
- "ssl/t1_reneg.c",
- "ssl/ssl_cert.c",
- "ssl/s3_lib.c",
- "ssl/d1_srvr.c",
- "ssl/s23_meth.c",
- "ssl/ssl_stat.c",
- "ssl/ssl_err.c",
- "ssl/ssl_algs.c",
- "ssl/s3_cbc.c",
- "ssl/d1_clnt.c",
- "ssl/s3_pkt.c",
- "ssl/d1_meth.c",
- "ssl/s3_both.c",
- "ssl/s2_enc.c",
- "ssl/s3_meth.c",
- "ssl/s3_enc.c",
- "ssl/s23_pkt.c",
- "ssl/s2_pkt.c",
- "ssl/d1_pkt.c",
- "ssl/ssl_rsa.c",
- "ssl/s23_srvr.c",
- "ssl/s2_meth.c",
- "ssl/s3_clnt.c",
- "ssl/s23_lib.c",
- "ssl/t1_srvr.c",
- "ssl/ssl_lib.c",
- "ssl/ssl_txt.c",
- "ssl/s2_srvr.c",
- "ssl/ssl_sess.c",
- "ssl/s2_clnt.c",
- "ssl/d1_lib.c",
- "ssl/s2_lib.c",
- "ssl/ssl_err2.c",
- "ssl/ssl_ciph.c",
- "crypto/dsa/dsa_lib.c",
- "crypto/dsa/dsa_pmeth.c",
- "crypto/dsa/dsa_ossl.c",
- "crypto/dsa/dsa_gen.c",
- "crypto/dsa/dsa_asn1.c",
- "crypto/dsa/dsa_prn.c",
- "crypto/dsa/dsa_sign.c",
- "crypto/dsa/dsa_key.c",
- "crypto/dsa/dsa_vrf.c",
- "crypto/dsa/dsa_err.c",
- "crypto/dsa/dsa_ameth.c",
- "crypto/dsa/dsa_depr.c",
- "crypto/x509/x509_lu.c",
- "crypto/x509/x509cset.c",
- "crypto/x509/x509_set.c",
- "crypto/x509/x509_d2.c",
- "crypto/x509/x509_txt.c",
- "crypto/x509/x509rset.c",
- "crypto/x509/by_dir.c",
- "crypto/x509/x509_vpm.c",
- "crypto/x509/x509_vfy.c",
- "crypto/x509/x509_trs.c",
- "crypto/x509/by_file.c",
- "crypto/x509/x509_obj.c",
- "crypto/x509/x509spki.c",
- "crypto/x509/x509_v3.c",
- "crypto/x509/x509_req.c",
- "crypto/x509/x509_att.c",
- "crypto/x509/x_all.c",
- "crypto/x509/x509_ext.c",
- "crypto/x509/x509type.c",
- "crypto/x509/x509_def.c",
- "crypto/x509/x509_err.c",
- "crypto/x509/x509name.c",
- "crypto/x509/x509_r2x.c",
- "crypto/x509/x509_cmp.c",
- "crypto/asn1/x_pkey.c",
- "crypto/asn1/a_gentm.c",
- "crypto/asn1/x_sig.c",
- "crypto/asn1/t_req.c",
- "crypto/asn1/t_pkey.c",
- "crypto/asn1/p8_pkey.c",
- "crypto/asn1/a_i2d_fp.c",
- "crypto/asn1/x_val.c",
- "crypto/asn1/f_string.c",
- "crypto/asn1/p5_pbe.c",
- "crypto/asn1/bio_ndef.c",
- "crypto/asn1/a_bool.c",
- "crypto/asn1/asn1_gen.c",
- "crypto/asn1/x_algor.c",
- "crypto/asn1/bio_asn1.c",
- "crypto/asn1/asn_mime.c",
- "crypto/asn1/t_x509.c",
- "crypto/asn1/a_strex.c",
- "crypto/asn1/x_nx509.c",
- "crypto/asn1/asn1_err.c",
- "crypto/asn1/x_crl.c",
- "crypto/asn1/a_print.c",
- "crypto/asn1/a_type.c",
- "crypto/asn1/tasn_new.c",
- "crypto/asn1/n_pkey.c",
- "crypto/asn1/x_bignum.c",
- "crypto/asn1/asn_pack.c",
- "crypto/asn1/evp_asn1.c",
- "crypto/asn1/t_bitst.c",
- "crypto/asn1/x_req.c",
- "crypto/asn1/a_time.c",
- "crypto/asn1/x_name.c",
- "crypto/asn1/x_pubkey.c",
- "crypto/asn1/tasn_typ.c",
- "crypto/asn1/asn_moid.c",
- "crypto/asn1/a_utctm.c",
- "crypto/asn1/asn1_lib.c",
- "crypto/asn1/x_x509a.c",
- "crypto/asn1/a_set.c",
- "crypto/asn1/t_crl.c",
- "crypto/asn1/p5_pbev2.c",
- "crypto/asn1/tasn_enc.c",
- "crypto/asn1/a_mbstr.c",
- "crypto/asn1/tasn_dec.c",
- "crypto/asn1/x_x509.c",
- "crypto/asn1/a_octet.c",
- "crypto/asn1/x_long.c",
- "crypto/asn1/a_bytes.c",
- "crypto/asn1/t_x509a.c",
- "crypto/asn1/a_enum.c",
- "crypto/asn1/a_int.c",
- "crypto/asn1/tasn_prn.c",
- "crypto/asn1/i2d_pr.c",
- "crypto/asn1/a_utf8.c",
- "crypto/asn1/t_spki.c",
- "crypto/asn1/a_digest.c",
- "crypto/asn1/a_dup.c",
- "crypto/asn1/i2d_pu.c",
- "crypto/asn1/a_verify.c",
- "crypto/asn1/f_enum.c",
- "crypto/asn1/a_sign.c",
- "crypto/asn1/d2i_pr.c",
- "crypto/asn1/asn1_par.c",
- "crypto/asn1/x_spki.c",
- "crypto/asn1/a_d2i_fp.c",
- "crypto/asn1/f_int.c",
- "crypto/asn1/x_exten.c",
- "crypto/asn1/tasn_utl.c",
- "crypto/asn1/nsseq.c",
- "crypto/asn1/a_bitstr.c",
- "crypto/asn1/x_info.c",
- "crypto/asn1/a_strnid.c",
- "crypto/asn1/a_object.c",
- "crypto/asn1/tasn_fre.c",
- "crypto/asn1/d2i_pu.c",
- "crypto/asn1/ameth_lib.c",
- "crypto/asn1/x_attrib.c",
- "crypto/evp/m_sha.c",
- "crypto/evp/e_camellia.c",
- "crypto/evp/e_aes.c",
- "crypto/evp/bio_b64.c",
- "crypto/evp/m_sigver.c",
- "crypto/evp/m_wp.c",
- "crypto/evp/m_sha1.c",
- "crypto/evp/p_seal.c",
- "crypto/evp/c_alld.c",
- "crypto/evp/p5_crpt.c",
- "crypto/evp/e_rc4.c",
- "crypto/evp/m_ecdsa.c",
- "crypto/evp/bio_enc.c",
- "crypto/evp/e_des3.c",
- "crypto/evp/m_null.c",
- "crypto/evp/bio_ok.c",
- "crypto/evp/pmeth_gn.c",
- "crypto/evp/e_rc5.c",
- "crypto/evp/e_rc2.c",
- "crypto/evp/p_dec.c",
- "crypto/evp/p_verify.c",
- "crypto/evp/e_rc4_hmac_md5.c",
- "crypto/evp/pmeth_lib.c",
- "crypto/evp/m_ripemd.c",
- "crypto/evp/m_md5.c",
- "crypto/evp/e_bf.c",
- "crypto/evp/p_enc.c",
- "crypto/evp/m_dss.c",
- "crypto/evp/bio_md.c",
- "crypto/evp/evp_pbe.c",
- "crypto/evp/e_seed.c",
- "crypto/evp/e_cast.c",
- "crypto/evp/p_open.c",
- "crypto/evp/p5_crpt2.c",
- "crypto/evp/m_dss1.c",
- "crypto/evp/names.c",
- "crypto/evp/evp_acnf.c",
- "crypto/evp/e_des.c",
- "crypto/evp/evp_cnf.c",
- "crypto/evp/evp_lib.c",
- "crypto/evp/digest.c",
- "crypto/evp/evp_err.c",
- "crypto/evp/evp_enc.c",
- "crypto/evp/e_old.c",
- "crypto/evp/c_all.c",
- "crypto/evp/m_md2.c",
- "crypto/evp/e_xcbc_d.c",
- "crypto/evp/pmeth_fn.c",
- "crypto/evp/p_lib.c",
- "crypto/evp/evp_key.c",
- "crypto/evp/encode.c",
- "crypto/evp/e_aes_cbc_hmac_sha1.c",
- "crypto/evp/e_aes_cbc_hmac_sha256.c",
- "crypto/evp/m_mdc2.c",
- "crypto/evp/e_null.c",
- "crypto/evp/p_sign.c",
- "crypto/evp/e_idea.c",
- "crypto/evp/c_allc.c",
- "crypto/evp/evp_pkey.c",
- "crypto/evp/m_md4.c",
- "crypto/ex_data.c",
- "crypto/pkcs12/p12_p8e.c",
- "crypto/pkcs12/p12_crt.c",
- "crypto/pkcs12/p12_utl.c",
- "crypto/pkcs12/p12_attr.c",
- "crypto/pkcs12/p12_npas.c",
- "crypto/pkcs12/p12_decr.c",
- "crypto/pkcs12/p12_init.c",
- "crypto/pkcs12/p12_kiss.c",
- "crypto/pkcs12/p12_add.c",
- "crypto/pkcs12/p12_p8d.c",
- "crypto/pkcs12/p12_mutl.c",
- "crypto/pkcs12/p12_crpt.c",
- "crypto/pkcs12/pk12err.c",
- "crypto/pkcs12/p12_asn.c",
- "crypto/pkcs12/p12_key.c",
- "crypto/ecdh/ech_key.c",
- "crypto/ecdh/ech_ossl.c",
- "crypto/ecdh/ech_lib.c",
- "crypto/ecdh/ech_err.c",
- "crypto/ecdh/ech_kdf.c",
- "crypto/o_str.c",
- "crypto/conf/conf_api.c",
- "crypto/conf/conf_err.c",
- "crypto/conf/conf_def.c",
- "crypto/conf/conf_lib.c",
- "crypto/conf/conf_mall.c",
- "crypto/conf/conf_sap.c",
- "crypto/conf/conf_mod.c",
- "crypto/ebcdic.c",
- "crypto/ecdsa/ecs_lib.c",
- "crypto/ecdsa/ecs_asn1.c",
- "crypto/ecdsa/ecs_ossl.c",
- "crypto/ecdsa/ecs_vrf.c",
- "crypto/ecdsa/ecs_sign.c",
- "crypto/ecdsa/ecs_err.c",
- "crypto/dso/dso_win32.c",
- "crypto/dso/dso_lib.c",
- "crypto/dso/dso_dlfcn.c",
- "crypto/dso/dso_dl.c",
- "crypto/dso/dso_beos.c",
- "crypto/dso/dso_null.c",
- "crypto/dso/dso_vms.c",
- "crypto/dso/dso_err.c",
- "crypto/dso/dso_openssl.c",
- "crypto/cryptlib.c",
- "crypto/md5/md5_one.c",
- "crypto/md5/md5_dgst.c",
- "crypto/pkcs7/pkcs7err.c",
- "crypto/pkcs7/pk7_smime.c",
- "crypto/pkcs7/bio_pk7.c",
- "crypto/pkcs7/pk7_mime.c",
- "crypto/pkcs7/pk7_lib.c",
- "crypto/pkcs7/pk7_asn1.c",
- "crypto/pkcs7/pk7_doit.c",
- "crypto/pkcs7/pk7_attr.c",
- "crypto/md4/md4_one.c",
- "crypto/md4/md4_dgst.c",
- "crypto/o_dir.c",
- "crypto/buffer/buf_err.c",
- "crypto/buffer/buf_str.c",
- "crypto/buffer/buffer.c",
- "crypto/cms/cms_lib.c",
- "crypto/cms/cms_io.c",
- "crypto/cms/cms_err.c",
- "crypto/cms/cms_dd.c",
- "crypto/cms/cms_smime.c",
- "crypto/cms/cms_att.c",
- "crypto/cms/cms_pwri.c",
- "crypto/cms/cms_cd.c",
- "crypto/cms/cms_sd.c",
- "crypto/cms/cms_asn1.c",
- "crypto/cms/cms_env.c",
- "crypto/cms/cms_enc.c",
- "crypto/cms/cms_ess.c",
- "crypto/cms/cms_kari.c",
- "crypto/mem_dbg.c",
- "crypto/uid.c",
- "crypto/stack/stack.c",
- "crypto/ec/ec_ameth.c",
- "crypto/ec/ec_err.c",
- "crypto/ec/ec_lib.c",
- "crypto/ec/ec_curve.c",
- "crypto/ec/ec_oct.c",
- "crypto/ec/ec_asn1.c",
- "crypto/ec/ecp_oct.c",
- "crypto/ec/ec_print.c",
- "crypto/ec/ec2_smpl.c",
- "crypto/ec/ecp_nistp224.c",
- "crypto/ec/ec2_oct.c",
- "crypto/ec/eck_prn.c",
- "crypto/ec/ec_key.c",
- "crypto/ec/ecp_nist.c",
- "crypto/ec/ec_check.c",
- "crypto/ec/ecp_smpl.c",
- "crypto/ec/ec2_mult.c",
- "crypto/ec/ecp_mont.c",
- "crypto/ec/ecp_nistp521.c",
- "crypto/ec/ec_mult.c",
- "crypto/ec/ecp_nistputil.c",
- "crypto/ec/ec_pmeth.c",
- "crypto/ec/ec_cvt.c",
- "crypto/ec/ecp_nistp256.c",
- "crypto/krb5/krb5_asn.c",
- "crypto/hmac/hmac.c",
- "crypto/hmac/hm_ameth.c",
- "crypto/hmac/hm_pmeth.c",
- "crypto/comp/c_rle.c",
- "crypto/comp/c_zlib.c",
- "crypto/comp/comp_lib.c",
- "crypto/comp/comp_err.c",
- "crypto/des/fcrypt.c",
- "crypto/des/str2key.c",
- "crypto/des/cbc_cksm.c",
- "crypto/des/des_enc.c",
- "crypto/des/ofb_enc.c",
- "crypto/des/read2pwd.c",
- "crypto/des/ecb3_enc.c",
- "crypto/des/rand_key.c",
- "crypto/des/cfb64ede.c",
- "crypto/des/rpc_enc.c",
- "crypto/des/ofb64ede.c",
- "crypto/des/qud_cksm.c",
- "crypto/des/enc_writ.c",
- "crypto/des/set_key.c",
- "crypto/des/xcbc_enc.c",
- "crypto/des/fcrypt_b.c",
- "crypto/des/ede_cbcm_enc.c",
- "crypto/des/des_old2.c",
- "crypto/des/cfb_enc.c",
- "crypto/des/ecb_enc.c",
- "crypto/des/enc_read.c",
- "crypto/des/des_old.c",
- "crypto/des/ofb64enc.c",
- "crypto/des/pcbc_enc.c",
- "crypto/des/cbc_enc.c",
- "crypto/des/cfb64enc.c",
- "crypto/lhash/lh_stats.c",
- "crypto/lhash/lhash.c",
- "crypto/x509v3/v3_genn.c",
- "crypto/x509v3/pcy_cache.c",
- "crypto/x509v3/v3_sxnet.c",
- "crypto/x509v3/v3_scts.c",
- "crypto/x509v3/v3err.c",
- "crypto/x509v3/v3_conf.c",
- "crypto/x509v3/v3_utl.c",
- "crypto/x509v3/v3_akeya.c",
- "crypto/x509v3/v3_lib.c",
- "crypto/x509v3/pcy_lib.c",
- "crypto/x509v3/v3_cpols.c",
- "crypto/x509v3/v3_ia5.c",
- "crypto/x509v3/v3_bitst.c",
- "crypto/x509v3/v3_skey.c",
- "crypto/x509v3/v3_info.c",
- "crypto/x509v3/v3_asid.c",
- "crypto/x509v3/pcy_tree.c",
- "crypto/x509v3/v3_pcons.c",
- "crypto/x509v3/v3_bcons.c",
- "crypto/x509v3/v3_pku.c",
- "crypto/x509v3/v3_ocsp.c",
- "crypto/x509v3/pcy_map.c",
- "crypto/x509v3/v3_ncons.c",
- "crypto/x509v3/v3_purp.c",
- "crypto/x509v3/v3_enum.c",
- "crypto/x509v3/v3_pmaps.c",
- "crypto/x509v3/pcy_node.c",
- "crypto/x509v3/v3_pcia.c",
- "crypto/x509v3/v3_crld.c",
- "crypto/x509v3/v3_pci.c",
- "crypto/x509v3/v3_akey.c",
- "crypto/x509v3/v3_addr.c",
- "crypto/x509v3/v3_int.c",
- "crypto/x509v3/v3_alt.c",
- "crypto/x509v3/v3_extku.c",
- "crypto/x509v3/v3_prn.c",
- "crypto/x509v3/pcy_data.c",
- "crypto/aes/aes_ofb.c",
- "crypto/aes/aes_ctr.c",
- "crypto/aes/aes_ecb.c",
- "crypto/aes/aes_cfb.c",
- "crypto/aes/aes_wrap.c",
- "crypto/aes/aes_ige.c",
- "crypto/aes/aes_misc.c",
- "crypto/pqueue/pqueue.c",
- "crypto/sha/sha_one.c",
- "crypto/sha/sha_dgst.c",
- "crypto/sha/sha512.c",
- "crypto/sha/sha1_one.c",
- "crypto/sha/sha1dgst.c",
- "crypto/sha/sha256.c",
- "crypto/whrlpool/wp_dgst.c",
- "crypto/objects/obj_xref.c",
- "crypto/objects/o_names.c",
- "crypto/objects/obj_err.c",
- "crypto/objects/obj_dat.c",
- "crypto/objects/obj_lib.c",
- "crypto/mem.c",
- "crypto/fips_ers.c",
- "crypto/o_fips.c",
- "crypto/engine/eng_rdrand.c",
- "crypto/engine/eng_err.c",
- "crypto/engine/tb_ecdsa.c",
- "crypto/engine/tb_rsa.c",
- "crypto/engine/tb_cipher.c",
- "crypto/engine/tb_dsa.c",
- "crypto/engine/eng_lib.c",
- "crypto/engine/tb_asnmth.c",
- "crypto/engine/tb_ecdh.c",
- "crypto/engine/tb_dh.c",
- "crypto/engine/tb_store.c",
- "crypto/engine/eng_init.c",
- "crypto/engine/eng_cnf.c",
- "crypto/engine/eng_all.c",
- "crypto/engine/tb_digest.c",
- "crypto/engine/tb_pkmeth.c",
- "crypto/engine/eng_table.c",
- "crypto/engine/eng_ctrl.c",
- "crypto/engine/eng_list.c",
- "crypto/engine/eng_cryptodev.c",
- "crypto/engine/eng_pkey.c",
- "crypto/engine/tb_rand.c",
- "crypto/engine/eng_openssl.c",
- "crypto/engine/eng_fat.c",
- "crypto/engine/eng_dyn.c",
- "crypto/ts/ts_rsp_verify.c",
- "crypto/ts/ts_req_print.c",
- "crypto/ts/ts_verify_ctx.c",
- "crypto/ts/ts_req_utils.c",
- "crypto/ts/ts_err.c",
- "crypto/ts/ts_rsp_print.c",
- "crypto/ts/ts_rsp_utils.c",
- "crypto/ts/ts_lib.c",
- "crypto/ts/ts_conf.c",
- "crypto/ts/ts_asn1.c",
- "crypto/ts/ts_rsp_sign.c",
- "crypto/ocsp/ocsp_ext.c",
- "crypto/ocsp/ocsp_cl.c",
- "crypto/ocsp/ocsp_ht.c",
- "crypto/ocsp/ocsp_lib.c",
- "crypto/ocsp/ocsp_srv.c",
- "crypto/ocsp/ocsp_vfy.c",
- "crypto/ocsp/ocsp_err.c",
- "crypto/ocsp/ocsp_prn.c",
- "crypto/ocsp/ocsp_asn.c",
- "crypto/bf/bf_cfb64.c",
- "crypto/bf/bf_ecb.c",
- "crypto/bf/bf_enc.c",
- "crypto/bf/bf_skey.c",
- "crypto/bf/bf_ofb64.c",
- "crypto/idea/i_skey.c",
- "crypto/idea/i_ofb64.c",
- "crypto/idea/i_cbc.c",
- "crypto/idea/i_ecb.c",
- "crypto/idea/i_cfb64.c",
- "crypto/cmac/cm_ameth.c",
- "crypto/cmac/cmac.c",
- "crypto/cmac/cm_pmeth.c",
- "crypto/dh/dh_lib.c",
- "crypto/dh/dh_key.c",
- "crypto/dh/dh_asn1.c",
- "crypto/dh/dh_depr.c",
- "crypto/dh/dh_pmeth.c",
- "crypto/dh/dh_prn.c",
- "crypto/dh/dh_gen.c",
- "crypto/dh/dh_ameth.c",
- "crypto/dh/dh_check.c",
- "crypto/dh/dh_err.c",
- "crypto/dh/dh_kdf.c",
- "crypto/dh/dh_rfc5114.c",
- "crypto/modes/ccm128.c",
- "crypto/modes/ofb128.c",
- "crypto/modes/cts128.c",
- "crypto/modes/ctr128.c",
- "crypto/modes/gcm128.c",
- "crypto/modes/cbc128.c",
- "crypto/modes/cfb128.c",
- "crypto/modes/xts128.c",
- "crypto/modes/wrap128.c",
- "crypto/camellia/cmll_cfb.c",
- "crypto/camellia/cmll_ecb.c",
- "crypto/camellia/cmll_utl.c",
- "crypto/camellia/cmll_misc.c",
- "crypto/camellia/cmll_ofb.c",
- "crypto/camellia/cmll_ctr.c",
- "crypto/seed/seed_ecb.c",
- "crypto/seed/seed_cbc.c",
- "crypto/seed/seed.c",
- "crypto/seed/seed_ofb.c",
- "crypto/seed/seed_cfb.c",
- "crypto/txt_db/txt_db.c",
- "crypto/cpt_err.c",
- "crypto/pem/pem_pk8.c",
- "crypto/pem/pem_lib.c",
- "crypto/pem/pem_sign.c",
- "crypto/pem/pem_all.c",
- "crypto/pem/pem_info.c",
- "crypto/pem/pem_pkey.c",
- "crypto/pem/pem_seal.c",
- "crypto/pem/pem_err.c",
- "crypto/pem/pem_xaux.c",
- "crypto/pem/pvkfmt.c",
- "crypto/pem/pem_x509.c",
- "crypto/pem/pem_oth.c",
- "crypto/rand/rand_lib.c",
- "crypto/rand/randfile.c",
- "crypto/rand/rand_os2.c",
- "crypto/rand/rand_unix.c",
- "crypto/rand/rand_nw.c",
- "crypto/rand/md_rand.c",
- "crypto/rand/rand_err.c",
- "crypto/rand/rand_win.c",
- "crypto/rand/rand_egd.c",
- "crypto/cversion.c",
- "crypto/cast/c_ecb.c",
- "crypto/cast/c_skey.c",
- "crypto/cast/c_ofb64.c",
- "crypto/cast/c_enc.c",
- "crypto/cast/c_cfb64.c",
- "crypto/o_time.c",
- "crypto/mdc2/mdc2dgst.c",
- "crypto/mdc2/mdc2_one.c",
- "crypto/rc4/rc4_utl.c",
- "crypto/ui/ui_compat.c",
- "crypto/ui/ui_util.c",
- "crypto/ui/ui_lib.c",
- "crypto/ui/ui_err.c",
- "crypto/ui/ui_openssl.c",
- "crypto/bio/bf_buff.c",
- "crypto/bio/bss_null.c",
- "crypto/bio/bss_acpt.c",
- "crypto/bio/bss_conn.c",
- "crypto/bio/bss_fd.c",
- "crypto/bio/bf_null.c",
- "crypto/bio/bio_err.c",
- "crypto/bio/bss_sock.c",
- "crypto/bio/bss_mem.c",
- "crypto/bio/b_dump.c",
- "crypto/bio/b_print.c",
- "crypto/bio/b_sock.c",
- "crypto/bio/bss_dgram.c",
- "crypto/bio/bf_nbio.c",
- "crypto/bio/bio_lib.c",
- "crypto/bio/bss_file.c",
- "crypto/bio/bss_bio.c",
- "crypto/bio/bss_log.c",
- "crypto/bio/bio_cb.c",
- "crypto/o_init.c",
- "crypto/rc2/rc2_skey.c",
- "crypto/rc2/rc2_cbc.c",
- "crypto/rc2/rc2cfb64.c",
- "crypto/rc2/rc2_ecb.c",
- "crypto/rc2/rc2ofb64.c",
- "crypto/bn/bn_x931p.c",
- "crypto/bn/bn_blind.c",
- "crypto/bn/bn_gf2m.c",
- "crypto/bn/bn_const.c",
- "crypto/bn/bn_sqr.c",
- "crypto/bn/bn_nist.c",
- "crypto/bn/bn_rand.c",
- "crypto/bn/bn_err.c",
- "crypto/bn/bn_div.c",
- "crypto/bn/bn_kron.c",
- "crypto/bn/bn_ctx.c",
- "crypto/bn/bn_shift.c",
- "crypto/bn/bn_mod.c",
- "crypto/bn/bn_exp2.c",
- "crypto/bn/bn_word.c",
- "crypto/bn/bn_add.c",
- "crypto/bn/bn_exp.c",
- "crypto/bn/bn_mont.c",
- "crypto/bn/bn_print.c",
- "crypto/bn/bn_mul.c",
- "crypto/bn/bn_prime.c",
- "crypto/bn/bn_depr.c",
- "crypto/bn/bn_gcd.c",
- "crypto/bn/bn_mpi.c",
- "crypto/bn/bn_sqrt.c",
- "crypto/bn/bn_recp.c",
- "crypto/bn/bn_lib.c",
- "crypto/ripemd/rmd_dgst.c",
- "crypto/ripemd/rmd_one.c",
- "crypto/rsa/rsa_x931.c",
- "crypto/rsa/rsa_depr.c",
- "crypto/rsa/rsa_saos.c",
- "crypto/rsa/rsa_crpt.c",
- "crypto/rsa/rsa_pss.c",
- "crypto/rsa/rsa_oaep.c",
- "crypto/rsa/rsa_null.c",
- "crypto/rsa/rsa_gen.c",
- "crypto/rsa/rsa_prn.c",
- "crypto/rsa/rsa_pmeth.c",
- "crypto/rsa/rsa_asn1.c",
- "crypto/rsa/rsa_ssl.c",
- "crypto/rsa/rsa_ameth.c",
- "crypto/rsa/rsa_pk1.c",
- "crypto/rsa/rsa_err.c",
- "crypto/rsa/rsa_lib.c",
- "crypto/rsa/rsa_none.c",
- "crypto/rsa/rsa_chk.c",
- "crypto/rsa/rsa_eay.c",
- "crypto/rsa/rsa_sign.c",
- "crypto/srp/srp_lib.c",
- "crypto/srp/srp_vfy.c",
- "crypto/err/err.c",
- "crypto/err/err_prn.c",
- "crypto/err/err_all.c",
- "crypto/mem_clr.c",
- "crypto/rc4/rc4_skey.c",
- "crypto/rc4/rc4_enc.c",
- "crypto/camellia/camellia.c",
- "crypto/camellia/cmll_cbc.c",
- #"crypto/aes/aes_x86core.c",
- "crypto/aes/aes_core.c",
- "crypto/aes/aes_cbc.c",
- "crypto/whrlpool/wp_block.c",
- "crypto/bn/bn_asm.c",
- ]
-
- if "platform" in env and env["platform"] == "uwp":
- thirdparty_sources += ['uwp.cpp']
-
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
- env_openssl.add_source_files(env.modules_sources, thirdparty_sources)
-
- # FIXME: Clone the environment to make env_openssl and not pollute the modules env
- thirdparty_include_paths = [
- "",
- "crypto",
- "crypto/asn1",
- "crypto/evp",
- "crypto/modes",
- "openssl",
- ]
- env_openssl.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths])
-
- env_openssl.Append(CPPFLAGS=["-DOPENSSL_NO_ASM", "-DOPENSSL_THREADS", "-DL_ENDIAN"])
-
- # Workaround for compilation error with GCC/Clang when -Werror is too greedy (GH-4517)
- import os
- import methods
- if not (os.name == "nt" and os.getenv("VCINSTALLDIR")): # not Windows and not MSVC
- env_openssl.Append(CFLAGS=["-Wno-error=implicit-function-declaration"])
-
-
-# Module sources
-env_openssl.add_source_files(env.modules_sources, "*.cpp")
-
-
-# Other thirdparty dependencies
-thirdparty_misc_dir = "#thirdparty/misc/"
-thirdparty_misc_sources = [
- "curl_hostcheck.c",
-]
-thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources]
-env_openssl.add_source_files(env.modules_sources, thirdparty_misc_sources)
-
-
-# platform/uwp need to know openssl is available, pass to main env
-if "platform" in env and env["platform"] == "uwp":
- env.Append(CPPPATH=[thirdparty_dir])
- env.Append(CPPFLAGS=['-DOPENSSL_ENABLED'])
-
-Export('env')
diff --git a/modules/openssl/stream_peer_openssl.cpp b/modules/openssl/stream_peer_openssl.cpp
deleted file mode 100644
index e3cb9bbdf8..0000000000
--- a/modules/openssl/stream_peer_openssl.cpp
+++ /dev/null
@@ -1,632 +0,0 @@
-/*************************************************************************/
-/* stream_peer_openssl.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_openssl.h"
-
-// Compatibility with OpenSSL 1.1.0.
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-#define BIO_set_num(b, n)
-#else
-#define BIO_set_num(b, n) ((b)->num = (n))
-
-#define BIO_set_init(b, i) ((b)->init = (i))
-#define BIO_set_data(b, p) ((b)->ptr = (p))
-#define BIO_get_data(b) ((b)->ptr)
-#endif
-
-//hostname matching code from curl
-
-bool StreamPeerOpenSSL::_match_host_name(const char *name, const char *hostname) {
-
- return Tool_Curl_cert_hostcheck(name, hostname) == CURL_HOST_MATCH;
-}
-
-Error StreamPeerOpenSSL::_match_common_name(const char *hostname, const X509 *server_cert) {
-
- // Find the position of the CN field in the Subject field of the certificate
- int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *)server_cert), NID_commonName, -1);
-
- ERR_FAIL_COND_V(common_name_loc < 0, ERR_INVALID_PARAMETER);
-
- // Extract the CN field
- X509_NAME_ENTRY *common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *)server_cert), common_name_loc);
-
- ERR_FAIL_COND_V(common_name_entry == NULL, ERR_INVALID_PARAMETER);
-
- // Convert the CN field to a C string
- ASN1_STRING *common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
-
- ERR_FAIL_COND_V(common_name_asn1 == NULL, ERR_INVALID_PARAMETER);
-
- char *common_name_str = (char *)ASN1_STRING_data(common_name_asn1);
-
- // Make sure there isn't an embedded NUL character in the CN
- bool malformed_certificate = (size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str);
-
- ERR_FAIL_COND_V(malformed_certificate, ERR_INVALID_PARAMETER);
-
- // Compare expected hostname with the CN
-
- return _match_host_name(common_name_str, hostname) ? OK : FAILED;
-}
-
-/**
-* Tries to find a match for hostname in the certificate's Subject Alternative Name extension.
-*
-*/
-
-Error StreamPeerOpenSSL::_match_subject_alternative_name(const char *hostname, const X509 *server_cert) {
-
- Error result = FAILED;
- int i;
- int san_names_nb = -1;
- STACK_OF(GENERAL_NAME) *san_names = NULL;
-
- // Try to extract the names within the SAN extension from the certificate
- san_names = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL);
- if (san_names == NULL) {
- return ERR_FILE_NOT_FOUND;
- }
- san_names_nb = sk_GENERAL_NAME_num(san_names);
-
- // Check each name within the extension
- for (i = 0; i < san_names_nb; i++) {
- const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
-
- if (current_name->type == GEN_DNS) {
- // Current name is a DNS name, let's check it
- char *dns_name = (char *)ASN1_STRING_data(current_name->d.dNSName);
-
- // Make sure there isn't an embedded NUL character in the DNS name
- if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
- result = ERR_INVALID_PARAMETER;
- break;
- } else { // Compare expected hostname with the DNS name
- if (_match_host_name(dns_name, hostname)) {
- result = OK;
- break;
- }
- }
- }
- }
- sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
-
- return result;
-}
-
-/* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
-int StreamPeerOpenSSL::_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) {
-
- /* This is the function that OpenSSL would call if we hadn't called
- * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
- * the default functionality, rather than replacing it. */
-
- bool base_cert_valid = X509_verify_cert(x509_ctx);
- if (!base_cert_valid) {
- print_line("Cause: " + String(X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))));
- ERR_print_errors_fp(stdout);
- }
- X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
-
- ERR_FAIL_COND_V(!server_cert, 0);
-
- char cert_str[256];
- X509_NAME_oneline(X509_get_subject_name(server_cert),
- cert_str, sizeof(cert_str));
-
- print_line("CERT STR: " + String(cert_str));
- print_line("VALID: " + itos(base_cert_valid));
-
- if (!base_cert_valid)
- return 0;
-
- StreamPeerOpenSSL *ssl = (StreamPeerOpenSSL *)arg;
-
- if (ssl->validate_hostname) {
-
- Error err = _match_subject_alternative_name(ssl->hostname.utf8().get_data(), server_cert);
-
- if (err == ERR_FILE_NOT_FOUND) {
-
- err = _match_common_name(ssl->hostname.utf8().get_data(), server_cert);
- }
-
- if (err != OK) {
-
- ssl->status = STATUS_ERROR_HOSTNAME_MISMATCH;
- return 0;
- }
- }
-
- return 1;
-}
-
-int StreamPeerOpenSSL::_bio_create(BIO *b) {
- BIO_set_init(b, 1);
- BIO_set_num(b, 0);
- BIO_set_data(b, NULL);
- BIO_clear_flags(b, ~0);
- return 1;
-}
-
-int StreamPeerOpenSSL::_bio_destroy(BIO *b) {
- if (b == NULL)
- return 0;
-
- BIO_set_data(b, NULL); /* sb_tls_remove() will free it */
- BIO_set_init(b, 0);
- BIO_clear_flags(b, ~0);
- return 1;
-}
-
-int StreamPeerOpenSSL::_bio_read(BIO *b, char *buf, int len) {
-
- if (buf == NULL || len <= 0) return 0;
-
- StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)BIO_get_data(b);
-
- ERR_FAIL_COND_V(sp == NULL, 0);
-
- BIO_clear_retry_flags(b);
- if (sp->use_blocking) {
-
- Error err = sp->base->get_data((uint8_t *)buf, len);
- if (err != OK) {
- return -1;
- }
-
- return len;
- } else {
-
- int got;
- Error err = sp->base->get_partial_data((uint8_t *)buf, len, got);
- if (err != OK) {
- return -1;
- }
- if (got == 0) {
- BIO_set_retry_read(b);
- }
- return got;
- }
-
- //unreachable
- return 0;
-}
-
-int StreamPeerOpenSSL::_bio_write(BIO *b, const char *buf, int len) {
-
- if (buf == NULL || len <= 0) return 0;
-
- StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)BIO_get_data(b);
-
- ERR_FAIL_COND_V(sp == NULL, 0);
-
- BIO_clear_retry_flags(b);
- if (sp->use_blocking) {
-
- Error err = sp->base->put_data((const uint8_t *)buf, len);
- if (err != OK) {
- return -1;
- }
-
- return len;
- } else {
-
- int sent;
- Error err = sp->base->put_partial_data((const uint8_t *)buf, len, sent);
- if (err != OK) {
- return -1;
- }
- if (sent == 0) {
- BIO_set_retry_write(b);
- }
- return sent;
- }
-
- //unreachable
- return 0;
-}
-
-long StreamPeerOpenSSL::_bio_ctrl(BIO *b, int cmd, long num, void *ptr) {
- if (cmd == BIO_CTRL_FLUSH) {
- /* The OpenSSL library needs this */
- return 1;
- }
- return 0;
-}
-
-int StreamPeerOpenSSL::_bio_gets(BIO *b, char *buf, int len) {
- return -1;
-}
-
-int StreamPeerOpenSSL::_bio_puts(BIO *b, const char *str) {
- return _bio_write(b, str, strlen(str));
-}
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-BIO_METHOD *StreamPeerOpenSSL::_bio_method = NULL;
-
-BIO_METHOD *StreamPeerOpenSSL::_get_bio_method() {
- if (_bio_method) // already initialized.
- return _bio_method;
-
- /* it's a source/sink BIO */
- _bio_method = BIO_meth_new(100 | 0x400, "streampeer glue");
- BIO_meth_set_write(_bio_method, _bio_write);
- BIO_meth_set_read(_bio_method, _bio_read);
- BIO_meth_set_puts(_bio_method, _bio_puts);
- BIO_meth_set_gets(_bio_method, _bio_gets);
- BIO_meth_set_ctrl(_bio_method, _bio_ctrl);
- BIO_meth_set_create(_bio_method, _bio_create);
- BIO_meth_set_destroy(_bio_method, _bio_destroy);
-
- return _bio_method;
-}
-#else
-BIO_METHOD StreamPeerOpenSSL::_bio_method = {
- /* it's a source/sink BIO */
- (100 | 0x400),
- "streampeer glue",
- _bio_write,
- _bio_read,
- _bio_puts,
- _bio_gets,
- _bio_ctrl,
- _bio_create,
- _bio_destroy
-};
-
-BIO_METHOD *StreamPeerOpenSSL::_get_bio_method() {
- return &_bio_method;
-}
-#endif
-
-Error StreamPeerOpenSSL::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) {
-
- if (connected)
- disconnect_from_stream();
-
- hostname = p_for_hostname;
- status = STATUS_DISCONNECTED;
-
- // Set up a SSL_CTX object, which will tell our BIO object how to do its work
- ctx = SSL_CTX_new(SSLv23_client_method());
- base = p_base;
- validate_certs = p_validate_certs;
- validate_hostname = p_for_hostname != "";
-
- if (p_validate_certs) {
-
- if (certs.size()) {
- //yay for undocumented OpenSSL functions
-
- X509_STORE *store = SSL_CTX_get_cert_store(ctx);
- for (int i = 0; i < certs.size(); i++) {
-
- X509_STORE_add_cert(store, certs[i]);
- }
- }
-
- //used for testing
- //int res = SSL_CTX_load_verify_locations(ctx,"/etc/ssl/certs/ca-certificates.crt",NULL);
- //print_line("verify locations res: "+itos(res));
-
- /* Ask OpenSSL to verify the server certificate. Note that this
- * does NOT include verifying that the hostname is correct.
- * So, by itself, this means anyone with any legitimate
- * CA-issued certificate for any website, can impersonate any
- * other website in the world. This is not good. See "The
- * Most Dangerous Code in the World" article at
- * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
- */
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- /* This is how we solve the problem mentioned in the previous
- * comment. We "wrap" OpenSSL's validation routine in our
- * own routine, which also validates the hostname by calling
- * the code provided by iSECPartners. Note that even though
- * the "Everything You've Always Wanted to Know About
- * Certificate Validation With OpenSSL (But Were Afraid to
- * Ask)" paper from iSECPartners says very explicitly not to
- * call SSL_CTX_set_cert_verify_callback (at the bottom of
- * page 2), what we're doing here is safe because our
- * cert_verify_callback() calls X509_verify_cert(), which is
- * OpenSSL's built-in routine which would have been called if
- * we hadn't set the callback. Therefore, we're just
- * "wrapping" OpenSSL's routine, not replacing it. */
- SSL_CTX_set_cert_verify_callback(ctx, _cert_verify_callback, this);
-
- //Let the verify_callback catch the verify_depth error so that we get an appropriate error in the logfile. (??)
- SSL_CTX_set_verify_depth(ctx, max_cert_chain_depth + 1);
- }
-
- ssl = SSL_new(ctx);
- bio = BIO_new(_get_bio_method());
- BIO_set_data(bio, this);
- SSL_set_bio(ssl, bio, bio);
-
- if (p_for_hostname != String()) {
- SSL_set_tlsext_host_name(ssl, p_for_hostname.utf8().get_data());
- }
-
- use_blocking = true; // let handshake use blocking
- // Set the SSL to automatically retry on failure.
- SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
-
- // Same as before, try to connect.
- int result = SSL_connect(ssl);
-
- print_line("CONNECTION RESULT: " + itos(result));
- if (result < 1) {
- ERR_print_errors_fp(stdout);
- _print_error(result);
- }
-
- X509 *peer = SSL_get_peer_certificate(ssl);
-
- if (peer) {
- bool cert_ok = SSL_get_verify_result(ssl) == X509_V_OK;
- print_line("cert_ok: " + itos(cert_ok));
-
- } else if (validate_certs) {
- status = STATUS_ERROR_NO_CERTIFICATE;
- }
-
- connected = true;
- status = STATUS_CONNECTED;
-
- return OK;
-}
-
-Error StreamPeerOpenSSL::accept_stream(Ref<StreamPeer> p_base) {
-
- return ERR_UNAVAILABLE;
-}
-
-void StreamPeerOpenSSL::_print_error(int err) {
-
- err = SSL_get_error(ssl, err);
- switch (err) {
- case SSL_ERROR_NONE:
- ERR_PRINT("NO ERROR: The TLS/SSL I/O operation completed");
- break;
- case SSL_ERROR_ZERO_RETURN:
- ERR_PRINT("The TLS/SSL connection has been closed.");
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- ERR_PRINT("The operation did not complete.");
- break;
- case SSL_ERROR_WANT_CONNECT:
- case SSL_ERROR_WANT_ACCEPT:
- ERR_PRINT("The connect/accept operation did not complete");
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- ERR_PRINT("The operation did not complete because an application callback set by SSL_CTX_set_client_cert_cb() has asked to be called again.");
- break;
- case SSL_ERROR_SYSCALL:
- ERR_PRINT("Some I/O error occurred. The OpenSSL error queue may contain more information on the error.");
- break;
- case SSL_ERROR_SSL:
- ERR_PRINT("A failure in the SSL library occurred, usually a protocol error.");
- break;
- }
-}
-
-Error StreamPeerOpenSSL::put_data(const uint8_t *p_data, int p_bytes) {
-
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
-
- while (p_bytes > 0) {
- int ret = SSL_write(ssl, p_data, p_bytes);
- if (ret <= 0) {
- _print_error(ret);
- disconnect_from_stream();
- return ERR_CONNECTION_ERROR;
- }
- p_data += ret;
- p_bytes -= ret;
- }
-
- return OK;
-}
-
-Error StreamPeerOpenSSL::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
-
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
- if (p_bytes == 0)
- return OK;
-
- Error err = put_data(p_data, p_bytes);
- if (err != OK)
- return err;
-
- r_sent = p_bytes;
- return OK;
-}
-
-Error StreamPeerOpenSSL::get_data(uint8_t *p_buffer, int p_bytes) {
-
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
-
- while (p_bytes > 0) {
-
- int ret = SSL_read(ssl, p_buffer, p_bytes);
- if (ret <= 0) {
- _print_error(ret);
- disconnect_from_stream();
- return ERR_CONNECTION_ERROR;
- }
- p_buffer += ret;
- p_bytes -= ret;
- }
-
- return OK;
-}
-
-Error StreamPeerOpenSSL::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
-
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
- if (p_bytes == 0) {
- r_received = 0;
- return OK;
- }
-
- Error err = get_data(p_buffer, p_bytes);
- if (err != OK)
- return err;
- r_received = p_bytes;
- return OK;
-}
-
-int StreamPeerOpenSSL::get_available_bytes() const {
-
- ERR_FAIL_COND_V(!connected, 0);
-
- return SSL_pending(ssl);
-}
-StreamPeerOpenSSL::StreamPeerOpenSSL() {
-
- ctx = NULL;
- ssl = NULL;
- bio = NULL;
- connected = false;
- use_blocking = true; //might be improved int the future, but for now it always blocks
- max_cert_chain_depth = 9;
- flags = 0;
-}
-
-void StreamPeerOpenSSL::disconnect_from_stream() {
-
- if (!connected)
- return;
- SSL_shutdown(ssl);
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- base = Ref<StreamPeer>();
- connected = false;
- validate_certs = false;
- validate_hostname = false;
- status = STATUS_DISCONNECTED;
-}
-
-StreamPeerOpenSSL::Status StreamPeerOpenSSL::get_status() const {
-
- return status;
-}
-
-StreamPeerOpenSSL::~StreamPeerOpenSSL() {
- disconnect_from_stream();
-}
-
-StreamPeerSSL *StreamPeerOpenSSL::_create_func() {
-
- return memnew(StreamPeerOpenSSL);
-}
-
-Vector<X509 *> StreamPeerOpenSSL::certs;
-
-void StreamPeerOpenSSL::_load_certs(const PoolByteArray &p_array) {
-
- PoolByteArray::Read r = p_array.read();
- BIO *mem = BIO_new(BIO_s_mem());
- BIO_puts(mem, (const char *)r.ptr());
- while (true) {
- X509 *cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
- if (!cert)
- break;
- certs.push_back(cert);
- }
- BIO_free(mem);
-}
-
-void StreamPeerOpenSSL::initialize_ssl() {
-
- available = true;
-
- load_certs_func = _load_certs;
-
- _create = _create_func;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
-#endif
- SSL_library_init(); // Initialize OpenSSL's SSL libraries
- SSL_load_error_strings(); // Load SSL error strings
- ERR_load_BIO_strings(); // Load BIO error strings
- OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
- String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
- ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
- if (certs_path != "") {
-
- FileAccess *f = FileAccess::open(certs_path, FileAccess::READ);
- if (f) {
- PoolByteArray arr;
- int flen = f->get_len();
- arr.resize(flen + 1);
- {
- PoolByteArray::Write w = arr.write();
- f->get_buffer(w.ptr(), flen);
- w[flen] = 0; //end f string
- }
-
- memdelete(f);
-
- _load_certs(arr);
- print_line("Loaded certs from '" + certs_path + "': " + itos(certs.size()));
- }
- }
- String config_path = GLOBAL_DEF("network/ssl/config", "");
- ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/config", PropertyInfo(Variant::STRING, "network/ssl/config", PROPERTY_HINT_FILE, "*.cnf"));
- if (config_path != "") {
-
- Vector<uint8_t> data = FileAccess::get_file_as_array(config_path);
- if (data.size()) {
- data.push_back(0);
- BIO *mem = BIO_new(BIO_s_mem());
- BIO_puts(mem, (const char *)data.ptr());
-
- while (true) {
- X509 *cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
- if (!cert)
- break;
- certs.push_back(cert);
- }
- BIO_free(mem);
- }
- print_line("Loaded certs from '" + certs_path + "': " + itos(certs.size()));
- }
-}
-
-void StreamPeerOpenSSL::finalize_ssl() {
-
- for (int i = 0; i < certs.size(); i++) {
- X509_free(certs[i]);
- }
- certs.clear();
-}
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/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 76c0b969d8..8174bccdb7 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -536,8 +536,8 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int
int p_x, p_y;
- int p_modulation[8][16];
- int p_modulation_modes[8][16];
+ int p_modulation[8][16] = { { 0 } };
+ int p_modulation_modes[8][16] = { { 0 } };
int Mod, DoPT;
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/SCsub b/modules/regex/SCsub
index 18b4051afe..4b8d5e9283 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -19,10 +19,13 @@ if env['builtin_pcre2']:
"pcre2_compile.c",
"pcre2_config.c",
"pcre2_context.c",
+ "pcre2_convert.c",
"pcre2_dfa_match.c",
"pcre2_error.c",
+ "pcre2_extuni.c",
"pcre2_find_bracket.c",
"pcre2_jit_compile.c",
+ #"pcre2_jit_match.c", "pcre2_jit_misc.c", # these files are included in pcre2_jit_compile.c.
"pcre2_maketables.c",
"pcre2_match.c",
"pcre2_match_data.c",
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/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index 2cf80acd28..75e8903ff8 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="RegEx" inherits="Reference" category="Core" version="3.0-stable">
+<class name="RegEx" inherits="Reference" category="Core" version="3.1">
<brief_description>
Class for searching text for patterns using regular expressions.
</brief_description>
<description>
- Regular Expression (or regex) is a compact programming language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explainations on the Internet.
+ Regular Expression (or regex) is a compact programming language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet.
To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used.
[codeblock]
var regex = RegEx.new()
regex.compile("\\w-(\\d+)")
[/codeblock]
The search pattern must be escaped first for gdscript before it is escaped for the expression. For example, [code]compile("\\d+")[/code] would be read by RegEx as [code]\d+[/code]. Similarly, [code]compile("\"(?:\\\\.|[^\"])*\"")[/code] would be read as [code]"(?:\\.|[^"])*"[/code]
- Using [method search] you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using fuctions such as [method RegExMatch.get_string] and [method RegExMatch.get_start].
+ Using [method search] you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using functions such as [method RegExMatch.get_string] and [method RegExMatch.get_start].
[codeblock]
var regex = RegEx.new()
regex.compile("\\w-(\\d+)")
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 9eba0f738b..3d070d2786 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="RegExMatch" inherits="Reference" category="Core" version="3.0-stable">
+<class name="RegExMatch" inherits="Reference" category="Core" version="3.1">
<brief_description>
Contains the results of a regex search.
</brief_description>
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/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 18ab616826..c95a8ac2dd 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -263,8 +263,8 @@ float AudioStreamOGGVorbis::get_length() const {
void AudioStreamOGGVorbis::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamOGGVorbis::set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &AudioStreamOGGVorbis::get_data);
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
+ ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);
ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);
@@ -272,7 +272,7 @@ void AudioStreamOGGVorbis::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
- ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset");
}
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/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
index 827e947a79..e2281babf7 100644
--- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.0-stable">
+<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.1">
<brief_description>
OGG Vorbis audio stream driver.
</brief_description>
@@ -13,6 +13,9 @@
<methods>
</methods>
<members>
+ <member name="data" type="PoolByteArray" setter="set_data" getter="get_data">
+ Contains the audio data in bytes.
+ </member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop">
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset">
diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml
index 9a095c3ddd..018f4734ec 100644
--- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml
+++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.0-stable">
+<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
index 16ebfa2832..c8acdb689a 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
@@ -100,6 +100,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
ogg_stream.instance();
ogg_stream->set_data(data);
+ ERR_FAIL_COND_V(!ogg_stream->get_data().size(), ERR_FILE_CORRUPT);
ogg_stream->set_loop(loop);
ogg_stream->set_loop_offset(loop_offset);
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/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml
index a280d767c3..5fc40a6eba 100644
--- a/modules/theora/doc_classes/ResourceImporterTheora.xml
+++ b/modules/theora/doc_classes/ResourceImporterTheora.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.0-stable">
+<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml
index 9da3dc0d02..e7c4727332 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.0-stable">
+<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 58c6d73ab2..9e6307c0bf 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -261,14 +261,12 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
/* look for further theora headers */
while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) {
if (ret < 0) {
- fprintf(stderr, "Error parsing Theora stream headers; "
- "corrupt stream?\n");
+ fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n");
clear();
return;
}
if (!th_decode_headerin(&ti, &tc, &ts, &op)) {
- fprintf(stderr, "Error parsing Theora stream headers; "
- "corrupt stream?\n");
+ fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n");
clear();
return;
}
@@ -312,9 +310,15 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
td = th_decode_alloc(&ti, ts);
px_fmt = ti.pixel_fmt;
switch (ti.pixel_fmt) {
- case TH_PF_420: printf(" 4:2:0 video\n"); break;
- case TH_PF_422: printf(" 4:2:2 video\n"); break;
- case TH_PF_444: printf(" 4:4:4 video\n"); break;
+ case TH_PF_420:
+ //printf(" 4:2:0 video\n");
+ break;
+ case TH_PF_422:
+ //printf(" 4:2:2 video\n");
+ break;
+ case TH_PF_444:
+ //printf(" 4:4:4 video\n");
+ break;
case TH_PF_RSVD:
default:
printf(" video\n (UNKNOWN Chroma sampling!)\n");
@@ -519,7 +523,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
#else
if (file && /*!videobuf_ready && */ no_theora && theora_eos) {
#endif
- printf("video done, stopping\n");
+ //printf("video done, stopping\n");
stop();
return;
};
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="&quot;&quot;">
+ </argument>
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
+ </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="&quot;UDP&quot;">
+ </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="&quot;InternetGatewayDevice&quot;">
+ </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="&quot;&quot;">
+ </argument>
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
+ </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="&quot;UDP&quot;">
+ </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 a6a43f31b8..28764aca40 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScript" inherits="Script" category="Core" version="3.0-stable">
+<class name="VisualScript" inherits="Script" category="Core" version="3.1">
<brief_description>
A script implemented in the Visual Script programming environment.
</brief_description>
@@ -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/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
index d63a6ad524..793291eca2 100644
--- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node representing a constant from the base types.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index da4db29086..399ba8ef5d 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node used to call built-in functions.
</brief_description>
@@ -80,7 +80,7 @@
Return the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
</constant>
<constant name="MATH_EXP" value="20" enum="BuiltinFunc">
- Return [b]e[/b] raised to the power of the input. [b]e[/b] sometimes called "Euler's number" is a mathematical constant whose value is approximately 2.71828.
+ Return the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
</constant>
<constant name="MATH_ISNAN" value="21" enum="BuiltinFunc">
Return whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
index 189a6f6ad8..2e3a5b33c0 100644
--- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Gets a constant from a given class.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml
index 5462c379ad..a4a890ea8a 100644
--- a/modules/visual_script/doc_classes/VisualScriptComment.xml
+++ b/modules/visual_script/doc_classes/VisualScriptComment.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node used to annotate the script.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml
index bb70a17357..353898c93a 100644
--- a/modules/visual_script/doc_classes/VisualScriptCondition.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node which branches the flow.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml
index e2ccb50bfd..ed633c4135 100644
--- a/modules/visual_script/doc_classes/VisualScriptConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Gets a contant's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
index da6779b79d..14c44c6970 100644
--- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml
+++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node which calls a base type constructor.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
index 33d2f1437a..b8e77a1b0f 100644
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A scripted Visual Script node.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
index 09fcba4314..d3158df357 100644
--- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
+++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node which deconstructs a base type instance into its parts.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml
index 8e26758a31..fc49cfc07b 100644
--- a/modules/visual_script/doc_classes/VisualScriptEditor.xml
+++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.0-stable">
+<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
index 30f96011d4..4bb05525bd 100644
--- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
+++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Emits a specified signal.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
index 0dc0cdf5eb..93d7ce3516 100644
--- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
+++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
A Visual Script node returning a singleton from [@GlobalScope]
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml
index 91f55edb2b..343e83cb55 100644
--- a/modules/visual_script/doc_classes/VisualScriptExpression.xml
+++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml
index bd59d739ea..ec8e955cf7 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
index e2b732a250..f6116cf539 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
index 614176498a..c75dd0cdbc 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable">
+<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
index a36f7809c2..9d43204f02 100644
--- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
index b2d0a194e0..73c1f47e1a 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
index 7ad200afa4..652c29a9ac 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
index 45c493887b..ab4c23012b 100644
--- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml
index 28e8a66182..7090621bd7 100644
--- a/modules/visual_script/doc_classes/VisualScriptIterator.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Steps through items in a given input.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
index 66faf448cb..5c8ee6453c 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Gets a local variable's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
index 8a816e5dd7..f2e6c48907 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Changes a local variable's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
index 45fa471c41..d456e880b7 100644
--- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Commonly used mathematical constants.
</brief_description>
<description>
- Provides common math constants, such as Pi or Euler's constant, on an output Data port.
+ Provides common math constants, such as Pi, on an output Data port.
[b]Input Ports:[/b]
none
[b]Output Ports:[/b]
@@ -35,7 +35,7 @@
Tau: [code]6.283185[/code]
</constant>
<constant name="MATH_CONSTANT_E" value="4" enum="MathConstant">
- Natural log: [code]2.718282[/code]
+ Mathematical constant [code]e[/code], the natural log base: [code]2.718282[/code]
</constant>
<constant name="MATH_CONSTANT_SQRT2" value="5" enum="MathConstant">
Square root of two: [code]1.414214[/code]
diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml
index e9d1cd949f..941a0cd91a 100644
--- a/modules/visual_script/doc_classes/VisualScriptNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptNode.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.0-stable">
+<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.1">
<brief_description>
A node which is part of a [VisualScript].
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml
index 4538bd3c78..e60d50c977 100644
--- a/modules/visual_script/doc_classes/VisualScriptOperator.xml
+++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml
index 3dae0e4b81..5a2886ccac 100644
--- a/modules/visual_script/doc_classes/VisualScriptPreload.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Creates a new [Resource] or loads one from the filesystem.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
index 7555c83960..60cc8fdd4f 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
index dc6a9efd83..8f29e9d152 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
index 3789626ed0..f6300e03f0 100644
--- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
+++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml
index 1172b7555b..6095520eff 100644
--- a/modules/visual_script/doc_classes/VisualScriptReturn.xml
+++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Exits a function and returns an optional value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
index 4c6181e040..7704eaba04 100644
--- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Node reference.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
index 68cc0d0b55..2c2af9262d 100644
--- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml
index 017efdb07a..0731fc77e1 100644
--- a/modules/visual_script/doc_classes/VisualScriptSelect.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Chooses between two input values.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml
index e9b480bbae..61a73e104c 100644
--- a/modules/visual_script/doc_classes/VisualScriptSelf.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Outputs a reference to the current instance.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml
index be793ae36e..c71e068045 100644
--- a/modules/visual_script/doc_classes/VisualScriptSequence.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Executes a series of Sequence ports.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
index 85db63b78a..46aeebab9c 100644
--- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml
index ec7565b31a..a00811a29b 100644
--- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Branches program flow based on a given input's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
index d414a95657..0bdc4ce89d 100644
--- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
+++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
index ccd2918ec8..06178a399d 100644
--- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Gets a variable's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
index e1fc1ba762..5969f25060 100644
--- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Changes a variable's value.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml
index de1ff45746..b9e7f6a553 100644
--- a/modules/visual_script/doc_classes/VisualScriptWhile.xml
+++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
Conditional loop.
</brief_description>
diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml
index f21b53861a..c4698f746a 100644
--- a/modules/visual_script/doc_classes/VisualScriptYield.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYield.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
index 5075fb6ded..b67e4ab1b8 100644
--- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable">
+<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 2809cff362..11401c0460 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -112,7 +112,9 @@ void register_visual_script_types() {
register_visual_script_expression_node();
#ifdef TOOLS_ENABLED
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
ClassDB::register_class<_VisualScriptEditor>();
+ ClassDB::set_current_api(ClassDB::API_CORE);
vs_editor_singleton = memnew(_VisualScriptEditor);
Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", _VisualScriptEditor::get_singleton()));
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 5987fdf5da..9331092171 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -121,6 +121,10 @@ Array VisualScriptNode::_get_default_input_values() const {
return default_input_values;
}
+String VisualScriptNode::get_text() const {
+ return "";
+}
+
void VisualScriptNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visual_script"), &VisualScriptNode::get_visual_script);
@@ -1967,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)) {
@@ -1983,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) {
@@ -2028,6 +2032,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
function.flow_stack_size = 0;
function.pass_stack_size = 0;
function.node_count = 0;
+
Map<StringName, int> local_var_indices;
if (function.node < 0) {
@@ -2182,7 +2187,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
Ref<VisualScriptNode> node = F->get().node;
VisualScriptNodeInstance *instance = instances[F->key()];
- // conect to default values
+ // connect to default values
for (int i = 0; i < instance->input_port_count; i++) {
if (instance->input_ports[i] == -1) {
@@ -2192,7 +2197,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
}
}
- // conect to trash
+ // connect to trash
for (int i = 0; i < instance->output_port_count; i++) {
if (instance->output_ports[i] == -1) {
instance->output_ports[i] = function.trash_pos; //trash is same for all
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 69bb522173..aaa6dfea11 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -78,7 +78,7 @@ public:
Variant get_default_input_value(int p_port) const;
virtual String get_caption() const = 0;
- virtual String get_text() const = 0;
+ virtual String get_text() const;
virtual String get_category() const = 0;
//used by editor, this is not really saved
@@ -374,12 +374,10 @@ class VisualScriptInstance : public ScriptInstance {
int node;
int max_stack;
int trash_pos;
- int return_pos;
int flow_stack_size;
int pass_stack_size;
int node_count;
int argument_count;
- bool valid;
};
Map<StringName, Function> functions;
@@ -435,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_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 7f0a42da82..73b6d702c1 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -666,12 +666,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
return PropertyInfo(t, "");
}
+/*
String VisualScriptBuiltinFunc::get_caption() const {
return "BuiltinFunc";
}
+*/
-String VisualScriptBuiltinFunc::get_text() const {
+String VisualScriptBuiltinFunc::get_caption() const {
return func_name[func];
}
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index f862d5c26f..3345762b9f 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -129,7 +129,7 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
+ //virtual String get_text() const;
virtual String get_category() const { return "functions"; }
void set_func(BuiltinFunc p_which);
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index b116dfdcf7..0618064ea6 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -268,7 +268,7 @@ protected:
if (String(p_name) == "export") {
script->set_variable_export(var, p_value);
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
return true;
}
@@ -527,6 +527,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
GraphNode *gnode = memnew(GraphNode);
gnode->set_title(node->get_caption());
+ gnode->set_offset(pos * EDSCALE);
if (error_line == E->get()) {
gnode->set_overlay(GraphNode::OVERLAY_POSITION);
} else if (node->is_breakpoint()) {
@@ -543,8 +544,10 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_show_close_button(true);
}
- if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
+ bool has_gnode_text = false;
+ if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
+ has_gnode_text = true;
LineEdit *line_edit = memnew(LineEdit);
line_edit->set_text(node->get_text());
line_edit->set_expand_to_text_length(true);
@@ -552,9 +555,13 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->add_child(line_edit);
line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get()));
} else {
- Label *text = memnew(Label);
- text->set_text(node->get_text());
- gnode->add_child(text);
+ String text = node->get_text();
+ if (!text.empty()) {
+ has_gnode_text = true;
+ Label *label = memnew(Label);
+ label->set_text(text);
+ gnode->add_child(label);
+ }
}
if (Object::cast_to<VisualScriptComment>(node.ptr())) {
@@ -589,9 +596,21 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
int slot_idx = 0;
bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
- gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- gnode->set_offset(pos * EDSCALE);
- slot_idx++;
+ if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
+ // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
+ // we still draw the disabled default ones to shift up the slots by one,
+ // so the slots DON'T start with the content text.
+
+ // IF has_gnode_text is false, but we DO want to draw default sequence ports,
+ // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
+ if (!has_gnode_text) {
+ Label *dummy = memnew(Label);
+ dummy->set_text(" ");
+ gnode->add_child(dummy);
+ }
+ gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
+ slot_idx++;
+ }
int mixed_seq_ports = 0;
@@ -1308,6 +1327,35 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
}
}
+void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventKey> key = p_event;
+ if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
+ if (members->has_focus()) {
+ TreeItem *ti = members->get_selected();
+ if (ti) {
+ TreeItem *root = members->get_root();
+ if (ti->get_parent() == root->get_children()) {
+ member_type = MEMBER_FUNCTION;
+ }
+ if (ti->get_parent() == root->get_children()->get_next()) {
+ member_type = MEMBER_VARIABLE;
+ }
+ if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
+ member_type = MEMBER_SIGNAL;
+ }
+ member_name = ti->get_text(0);
+ }
+ if (ED_IS_SHORTCUT("visual_script_editor/delete_selected", p_event)) {
+ _member_option(MEMBER_REMOVE);
+ }
+ if (ED_IS_SHORTCUT("visual_script_editor/edit_member", p_event)) {
+ _member_option(MEMBER_EDIT);
+ }
+ }
+ }
+}
+
Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
if (p_from == nodes) {
@@ -3090,7 +3138,7 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
member_type = MEMBER_FUNCTION;
member_name = ti->get_text(0);
- member_popup->add_icon_item(del_icon, TTR("Remove Function"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
member_popup->popup();
return;
}
@@ -3099,9 +3147,9 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
member_type = MEMBER_VARIABLE;
member_name = ti->get_text(0);
- member_popup->add_icon_item(edit_icon, TTR("Edit Variable"), MEMBER_EDIT);
+ member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
member_popup->add_separator();
- member_popup->add_icon_item(del_icon, TTR("Remove Variable"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
member_popup->popup();
return;
}
@@ -3110,9 +3158,9 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
member_type = MEMBER_SIGNAL;
member_name = ti->get_text(0);
- member_popup->add_icon_item(edit_icon, TTR("Edit Signal"), MEMBER_EDIT);
+ member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
member_popup->add_separator();
- member_popup->add_icon_item(del_icon, TTR("Remove Signal"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
member_popup->popup();
return;
}
@@ -3207,6 +3255,12 @@ void VisualScriptEditor::_member_option(int p_option) {
}
}
+void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
+void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
@@ -3243,6 +3297,7 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw);
ClassDB::bind_method("_input", &VisualScriptEditor::_input);
+ ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input);
ClassDB::bind_method("_on_nodes_delete", &VisualScriptEditor::_on_nodes_delete);
ClassDB::bind_method("_on_nodes_duplicate", &VisualScriptEditor::_on_nodes_duplicate);
@@ -3305,6 +3360,7 @@ VisualScriptEditor::VisualScriptEditor() {
members->connect("button_pressed", this, "_member_button");
members->connect("item_edited", this, "_member_edited");
members->connect("cell_selected", this, "_member_selected", varray(), CONNECT_DEFERRED);
+ members->connect("gui_input", this, "_members_gui_input");
members->set_allow_reselect(true);
members->set_hide_folding(true);
members->set_drag_forwarding(this);
@@ -3478,12 +3534,13 @@ static void register_editor_callback() {
ScriptEditor::register_create_script_editor_function(create_editor);
- ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"));
+ ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"), KEY_DELETE);
ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C);
ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X);
ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V);
+ ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E);
}
void VisualScriptEditor::register_editor() {
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 4d789e6ef7..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();
@@ -211,6 +211,7 @@ class VisualScriptEditor : public ScriptEditorBase {
String revert_on_drag;
void _input(const Ref<InputEvent> &p_event);
+ void _members_gui_input(const Ref<InputEvent> &p_event);
void _on_nodes_delete();
void _on_nodes_duplicate();
@@ -245,6 +246,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 16de04e4cf..d5f9d21348 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -455,7 +455,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
break;
}
- if (cchar == '-' || (cchar >= '0' && cchar <= '9')) {
+ if (cchar >= '0' && cchar <= '9') {
//a number
String num;
@@ -466,11 +466,6 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
#define READING_DONE 4
int reading = READING_INT;
- if (cchar == '-') {
- num += '-';
- cchar = GET_CHAR();
- }
-
CharType c = cchar;
bool exp_sign = false;
bool exp_beg = false;
@@ -1146,7 +1141,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression..");
+ _set_error("Unexpected end of expression...");
return NULL;
}
}
@@ -1166,7 +1161,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1175,7 +1170,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 5c097dfa76..ea23ab1b2a 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -772,7 +772,7 @@ PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const {
String VisualScriptTypeCast::get_caption() const {
- return "TypeCast";
+ return "Type Cast";
}
String VisualScriptTypeCast::get_text() const {
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index 187c9b0b9e..bdf5705ecd 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -262,26 +262,6 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
}
String VisualScriptFunctionCall::get_caption() const {
-
- static const char *cname[5] = {
- "CallSelf",
- "CallNode",
- "CallInstance",
- "CallBasic",
- "CallSingleton"
- };
-
- String caption = cname[call_mode];
-
- if (rpc_call_mode) {
- caption += " (RPC)";
- }
-
- return caption;
-}
-
-String VisualScriptFunctionCall::get_text() const {
-
if (call_mode == CALL_MODE_SELF)
return " " + String(function) + "()";
if (call_mode == CALL_MODE_SINGLETON)
@@ -294,6 +274,14 @@ String VisualScriptFunctionCall::get_text() const {
return " " + base_type + "." + String(function) + "()";
}
+String VisualScriptFunctionCall::get_text() const {
+
+ if (rpc_call_mode) {
+ return "RPC";
+ }
+ return "";
+}
+
void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
if (basic_type == p_type)
@@ -1075,36 +1063,31 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons
String VisualScriptPropertySet::get_caption() const {
- static const char *cname[4] = {
- "Self",
- "Node",
- "Instance",
- "Basic"
- };
-
static const char *opname[ASSIGN_OP_MAX] = {
- "Set", "Add", "Sub", "Mul", "Div", "Mod", "ShiftLeft", "ShiftRight", "BitAnd", "BitOr", "BitXor"
+ "Set", "Add", "Subtract", "Multiply", "Divide", "Mod", "ShiftLeft", "ShiftRight", "BitAnd", "BitOr", "BitXor"
};
- return String(cname[call_mode]) + opname[assign_op];
+
+ String prop = String(opname[assign_op]) + " " + property;
+ if (index != StringName()) {
+ prop += "." + String(index);
+ }
+
+ return prop;
}
String VisualScriptPropertySet::get_text() const {
- String prop;
+ if (call_mode == CALL_MODE_BASIC_TYPE) {
+ return String("On ") + Variant::get_type_name(basic_type);
+ }
- if (call_mode == CALL_MODE_BASIC_TYPE)
- prop = Variant::get_type_name(basic_type) + "." + property;
- else if (call_mode == CALL_MODE_NODE_PATH)
- prop = String(base_path) + ":" + property;
- else if (call_mode == CALL_MODE_SELF)
- prop = property;
- else if (call_mode == CALL_MODE_INSTANCE)
- prop = String(base_type) + ":" + property;
+ static const char *cname[3] = {
+ "Self",
+ "Scene Node",
+ "Instance"
+ };
- if (index != StringName()) {
- prop += "." + String(index);
- }
- return prop;
+ return String("On ") + cname[call_mode];
}
void VisualScriptPropertySet::_update_base_type() {
@@ -1838,30 +1821,22 @@ PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) cons
String VisualScriptPropertyGet::get_caption() const {
- static const char *cname[4] = {
- "SelfGet",
- "NodeGet",
- "InstanceGet",
- "BasicGet"
- };
-
- return cname[call_mode];
+ return String("Get ") + property;
}
String VisualScriptPropertyGet::get_text() const {
- String prop;
+ if (call_mode == CALL_MODE_BASIC_TYPE) {
+ return String("On ") + Variant::get_type_name(basic_type);
+ }
- if (call_mode == CALL_MODE_BASIC_TYPE)
- prop = Variant::get_type_name(basic_type) + "." + property;
- else if (call_mode == CALL_MODE_NODE_PATH)
- prop = String(base_path) + ":" + property;
- else if (call_mode == CALL_MODE_SELF)
- prop = property;
- else if (call_mode == CALL_MODE_INSTANCE)
- prop = String(base_type) + ":" + property;
+ static const char *cname[3] = {
+ "Self",
+ "Scene Node",
+ "Instance"
+ };
- return prop;
+ return String("On ") + cname[call_mode];
}
void VisualScriptPropertyGet::set_base_type(const StringName &p_type) {
@@ -2399,12 +2374,7 @@ PropertyInfo VisualScriptEmitSignal::get_output_value_port_info(int p_idx) const
String VisualScriptEmitSignal::get_caption() const {
- return "EmitSignal";
-}
-
-String VisualScriptEmitSignal::get_text() const {
-
- return "emit " + String(name);
+ return "Emit " + String(name);
}
void VisualScriptEmitSignal::set_signal(const StringName &p_type) {
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
index 0b30eae65a..3b522564d4 100644
--- a/modules/visual_script/visual_script_func_nodes.h
+++ b/modules/visual_script/visual_script_func_nodes.h
@@ -346,7 +346,7 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
+ //virtual String get_text() const;
virtual String get_category() const { return "functions"; }
void set_signal(const StringName &p_type);
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index e0b4fde237..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) {
@@ -425,7 +425,7 @@ PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
static const Variant::Type port_types[Variant::OP_MAX] = {
- //comparation
+ //comparison
Variant::BOOL, //OP_EQUAL,
Variant::BOOL, //OP_NOT_EQUAL,
Variant::BOOL, //OP_LESS,
@@ -466,13 +466,13 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
}
static const char *op_names[] = {
- //comparation
- "Equal", //OP_EQUAL,
- "NotEqual", //OP_NOT_EQUAL,
- "Less", //OP_LESS,
- "LessEqual", //OP_LESS_EQUAL,
- "Greater", //OP_GREATER,
- "GreaterEq", //OP_GREATER_EQUAL,
+ //comparison
+ "Are Equal", //OP_EQUAL,
+ "Are Not Equal", //OP_NOT_EQUAL,
+ "Less Than", //OP_LESS,
+ "Less Than or Equal", //OP_LESS_EQUAL,
+ "Greater Than", //OP_GREATER,
+ "Greater Than or Equal", //OP_GREATER_EQUAL,
//mathematic
"Add", //OP_ADD,
"Subtract", //OP_SUBTRACT,
@@ -481,14 +481,14 @@ static const char *op_names[] = {
"Negate", //OP_NEGATE,
"Positive", //OP_POSITIVE,
"Remainder", //OP_MODULE,
- "Concat", //OP_STRING_CONCAT,
+ "Concatenate", //OP_STRING_CONCAT,
//bitwise
- "ShiftLeft", //OP_SHIFT_LEFT,
- "ShiftRight", //OP_SHIFT_RIGHT,
- "BitAnd", //OP_BIT_AND,
- "BitOr", //OP_BIT_OR,
- "BitXor", //OP_BIT_XOR,
- "BitNeg", //OP_BIT_NEGATE,
+ "Bit Shift Left", //OP_SHIFT_LEFT,
+ "Bit Shift Right", //OP_SHIFT_RIGHT,
+ "Bit And", //OP_BIT_AND,
+ "Bit Or", //OP_BIT_OR,
+ "Bit Xor", //OP_BIT_XOR,
+ "Bit Negate", //OP_BIT_NEGATE,
//logic
"And", //OP_AND,
"Or", //OP_OR,
@@ -500,13 +500,8 @@ static const char *op_names[] = {
String VisualScriptOperator::get_caption() const {
- return op_names[op];
-}
-
-String VisualScriptOperator::get_text() const {
-
static const wchar_t *op_names[] = {
- //comparation
+ //comparison
L"A = B", //OP_EQUAL,
L"A \u2260 B", //OP_NOT_EQUAL,
L"A < B", //OP_LESS,
@@ -803,14 +798,8 @@ PropertyInfo VisualScriptVariableGet::get_output_value_port_info(int p_idx) cons
String VisualScriptVariableGet::get_caption() const {
- return "Variable";
+ return "Get " + variable;
}
-
-String VisualScriptVariableGet::get_text() const {
-
- return variable;
-}
-
void VisualScriptVariableGet::set_variable(StringName p_variable) {
if (variable == p_variable)
@@ -928,12 +917,7 @@ PropertyInfo VisualScriptVariableSet::get_output_value_port_info(int p_idx) cons
String VisualScriptVariableSet::get_caption() const {
- return "VariableSet";
-}
-
-String VisualScriptVariableSet::get_text() const {
-
- return variable;
+ return "Set " + variable;
}
void VisualScriptVariableSet::set_variable(StringName p_variable) {
@@ -1044,7 +1028,7 @@ PropertyInfo VisualScriptConstant::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptConstant::get_output_value_port_info(int p_idx) const {
PropertyInfo pinfo;
- pinfo.name = "get";
+ pinfo.name = String(value);
pinfo.type = type;
return pinfo;
}
@@ -1054,11 +1038,6 @@ String VisualScriptConstant::get_caption() const {
return "Constant";
}
-String VisualScriptConstant::get_text() const {
-
- return String(value);
-}
-
void VisualScriptConstant::set_constant_type(Variant::Type p_type) {
if (type == p_type)
@@ -1174,10 +1153,20 @@ PropertyInfo VisualScriptPreload::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo = PropertyInfo(Variant::OBJECT, "res");
+ PropertyInfo pinfo;
+ pinfo.type = Variant::OBJECT;
if (preload.is_valid()) {
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = preload->get_class();
+ if (preload->get_path().is_resource_file()) {
+ pinfo.name = preload->get_path();
+ } else if (preload->get_name() != String()) {
+ pinfo.name = preload->get_name();
+ } else {
+ pinfo.name = preload->get_class();
+ }
+ } else {
+ pinfo.name = "<empty>";
}
return pinfo;
@@ -1188,21 +1177,6 @@ String VisualScriptPreload::get_caption() const {
return "Preload";
}
-String VisualScriptPreload::get_text() const {
-
- if (preload.is_valid()) {
- if (preload->get_path().is_resource_file()) {
- return preload->get_path();
- } else if (preload->get_name() != String()) {
- return preload->get_name();
- } else {
- return preload->get_class();
- }
- } else {
- return "<empty>";
- }
-}
-
void VisualScriptPreload::set_preload(const Ref<Resource> &p_preload) {
if (preload == p_preload)
@@ -1291,12 +1265,7 @@ PropertyInfo VisualScriptIndexGet::get_output_value_port_info(int p_idx) const {
String VisualScriptIndexGet::get_caption() const {
- return "IndexGet";
-}
-
-String VisualScriptIndexGet::get_text() const {
-
- return String("get");
+ return "Get Index";
}
class VisualScriptNodeInstanceIndexGet : public VisualScriptNodeInstance {
@@ -1371,12 +1340,7 @@ PropertyInfo VisualScriptIndexSet::get_output_value_port_info(int p_idx) const {
String VisualScriptIndexSet::get_caption() const {
- return "IndexSet";
-}
-
-String VisualScriptIndexSet::get_text() const {
-
- return String("set");
+ return "Set Index";
}
class VisualScriptNodeInstanceIndexSet : public VisualScriptNodeInstance {
@@ -1439,18 +1403,13 @@ PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) co
}
PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const {
-
- return PropertyInfo(Variant::REAL, "value");
+ String name = GlobalConstants::get_global_constant_name(index);
+ return PropertyInfo(Variant::REAL, name);
}
String VisualScriptGlobalConstant::get_caption() const {
- return "GlobalConst";
-}
-
-String VisualScriptGlobalConstant::get_text() const {
-
- return GlobalConstants::get_global_constant_name(index);
+ return "Global Constant";
}
void VisualScriptGlobalConstant::set_global_constant(int p_which) {
@@ -1539,17 +1498,12 @@ PropertyInfo VisualScriptClassConstant::get_input_value_port_info(int p_idx) con
PropertyInfo VisualScriptClassConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::INT, "value");
+ return PropertyInfo(Variant::INT, String(base_type) + "." + String(name));
}
String VisualScriptClassConstant::get_caption() const {
- return "ClassConst";
-}
-
-String VisualScriptClassConstant::get_text() const {
-
- return String(base_type) + "." + String(name);
+ return "Class Constant";
}
void VisualScriptClassConstant::set_class_constant(const StringName &p_which) {
@@ -1673,7 +1627,7 @@ PropertyInfo VisualScriptBasicTypeConstant::get_output_value_port_info(int p_idx
String VisualScriptBasicTypeConstant::get_caption() const {
- return "BasicConst";
+ return "Basic Constant";
}
String VisualScriptBasicTypeConstant::get_text() const {
@@ -1828,17 +1782,12 @@ PropertyInfo VisualScriptMathConstant::get_input_value_port_info(int p_idx) cons
PropertyInfo VisualScriptMathConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::REAL, "value");
+ return PropertyInfo(Variant::REAL, const_name[constant]);
}
String VisualScriptMathConstant::get_caption() const {
- return "MathConst";
-}
-
-String VisualScriptMathConstant::get_text() const {
-
- return const_name[constant];
+ return "Math Constant";
}
void VisualScriptMathConstant::set_math_constant(MathConstant p_which) {
@@ -1903,7 +1852,7 @@ VisualScriptMathConstant::VisualScriptMathConstant() {
}
//////////////////////////////////////////
-////////////////GLOBALSINGLETON///////////
+////////////////ENGINESINGLETON///////////
//////////////////////////////////////////
int VisualScriptEngineSingleton::get_output_sequence_port_count() const {
@@ -1937,17 +1886,12 @@ PropertyInfo VisualScriptEngineSingleton::get_input_value_port_info(int p_idx) c
PropertyInfo VisualScriptEngineSingleton::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
+ return PropertyInfo(Variant::OBJECT, singleton);
}
String VisualScriptEngineSingleton::get_caption() const {
- return "EngineSingleton";
-}
-
-String VisualScriptEngineSingleton::get_text() const {
-
- return singleton;
+ return "Get Engine Singleton";
}
void VisualScriptEngineSingleton::set_singleton(const String &p_string) {
@@ -2058,17 +2002,12 @@ PropertyInfo VisualScriptSceneNode::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSceneNode::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "node");
+ return PropertyInfo(Variant::OBJECT, path.simplified());
}
String VisualScriptSceneNode::get_caption() const {
- return "SceneNode";
-}
-
-String VisualScriptSceneNode::get_text() const {
-
- return path.simplified();
+ return "Get Scene Node";
}
void VisualScriptSceneNode::set_node_path(const NodePath &p_path) {
@@ -2259,17 +2198,12 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
+ return PropertyInfo(Variant::OBJECT, "Scene Tree");
}
String VisualScriptSceneTree::get_caption() const {
- return "SceneTree";
-}
-
-String VisualScriptSceneTree::get_text() const {
-
- return "";
+ return "Get Scene Tree";
}
class VisualScriptNodeInstanceSceneTree : public VisualScriptNodeInstance {
@@ -2361,17 +2295,12 @@ PropertyInfo VisualScriptResourcePath::get_input_value_port_info(int p_idx) cons
PropertyInfo VisualScriptResourcePath::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::STRING, "path");
+ return PropertyInfo(Variant::STRING, path);
}
String VisualScriptResourcePath::get_caption() const {
- return "ResourcePath";
-}
-
-String VisualScriptResourcePath::get_text() const {
-
- return path;
+ return "Resource Path";
}
void VisualScriptResourcePath::set_resource_path(const String &p_path) {
@@ -2453,20 +2382,18 @@ PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
-}
-
-String VisualScriptSelf::get_caption() const {
+ String type_name;
+ if (get_visual_script().is_valid())
+ type_name = get_visual_script()->get_instance_base_type();
+ else
+ type_name = "instance";
- return "Self";
+ return PropertyInfo(Variant::OBJECT, type_name);
}
-String VisualScriptSelf::get_text() const {
+String VisualScriptSelf::get_caption() const {
- if (get_visual_script().is_valid())
- return get_visual_script()->get_instance_base_type();
- else
- return "";
+ return "Get Self";
}
class VisualScriptNodeInstanceSelf : public VisualScriptNodeInstance {
@@ -3032,12 +2959,7 @@ PropertyInfo VisualScriptConstructor::get_output_value_port_info(int p_idx) cons
String VisualScriptConstructor::get_caption() const {
- return "Construct";
-}
-
-String VisualScriptConstructor::get_text() const {
-
- return "new " + Variant::get_type_name(type) + "()";
+ return "Construct " + Variant::get_type_name(type);
}
String VisualScriptConstructor::get_category() const {
@@ -3163,17 +3085,12 @@ PropertyInfo VisualScriptLocalVar::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptLocalVar::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, "get");
+ return PropertyInfo(type, name);
}
String VisualScriptLocalVar::get_caption() const {
- return "LocalVarGet";
-}
-
-String VisualScriptLocalVar::get_text() const {
-
- return name;
+ return "Get Local Var";
}
String VisualScriptLocalVar::get_category() const {
@@ -3289,7 +3206,7 @@ PropertyInfo VisualScriptLocalVarSet::get_output_value_port_info(int p_idx) cons
String VisualScriptLocalVarSet::get_caption() const {
- return "LocalVarSet";
+ return "Set Local Var";
}
String VisualScriptLocalVarSet::get_text() const {
@@ -3427,12 +3344,7 @@ PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) cons
String VisualScriptInputAction::get_caption() const {
- return "Action";
-}
-
-String VisualScriptInputAction::get_text() const {
-
- return name;
+ return "Action " + name;
}
String VisualScriptInputAction::get_category() const {
@@ -3600,12 +3512,7 @@ PropertyInfo VisualScriptDeconstruct::get_output_value_port_info(int p_idx) cons
String VisualScriptDeconstruct::get_caption() const {
- return "Deconstruct";
-}
-
-String VisualScriptDeconstruct::get_text() const {
-
- return "from " + Variant::get_type_name(type) + ":";
+ return "Deconstruct " + Variant::get_type_name(type);
}
String VisualScriptDeconstruct::get_category() const {
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index a581e81c8c..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);
@@ -124,7 +124,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
void set_operator(Variant::Operator p_op);
@@ -194,7 +193,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_variable(StringName p_variable);
@@ -228,7 +226,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_variable(StringName p_variable);
@@ -263,7 +260,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_constant_type(Variant::Type p_type);
@@ -299,7 +295,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_preload(const Ref<Resource> &p_preload);
@@ -327,7 +322,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -352,7 +346,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "operators"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -381,7 +374,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_global_constant(int p_which);
@@ -416,7 +408,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_class_constant(const StringName &p_which);
@@ -505,7 +496,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "constants"; }
void set_math_constant(MathConstant p_which);
@@ -539,7 +529,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_singleton(const String &p_string);
@@ -575,7 +564,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_node_path(const NodePath &p_path);
@@ -609,7 +597,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -641,7 +628,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
void set_resource_path(const String &p_path);
@@ -672,7 +658,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const { return "data"; }
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
@@ -822,7 +807,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_constructor_type(Variant::Type p_type);
@@ -859,7 +843,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_var_name(const StringName &p_name);
@@ -942,7 +925,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_action_name(const StringName &p_name);
@@ -993,7 +975,6 @@ public:
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
virtual String get_caption() const;
- virtual String get_text() const;
virtual String get_category() const;
void set_deconstruct_type(Variant::Type p_type);
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/SCsub b/modules/webm/SCsub
index 2f1a28a54c..33561da098 100644
--- a/modules/webm/SCsub
+++ b/modules/webm/SCsub
@@ -18,6 +18,10 @@ thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file
env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources)
env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"])
+# upstream uses c++11
+if (not env_webm.msvc):
+ env_webm.Append(CCFLAGS="-std=c++11")
+
# also requires libogg, libvorbis and libopus
if env['builtin_libogg']:
env_webm.Append(CPPPATH=["#thirdparty/libogg"])
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/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml
index 20e0e48187..0cfab1baf0 100644
--- a/modules/webm/doc_classes/ResourceImporterWebm.xml
+++ b/modules/webm/doc_classes/ResourceImporterWebm.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.0-stable">
+<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml
index 94aea5c8d2..c02a7a8016 100644
--- a/modules/webm/doc_classes/VideoStreamWebm.xml
+++ b/modules/webm/doc_classes/VideoStreamWebm.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.0-stable">
+<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index 73ba17d184..c681e2b34f 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -254,7 +254,6 @@ env_libvpx = env.Clone()
env_libvpx.Append(CPPPATH=[libvpx_dir])
cpu_bits = env["bits"]
-osx_fat = (env["platform"] == 'osx' and cpu_bits == 'fat')
webm_cpu_x86 = False
webm_cpu_arm = False
if env["platform"] == 'uwp':
@@ -264,16 +263,13 @@ if env["platform"] == 'uwp':
webm_cpu_x86 = True
else:
import platform
- is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and platform.machine().startswith('arm'))
+ is_x11_or_server_arm = ((env["platform"] == 'x11' or env["platform"] == 'server') and (platform.machine().startswith('arm') or platform.machine().startswith('aarch')))
is_ios_x86 = (env["platform"] == 'iphone' and env["ios_sim"])
is_android_x86 = (env["platform"] == 'android' and env["android_arch"] == 'x86')
if is_android_x86:
cpu_bits = '32'
- if osx_fat:
- webm_cpu_x86 = True
- else:
- webm_cpu_x86 = not is_x11_or_server_arm and (cpu_bits == '32' or cpu_bits == '64') and (env["platform"] == 'windows' or env["platform"] == 'x11' or env["platform"] == 'osx' or env["platform"] == 'haiku' or is_android_x86 or is_ios_x86)
- webm_cpu_arm = is_x11_or_server_arm or (not is_ios_x86 and env["platform"] == 'iphone') or (not is_android_x86 and env["platform"] == 'android')
+ webm_cpu_x86 = not is_x11_or_server_arm and (cpu_bits == '32' or cpu_bits == '64') and (env["platform"] == 'windows' or env["platform"] == 'x11' or env["platform"] == 'osx' or env["platform"] == 'haiku' or is_android_x86 or is_ios_x86)
+ webm_cpu_arm = is_x11_or_server_arm or (not is_ios_x86 and env["platform"] == 'iphone') or (not is_android_x86 and env["platform"] == 'android')
if webm_cpu_x86:
import subprocess
@@ -303,28 +299,22 @@ if webm_cpu_x86:
webm_simd_optimizations = False
if webm_cpu_x86:
- if osx_fat:
- #'osx' platform only: run python script which will compile using 'yasm' command and then merge 32-bit and 64-bit using 'lipo' command
- env_libvpx["AS"] = 'python modules/webm/libvpx/yasm_osx_fat.py'
- env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:]
- env_libvpx["ASCOM"] = '$AS $ASFLAGS $TARGET $SOURCES'
+ if env["platform"] == 'windows' or env["platform"] == 'uwp':
+ env_libvpx["ASFORMAT"] = 'win'
+ elif env["platform"] == 'osx' or env["platform"] == "iphone":
+ env_libvpx["ASFORMAT"] = 'macho'
else:
- if env["platform"] == 'windows' or env["platform"] == 'uwp':
- env_libvpx["ASFORMAT"] = 'win'
- elif env["platform"] == 'osx' or env["platform"] == "iphone":
- env_libvpx["ASFORMAT"] = 'macho'
- else:
- env_libvpx["ASFORMAT"] = 'elf'
- env_libvpx["ASFORMAT"] += cpu_bits
-
- env_libvpx["AS"] = 'yasm'
- env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:] + ' -f $ASFORMAT -D $ASCPU'
- env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES'
+ env_libvpx["ASFORMAT"] = 'elf'
+ env_libvpx["ASFORMAT"] += cpu_bits
- if cpu_bits == '32':
- env_libvpx["ASCPU"] = 'X86_32'
- elif cpu_bits == '64':
- env_libvpx["ASCPU"] = 'X86_64'
+ env_libvpx["AS"] = 'yasm'
+ env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:] + ' -f $ASFORMAT -D $ASCPU'
+ env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES'
+
+ if cpu_bits == '32':
+ env_libvpx["ASCPU"] = 'X86_32'
+ elif cpu_bits == '64':
+ env_libvpx["ASCPU"] = 'X86_64'
env_libvpx.Append(CCFLAGS=['-DWEBM_X86ASM'])
@@ -333,7 +323,7 @@ if webm_cpu_x86:
if webm_cpu_arm:
if env["platform"] == 'iphone':
env_libvpx["ASFLAGS"] = '-arch armv7'
- elif env["platform"] == 'android' or env["platform"] == 'x11' or env["platform"] == 'server':
+ elif env["platform"] == 'android' and env["android_arch"] == 'armv7' or env["platform"] == 'x11' or env["platform"] == 'server':
env_libvpx["ASFLAGS"] = '-mfpu=neon'
elif env["platform"] == 'uwp':
env_libvpx["AS"] = 'armasm'
@@ -347,10 +337,9 @@ if webm_cpu_arm:
if webm_simd_optimizations == False:
print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!")
-
env_libvpx.add_source_files(env.modules_sources, libvpx_sources)
if webm_cpu_x86:
- is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"])
+ is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ)
env_libvpx_mmx = env_libvpx.Clone()
if cpu_bits == '32' and is_clang_or_gcc:
@@ -375,7 +364,7 @@ if webm_cpu_x86:
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86)
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86asm)
- if cpu_bits == '64' or osx_fat:
+ if cpu_bits == '64':
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86_64asm)
elif webm_cpu_arm:
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm)
@@ -389,5 +378,5 @@ elif webm_cpu_arm:
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_armasm_ms)
elif env["platform"] == 'iphone':
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas_apple)
- else:
+ elif (is_x11_or_server_arm and cpu_bits == '32') or (env["platform"] == 'android' and not env["android_arch"] == 'arm64v8'):
env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas)
diff --git a/modules/webm/libvpx/yasm_osx_fat.py b/modules/webm/libvpx/yasm_osx_fat.py
deleted file mode 100644
index 0065e2766c..0000000000
--- a/modules/webm/libvpx/yasm_osx_fat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import os
-
-includes = sys.argv[1]
-output_file = sys.argv[2]
-input_file = sys.argv[3]
-
-can_remove = {}
-
-lipo_command = ''
-
-exit_code = 1
-
-for arch in ['32', '64']:
- if arch == '32' and input_file.endswith('x86_64.asm'):
- can_remove[arch] = False
- else:
- command = 'yasm ' + includes + ' -f macho' + arch + ' -D X86_' + arch + ' -o ' + output_file + '.' + arch + ' ' + input_file
- print(command)
- if os.system(command) == 0:
- lipo_command += output_file + '.' + arch + ' '
- can_remove[arch] = True
- else:
- can_remove[arch] = False
-
-if lipo_command != '':
- lipo_command = 'lipo -create ' + lipo_command + '-output ' + output_file
- print(lipo_command)
- if os.system(lipo_command) == 0:
- exit_code = 0
-
-for arch in ['32', '64']:
- if can_remove[arch]:
- os.remove(output_file + '.' + arch)
-
-sys.exit(exit_code)
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index ea7af1bf9e..21ae0ce7c2 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -78,10 +78,12 @@ if env['builtin_libwebp']:
"dsp/upsampling_msa.c",
"dsp/upsampling_neon.c",
"dsp/upsampling_sse2.c",
+ "dsp/upsampling_sse41.c",
"dsp/yuv.c",
"dsp/yuv_mips32.c",
"dsp/yuv_mips_dsp_r2.c",
"dsp/yuv_sse2.c",
+ "dsp/yuv_sse41.c",
"enc/alpha_enc.c",
"enc/analysis_enc.c",
"enc/backward_references_cost_enc.c",
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
new file mode 100644
index 0000000000..15a88773e7
--- /dev/null
+++ b/modules/websocket/SCsub
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+# Thirdparty source files
+
+env_lws = env_modules.Clone()
+
+thirdparty_dir = "#thirdparty/libwebsockets/"
+helper_dir = "win32helpers/"
+thirdparty_sources = [
+
+ "core/alloc.c",
+ "core/context.c",
+ "core/libwebsockets.c",
+ "core/output.c",
+ "core/pollfd.c",
+ "core/service.c",
+
+ "event-libs/poll/poll.c",
+
+ "misc/base64-decode.c",
+ "misc/lejp.c",
+ "misc/sha-1.c",
+
+ "roles/h1/ops-h1.c",
+ "roles/http/header.c",
+ "roles/http/client/client.c",
+ "roles/http/client/client-handshake.c",
+ "roles/http/server/fops-zip.c",
+ "roles/http/server/lejp-conf.c",
+ "roles/http/server/parsers.c",
+ "roles/http/server/server.c",
+ "roles/listen/ops-listen.c",
+ "roles/pipe/ops-pipe.c",
+ "roles/raw/ops-raw.c",
+
+ "roles/ws/client-ws.c",
+ "roles/ws/client-parser-ws.c",
+ "roles/ws/ops-ws.c",
+ "roles/ws/server-ws.c",
+
+ "tls/tls.c",
+ "tls/tls-client.c",
+ "tls/tls-server.c",
+
+ "tls/mbedtls/wrapper/library/ssl_cert.c",
+ "tls/mbedtls/wrapper/library/ssl_pkey.c",
+ "tls/mbedtls/wrapper/library/ssl_stack.c",
+ "tls/mbedtls/wrapper/library/ssl_methods.c",
+ "tls/mbedtls/wrapper/library/ssl_lib.c",
+ "tls/mbedtls/wrapper/library/ssl_x509.c",
+ "tls/mbedtls/wrapper/platform/ssl_port.c",
+ "tls/mbedtls/wrapper/platform/ssl_pm.c",
+ "tls/mbedtls/lws-genhash.c",
+ "tls/mbedtls/mbedtls-client.c",
+ "tls/mbedtls/lws-genrsa.c",
+ "tls/mbedtls/ssl.c",
+ "tls/mbedtls/mbedtls-server.c"
+]
+
+if env_lws["platform"] == "android": # Builtin getifaddrs
+ thirdparty_sources += ["misc/getifaddrs.c"]
+
+if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock
+ thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
+else: # Unix socket
+ thirdparty_sources += ["plat/lws-plat-unix.c"]
+
+
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+if env_lws["platform"] == "javascript": # No need to add third party libraries at all
+ pass
+else:
+ env_lws.add_source_files(env.modules_sources, thirdparty_sources)
+ env_lws.Append(CPPPATH=[thirdparty_dir])
+
+ 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']:
+ mbedtls_includes = "#thirdparty/mbedtls/include"
+ env_lws.Prepend(CPPPATH=[mbedtls_includes])
+
+ if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
+ env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
+
+ if env_lws["platform"] == "uwp":
+ env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
+
+env_lws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/config.py b/modules/websocket/config.py
new file mode 100644
index 0000000000..f59ef432b4
--- /dev/null
+++ b/modules/websocket/config.py
@@ -0,0 +1,16 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
+
+def get_doc_classes():
+ return [
+ "WebSocketClient",
+ "WebSocketMultiplayerPeer",
+ "WebSocketPeer",
+ "WebSocketServer"
+ ]
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
new file mode 100644
index 0000000000..2e11e1a44c
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ A WebSocket client implementation
+ </brief_description>
+ <description>
+ This class implements a WebSocket client compatible with any RFC 6455 complaint WebSocket server.
+ This client can be optionally used as a network peer for the [MultiplayerAPI].
+ After starting the client ([method connect_to_url]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
+ You will received appropriate signals when connecting, disconnecting, or when new data is available.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="connect_to_url">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="url" type="String">
+ </argument>
+ <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
+ </argument>
+ <argument index="2" name="gd_mp_api" type="bool" default="false">
+ </argument>
+ <description>
+ Connect to the given URL requesting one of the given [code]protocols[/code] as sub-protocol.
+ If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connnections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true.
+ </description>
+ </method>
+ <method name="disconnect_from_host">
+ <return type="void">
+ </return>
+ <description>
+ Disconnect from the server if currently connected.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
+ Enable or disable SSL certificate verification. Note: You must specify the certificates to be used in the project settings for it to work when exported.
+ </member>
+ </members>
+ <signals>
+ <signal name="connection_closed">
+ <description>
+ Emitted when the connection to the server is closed.
+ </description>
+ </signal>
+ <signal name="connection_error">
+ <description>
+ Emitted when the connection to the server fails.
+ </description>
+ </signal>
+ <signal name="connection_established">
+ <argument index="0" name="protocol" type="String">
+ </argument>
+ <description>
+ Emitted when a connection with the server is established, [code]protocol[/code] will contain the sub-protocol agreed with the server.
+ </description>
+ </signal>
+ <signal name="data_received">
+ <description>
+ Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
new file mode 100644
index 0000000000..1a841f85ed
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ Base class for WebSocket server and client.
+ </brief_description>
+ <description>
+ Base class for WebSocket server and client, allowing them to be used as network peer for the [MultiplayerAPI].
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_peer" qualifiers="const">
+ <return type="WebSocketPeer">
+ </return>
+ <argument index="0" name="peer_id" type="int">
+ </argument>
+ <description>
+ Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="peer_packet">
+ <argument index="0" name="peer_source" type="int">
+ </argument>
+ <description>
+ Emitted when a packet is received from a peer. Note: this signal is only emitted when the client or server is configured to use Godot multiplayer API.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
new file mode 100644
index 0000000000..85a08e0c0b
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.1">
+ <brief_description>
+ A class representing a specific WebSocket connection.
+ </brief_description>
+ <description>
+ This class represent a specific WebSocket connection, you can do lower level operations with it.
+ You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ Close this WebSocket connection, actively disconnecting the peer.
+ </description>
+ </method>
+ <method name="get_connected_host" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ Returns the IP Address of the connected peer. (Not available in HTML5 export)
+ </description>
+ </method>
+ <method name="get_connected_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the remote port of the connected peer. (Not available in HTML5 export)
+ </description>
+ </method>
+ <method name="get_write_mode" qualifiers="const">
+ <return type="int" enum="WebSocketPeer.WriteMode">
+ </return>
+ <description>
+ Get the current selected write mode. See [enum WriteMode].
+ </description>
+ </method>
+ <method name="is_connected_to_host" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if this peer is currently connected.
+ </description>
+ </method>
+ <method name="set_write_mode">
+ <return type="void">
+ </return>
+ <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode">
+ </argument>
+ <description>
+ Sets the socket to use the given [enum WriteMode].
+ </description>
+ </method>
+ <method name="was_string_packet" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if the last received packet was sent as a text payload. See [enum WriteMode]
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
+ Specify that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed).
+ </constant>
+ <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
+ Specify that WebSockets messages should be transferred as binary payload (any byte combination is allowed).
+ </constant>
+ </constants>
+</class>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
new file mode 100644
index 0000000000..a1061e446b
--- /dev/null
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ A WebSocket server implementation
+ </brief_description>
+ <description>
+ This class implements a WebSocket server that can also support the high level multiplayer API.
+ After starting the server ([method listen]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
+ Note: This class will not work in HTML5 exports due to browser restrictions.
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="disconnect_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Disconnects the given peer.
+ </description>
+ </method>
+ <method name="get_peer_address" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the IP address of the given peer.
+ </description>
+ </method>
+ <method name="get_peer_port" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns the remote port of the given peer.
+ </description>
+ </method>
+ <method name="has_peer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Returns [code]true[/code] if a peer with the given ID is connected.
+ </description>
+ </method>
+ <method name="is_listening" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if the server is actively listening on a port.
+ </description>
+ </method>
+ <method name="listen">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="port" type="int">
+ </argument>
+ <argument index="1" name="protocols" type="PoolStringArray" default="PoolStringArray( )">
+ </argument>
+ <argument index="2" name="gd_mp_api" type="bool" default="false">
+ </argument>
+ <description>
+ Start listening on the given port.
+ You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used.
+ You can use this server as a network peer for [MultiplayerAPI] by passing true as "gd_mp_api". Note: [signal data_received] will not be fired and clients other than Godot will not work in this case.
+ </description>
+ </method>
+ <method name="stop">
+ <return type="void">
+ </return>
+ <description>
+ Stop the server and clear its state.
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="client_connected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="protocol" type="String">
+ </argument>
+ <description>
+ Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client.
+ </description>
+ </signal>
+ <signal name="client_disconnected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a client disconnects.
+ </description>
+ </signal>
+ <signal name="data_received">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ Emitted when a new message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
new file mode 100644
index 0000000000..1405fa98b0
--- /dev/null
+++ b/modules/websocket/emws_client.cpp
@@ -0,0 +1,224 @@
+/*************************************************************************/
+/* emws_client.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifdef JAVASCRIPT_ENABLED
+
+#include "emws_client.h"
+#include "core/io/ip.h"
+#include "emscripten.h"
+
+extern "C" {
+EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
+ EMWSClient *client = static_cast<EMWSClient *>(obj);
+ client->_is_connecting = false;
+ client->_on_connect(String(proto));
+}
+
+EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
+ EMWSClient *client = static_cast<EMWSClient *>(obj);
+
+ static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
+ client->_on_peer_packet();
+}
+
+EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
+ EMWSClient *client = static_cast<EMWSClient *>(obj);
+ client->_is_connecting = false;
+ client->_on_error();
+}
+
+EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) {
+ EMWSClient *client = static_cast<EMWSClient *>(obj);
+ client->_is_connecting = false;
+ client->_on_disconnect();
+}
+}
+
+Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
+
+ String str = "ws://";
+ String proto_string = "";
+ int i = 0;
+
+ if (p_ssl)
+ str = "wss://";
+ str += p_host + ":" + itos(p_port) + p_path;
+ for (int i = 0; i < p_protocols.size(); i++) {
+ proto_string += p_protocols[i];
+ proto_string += ",";
+ }
+ if (proto_string == "")
+ proto_string = "binary,";
+
+ proto_string = proto_string.substr(0, proto_string.length() - 1);
+
+ _is_connecting = true;
+ /* clang-format off */
+ int peer_sock = EM_ASM_INT({
+ var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(","));
+ var c_ptr = Module.IDHandler.get($0);
+ socket.binaryType = "arraybuffer";
+
+ // Connection opened
+ socket.addEventListener("open", function (event) {
+ if (!Module.IDHandler.has($0))
+ return; // Godot Object is gone!
+ ccall("_esws_on_connect",
+ "void",
+ ["number", "string"],
+ [c_ptr, socket.protocol]
+ );
+ });
+
+ // Listen for messages
+ socket.addEventListener("message", function (event) {
+ if (!Module.IDHandler.has($0))
+ return; // Godot Object is gone!
+ var buffer;
+ var is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+
+ buffer = new Uint8Array(event.data);
+
+ } else if (event.data instanceof Blob) {
+
+ alert("Blob type not supported");
+ return;
+
+ } else if (typeof event.data === "string") {
+
+ is_string = 1;
+ var enc = new TextEncoder("utf-8");
+ buffer = new Uint8Array(enc.encode(event.data));
+
+ } else {
+
+ alert("Unknown message type");
+ return;
+
+ }
+ var len = buffer.length*buffer.BYTES_PER_ELEMENT;
+ var out = Module._malloc(len);
+ Module.HEAPU8.set(buffer, out);
+ ccall("_esws_on_message",
+ "void",
+ ["number", "number", "number", "number"],
+ [c_ptr, out, len, is_string]
+ );
+ Module._free(out);
+ });
+
+ socket.addEventListener("error", function (event) {
+ if (!Module.IDHandler.has($0))
+ return; // Godot Object is gone!
+ ccall("_esws_on_error",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ });
+
+ socket.addEventListener("close", function (event) {
+ if (!Module.IDHandler.has($0))
+ return; // Godot Object is gone!
+ var was_clean = 0;
+ if (event.was_clean)
+ was_clean = 1;
+ ccall("_esws_on_close",
+ "void",
+ ["number", "number", "string", "number"],
+ [c_ptr, event.code, event.reason, was_clean]
+ );
+ });
+
+ return Module.IDHandler.add(socket);
+ }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
+ /* clang-format on */
+
+ static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock);
+
+ return OK;
+};
+
+void EMWSClient::poll() {
+}
+
+Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const {
+
+ return _peer;
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const {
+
+ if (_peer->is_connected_to_host())
+ return CONNECTION_CONNECTED;
+
+ if (_is_connecting)
+ return CONNECTION_CONNECTING;
+
+ return CONNECTION_DISCONNECTED;
+};
+
+void EMWSClient::disconnect_from_host() {
+
+ _peer->close();
+};
+
+IP_Address EMWSClient::get_connected_host() const {
+
+ return IP_Address();
+};
+
+uint16_t EMWSClient::get_connected_port() const {
+
+ return 1025;
+};
+
+EMWSClient::EMWSClient() {
+ _is_connecting = false;
+ _peer = Ref<EMWSPeer>(memnew(EMWSPeer));
+ /* clang-format off */
+ _js_id = EM_ASM_INT({
+ return Module.IDHandler.add($0);
+ }, this);
+ /* clang-format on */
+};
+
+EMWSClient::~EMWSClient() {
+
+ disconnect_from_host();
+ _peer = Ref<EMWSPeer>();
+ /* clang-format off */
+ EM_ASM({
+ Module.IDHandler.remove($0);
+ }, _js_id);
+ /* clang-format on */
+};
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
new file mode 100644
index 0000000000..6c2fa23b53
--- /dev/null
+++ b/modules/websocket/emws_client.h
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* emws_client.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 EMWSCLIENT_H
+#define EMWSCLIENT_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "emws_peer.h"
+#include "websocket_client.h"
+
+class EMWSClient : public WebSocketClient {
+
+ GDCIIMPL(EMWSClient, WebSocketClient);
+
+private:
+ int _js_id;
+
+public:
+ bool _is_connecting;
+
+ Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
+ Ref<WebSocketPeer> get_peer(int p_peer_id) const;
+ void disconnect_from_host();
+ IP_Address get_connected_host() const;
+ uint16_t get_connected_port() const;
+ virtual ConnectionStatus get_connection_status() const;
+ virtual void poll();
+ EMWSClient();
+ ~EMWSClient();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // EMWSCLIENT_H
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
new file mode 100644
index 0000000000..e0b987b4d7
--- /dev/null
+++ b/modules/websocket/emws_peer.cpp
@@ -0,0 +1,175 @@
+/*************************************************************************/
+/* emws_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifdef JAVASCRIPT_ENABLED
+
+#include "emws_peer.h"
+#include "core/io/ip.h"
+
+void EMWSPeer::set_sock(int p_sock) {
+
+ peer_sock = p_sock;
+ in_buffer.clear();
+ queue_count = 0;
+}
+
+void EMWSPeer::set_write_mode(WriteMode p_mode) {
+ write_mode = p_mode;
+}
+
+EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
+ return write_mode;
+}
+
+void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+
+ if (in_buffer.space_left() < p_size + 5) {
+ ERR_EXPLAIN("Buffer full! Dropping data");
+ ERR_FAIL();
+ }
+
+ uint8_t is_string = p_is_string ? 1 : 0;
+ in_buffer.write((uint8_t *)&p_size, 4);
+ in_buffer.write((uint8_t *)&is_string, 1);
+ in_buffer.write(p_data, p_size);
+ queue_count++;
+}
+
+Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+
+ int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0;
+
+ /* clang-format off */
+ EM_ASM({
+ var sock = Module.IDHandler.get($0);
+ var bytes_array = new Uint8Array($2);
+ var i = 0;
+
+ for(i=0; i<$2; i++) {
+ bytes_array[i] = getValue($1+i, 'i8');
+ }
+
+ if ($3) {
+ sock.send(bytes_array.buffer);
+ } else {
+ var string = new TextDecoder("utf-8").decode(bytes_array);
+ sock.send(string);
+ }
+ }, peer_sock, p_buffer, p_buffer_size, is_bin);
+ /* clang-format on */
+
+ return OK;
+};
+
+Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+
+ if (queue_count == 0)
+ return ERR_UNAVAILABLE;
+
+ uint32_t to_read = 0;
+ uint32_t left = 0;
+ uint8_t is_string = 0;
+ r_buffer_size = 0;
+
+ in_buffer.read((uint8_t *)&to_read, 4);
+ --queue_count;
+ left = in_buffer.data_left();
+
+ if (left < to_read + 1) {
+ in_buffer.advance_read(left);
+ return FAILED;
+ }
+
+ in_buffer.read(&is_string, 1);
+ _was_string = is_string == 1;
+ in_buffer.read(packet_buffer, to_read);
+ *r_buffer = packet_buffer;
+ r_buffer_size = to_read;
+
+ return OK;
+};
+
+int EMWSPeer::get_available_packet_count() const {
+
+ return queue_count;
+};
+
+bool EMWSPeer::was_string_packet() const {
+
+ return _was_string;
+};
+
+bool EMWSPeer::is_connected_to_host() const {
+
+ return peer_sock != -1;
+};
+
+void EMWSPeer::close() {
+
+ if (peer_sock != -1) {
+ /* clang-format off */
+ EM_ASM({
+ var sock = Module.IDHandler.get($0);
+ sock.close();
+ Module.IDHandler.remove($0);
+ }, peer_sock);
+ /* clang-format on */
+ }
+ peer_sock = -1;
+ queue_count = 0;
+ in_buffer.clear();
+};
+
+IP_Address EMWSPeer::get_connected_host() const {
+
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(IP_Address());
+};
+
+uint16_t EMWSPeer::get_connected_port() const {
+
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(0);
+};
+
+EMWSPeer::EMWSPeer() {
+ peer_sock = -1;
+ queue_count = 0;
+ _was_string = false;
+ in_buffer.resize(16);
+ write_mode = WRITE_MODE_BINARY;
+};
+
+EMWSPeer::~EMWSPeer() {
+
+ in_buffer.resize(0);
+ close();
+};
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
new file mode 100644
index 0000000000..e06f725265
--- /dev/null
+++ b/modules/websocket/emws_peer.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* emws_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 EMWSPEER_H
+#define EMWSPEER_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "core/io/packet_peer.h"
+#include "core/ring_buffer.h"
+#include "emscripten.h"
+#include "websocket_peer.h"
+
+class EMWSPeer : public WebSocketPeer {
+
+ GDCIIMPL(EMWSPeer, WebSocketPeer);
+
+private:
+ enum {
+ PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
+ };
+
+ int peer_sock;
+ WriteMode write_mode;
+
+ uint8_t packet_buffer[PACKET_BUFFER_SIZE];
+ RingBuffer<uint8_t> in_buffer;
+ int queue_count;
+ bool _was_string;
+
+public:
+ void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ void set_sock(int sock);
+ virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+
+ virtual void close();
+ virtual bool is_connected_to_host() const;
+ virtual IP_Address get_connected_host() const;
+ virtual uint16_t get_connected_port() const;
+
+ virtual WriteMode get_write_mode() const;
+ virtual void set_write_mode(WriteMode p_mode);
+ virtual bool was_string_packet() const;
+
+ void set_wsi(struct lws *wsi);
+ Error read_wsi(void *in, size_t len);
+ Error write_wsi();
+
+ EMWSPeer();
+ ~EMWSPeer();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // LSWPEER_H
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
new file mode 100644
index 0000000000..3eb93e4152
--- /dev/null
+++ b/modules/websocket/emws_server.cpp
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* emws_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifdef JAVASCRIPT_ENABLED
+
+#include "emws_server.h"
+#include "core/os/os.h"
+
+Error EMWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
+
+ return FAILED;
+}
+
+bool EMWSServer::is_listening() const {
+ return false;
+}
+
+void EMWSServer::stop() {
+}
+
+bool EMWSServer::has_peer(int p_id) const {
+ return false;
+}
+
+Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const {
+ return NULL;
+}
+
+PoolVector<String> EMWSServer::get_protocols() const {
+ PoolVector<String> out;
+
+ return out;
+}
+
+IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
+
+ return IP_Address();
+}
+
+int EMWSServer::get_peer_port(int p_peer_id) const {
+
+ return 0;
+}
+
+void EMWSServer::disconnect_peer(int p_peer_id) {
+}
+
+EMWSServer::EMWSServer() {
+}
+
+EMWSServer::~EMWSServer() {
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
new file mode 100644
index 0000000000..9ec4ce72c8
--- /dev/null
+++ b/modules/websocket/emws_server.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* emws_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 EMWSSERVER_H
+#define EMWSSERVER_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/reference.h"
+#include "emws_peer.h"
+#include "websocket_server.h"
+
+class EMWSServer : public WebSocketServer {
+
+ GDCIIMPL(EMWSServer, WebSocketServer);
+
+public:
+ Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+ void stop();
+ bool is_listening() const;
+ bool has_peer(int p_id) const;
+ Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
+ virtual void poll();
+ virtual PoolVector<String> get_protocols() const;
+
+ EMWSServer();
+ ~EMWSServer();
+};
+
+#endif
+
+#endif // LWSSERVER_H
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
new file mode 100644
index 0000000000..06f97aaf05
--- /dev/null
+++ b/modules/websocket/lws_client.cpp
@@ -0,0 +1,222 @@
+/*************************************************************************/
+/* lws_client.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef JAVASCRIPT_ENABLED
+
+#include "lws_client.h"
+#include "core/io/ip.h"
+#include "core/io/stream_peer_ssl.h"
+#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
+
+Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
+
+ ERR_FAIL_COND_V(context != NULL, FAILED);
+
+ IP_Address addr;
+
+ if (!p_host.is_valid_ip_address()) {
+ addr = IP::get_singleton()->resolve_hostname(p_host);
+ } else {
+ addr = p_host;
+ }
+
+ ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
+
+ // prepare protocols
+ if (p_protocols.size() == 0) // default to binary protocol
+ p_protocols.append("binary");
+ _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref);
+
+ // init lws client
+ struct lws_context_creation_info info;
+ struct lws_client_connect_info i;
+
+ memset(&i, 0, sizeof i);
+ memset(&info, 0, sizeof info);
+
+ info.port = CONTEXT_PORT_NO_LISTEN;
+ info.protocols = _lws_ref->lws_structs;
+ info.gid = -1;
+ info.uid = -1;
+ //info.ws_ping_pong_interval = 5;
+ info.user = _lws_ref;
+#if defined(LWS_OPENSSL_SUPPORT)
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+#endif
+ context = lws_create_context(&info);
+
+ if (context == NULL) {
+ _lws_free_ref(_lws_ref);
+ _lws_ref = NULL;
+ ERR_EXPLAIN("Unable to create lws context");
+ ERR_FAIL_V(FAILED);
+ }
+
+ char abuf[1024];
+ char hbuf[1024];
+ char pbuf[2048];
+ String addr_str = (String)addr;
+ strncpy(abuf, addr_str.ascii().get_data(), 1024);
+ strncpy(hbuf, p_host.utf8().get_data(), 1024);
+ strncpy(pbuf, p_path.utf8().get_data(), 2048);
+
+ i.context = context;
+ i.protocol = _lws_ref->lws_names;
+ i.address = abuf;
+ i.host = hbuf;
+ i.path = pbuf;
+ i.port = p_port;
+
+ if (p_ssl) {
+ i.ssl_connection = LCCSCF_USE_SSL;
+ if (!verify_ssl)
+ i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
+ } else {
+ i.ssl_connection = 0;
+ }
+
+ lws_client_connect_via_info(&i);
+ return OK;
+};
+
+void LWSClient::poll() {
+
+ _lws_poll();
+}
+
+int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
+
+ Ref<LWSPeer> peer = static_cast<Ref<LWSPeer> >(_peer);
+ LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
+
+ switch (reason) {
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: {
+ PoolByteArray arr = StreamPeerSSL::get_project_cert_array();
+ if (arr.size() > 0)
+ SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size()));
+ else if (verify_ssl)
+ WARN_PRINTS("No CA cert specified in project settings, SSL will not work");
+ } break;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ peer->set_wsi(wsi);
+ peer_data->peer_id = 0;
+ peer_data->in_size = 0;
+ peer_data->in_count = 0;
+ peer_data->out_count = 0;
+ peer_data->rbw.resize(16);
+ peer_data->rbr.resize(16);
+ peer_data->force_close = false;
+ _on_connect(lws_get_protocol(wsi)->name);
+ break;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ _on_error();
+ destroy_context();
+ return -1; // we should close the connection (would probably happen anyway)
+
+ case LWS_CALLBACK_CLIENT_CLOSED:
+ peer_data->in_count = 0;
+ peer_data->out_count = 0;
+ peer_data->rbw.resize(0);
+ peer_data->rbr.resize(0);
+ peer->close();
+ destroy_context();
+ _on_disconnect();
+ return 0; // we can end here
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ peer->read_wsi(in, len);
+ if (peer->get_available_packet_count() > 0)
+ _on_peer_packet();
+ break;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ if (peer_data->force_close)
+ return -1;
+
+ peer->write_wsi();
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+Ref<WebSocketPeer> LWSClient::get_peer(int p_peer_id) const {
+
+ return _peer;
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() const {
+
+ if (context == NULL)
+ return CONNECTION_DISCONNECTED;
+
+ if (_peer->is_connected_to_host())
+ return CONNECTION_CONNECTED;
+
+ return CONNECTION_CONNECTING;
+}
+
+void LWSClient::disconnect_from_host() {
+
+ if (context == NULL)
+ return;
+
+ _peer->close();
+ destroy_context();
+};
+
+IP_Address LWSClient::get_connected_host() const {
+
+ return IP_Address();
+};
+
+uint16_t LWSClient::get_connected_port() const {
+
+ return 1025;
+};
+
+LWSClient::LWSClient() {
+ context = NULL;
+ _lws_ref = NULL;
+ _peer = Ref<LWSPeer>(memnew(LWSPeer));
+};
+
+LWSClient::~LWSClient() {
+
+ invalidate_lws_ref(); // We do not want any more callback
+ disconnect_from_host();
+ _peer = Ref<LWSPeer>();
+};
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h
new file mode 100644
index 0000000000..8850683cb5
--- /dev/null
+++ b/modules/websocket/lws_client.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* lws_client.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 LWSCLIENT_H
+#define LWSCLIENT_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "lws_helper.h"
+#include "lws_peer.h"
+#include "websocket_client.h"
+
+class LWSClient : public WebSocketClient {
+
+ GDCIIMPL(LWSClient, WebSocketClient);
+
+ LWS_HELPER(LWSClient);
+
+public:
+ Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
+ Ref<WebSocketPeer> get_peer(int p_peer_id) const;
+ void disconnect_from_host();
+ IP_Address get_connected_host() const;
+ uint16_t get_connected_port() const;
+ virtual ConnectionStatus get_connection_status() const;
+ virtual void poll();
+
+ LWSClient();
+ ~LWSClient();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // LWSCLIENT_H
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
new file mode 100644
index 0000000000..a4920c3d54
--- /dev/null
+++ b/modules/websocket/lws_helper.h
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* lws_helper.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef LWS_HELPER_H
+#define LWS_HELPER_H
+
+#define LWS_BUF_SIZE 65536
+#define LWS_PACKET_SIZE LWS_BUF_SIZE
+
+#include "core/io/stream_peer.h"
+#include "core/os/os.h"
+#include "core/reference.h"
+#include "core/ring_buffer.h"
+#include "lws_peer.h"
+
+struct _LWSRef {
+ bool free_context;
+ bool is_polling;
+ bool is_valid;
+ bool is_destroying;
+ void *obj;
+ struct lws_protocols *lws_structs;
+ char *lws_names;
+};
+
+static _LWSRef *_lws_create_ref(void *obj) {
+
+ _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef));
+ out->is_destroying = false;
+ out->free_context = false;
+ out->is_polling = false;
+ out->obj = obj;
+ out->is_valid = true;
+ out->lws_structs = NULL;
+ out->lws_names = NULL;
+ return out;
+}
+
+static void _lws_free_ref(_LWSRef *ref) {
+ // Free strings and structs
+ memfree(ref->lws_structs);
+ memfree(ref->lws_names);
+ // Free ref
+ memfree(ref);
+}
+
+static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) {
+ if (context == NULL || ref->is_destroying)
+ return false;
+
+ if (ref->is_polling) {
+ ref->free_context = true;
+ return false;
+ }
+
+ ref->is_destroying = true;
+ lws_context_destroy(context);
+ _lws_free_ref(ref);
+ return true;
+}
+
+static bool _lws_poll(struct lws_context *context, _LWSRef *ref) {
+
+ ERR_FAIL_COND_V(context == NULL, false);
+ ERR_FAIL_COND_V(ref == NULL, false);
+
+ ref->is_polling = true;
+ lws_service(context, 0);
+ ref->is_polling = false;
+
+ if (!ref->free_context)
+ return false; // Nothing to do
+
+ bool is_valid = ref->is_valid; // Might have been destroyed by poll
+
+ _lws_destroy(context, ref); // Will destroy context and ref
+
+ return is_valid; // If the object should NULL its context and ref
+}
+
+/*
+ * prepare the protocol_structs to be fed to context
+ * also prepare the protocol string used by the client
+ */
+static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) {
+ /* the input strings might go away after this call,
+ * we need to copy them. Will clear them when
+ * destroying the context */
+ int i;
+ int len = p_names.size();
+ size_t data_size = sizeof(struct LWSPeer::PeerData);
+ PoolVector<String>::Read pnr = p_names.read();
+
+ /*
+ * This is a reference connecting the object with lws
+ * keep track of status, mallocs, etc.
+ * Must survive as long the context
+ * Must be freed manually when context creation fails.
+ */
+ _LWSRef *ref = _lws_create_ref(p_obj);
+
+ /* LWS protocol structs */
+ ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2));
+ memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2));
+
+ CharString strings = p_names.join(",").ascii();
+ int str_len = strings.length();
+
+ /* Joined string of protocols, double the size: comma separated first, NULL separated last */
+ ref->lws_names = (char *)memalloc((str_len + 1) * 2); /* plus the terminator */
+
+ char *names_ptr = ref->lws_names;
+ struct lws_protocols *structs_ptr = ref->lws_structs;
+
+ copymem(names_ptr, strings.get_data(), str_len);
+ names_ptr[str_len] = '\0'; /* NULL terminator */
+ /* NULL terminated strings to be used in protocol structs */
+ copymem(&names_ptr[str_len + 1], strings.get_data(), str_len);
+ names_ptr[(str_len * 2) + 1] = '\0'; /* NULL terminator */
+ int pos = str_len + 1;
+
+ /* the first protocol is always http-only */
+ 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 = LWS_BUF_SIZE;
+ structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE;
+ /* add user defined protocols */
+ for (i = 0; i < len; i++) {
+ structs_ptr[i + 1].name = (const char *)&names_ptr[pos];
+ structs_ptr[i + 1].callback = p_callback;
+ structs_ptr[i + 1].per_session_data_size = data_size;
+ structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE;
+ pos += pnr[i].ascii().length() + 1;
+ names_ptr[pos - 1] = '\0';
+ }
+ /* add protocols terminator */
+ structs_ptr[len + 1].name = NULL;
+ structs_ptr[len + 1].callback = NULL;
+ structs_ptr[len + 1].per_session_data_size = 0;
+ structs_ptr[len + 1].rx_buffer_size = 0;
+
+ *r_lws_ref = ref;
+}
+
+/* clang-format off */
+#define LWS_HELPER(CNAME) \
+protected: \
+ struct _LWSRef *_lws_ref; \
+ struct lws_context *context; \
+ \
+ static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \
+ \
+ if (wsi == NULL) { \
+ return 0; \
+ } \
+ \
+ struct _LWSRef *ref = (struct _LWSRef *)lws_context_user(lws_get_context(wsi)); \
+ if (!ref->is_valid) \
+ return 0; \
+ CNAME *helper = (CNAME *)ref->obj; \
+ return helper->_handle_cb(wsi, reason, user, in, len); \
+ } \
+ \
+ void invalidate_lws_ref() { \
+ if (_lws_ref != NULL) \
+ _lws_ref->is_valid = false; \
+ } \
+ \
+ void destroy_context() { \
+ if (_lws_destroy(context, _lws_ref)) { \
+ context = NULL; \
+ _lws_ref = NULL; \
+ } \
+ } \
+ \
+public: \
+ virtual int _handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); \
+ \
+ void _lws_poll() { \
+ ERR_FAIL_COND(context == NULL); \
+ \
+ if (::_lws_poll(context, _lws_ref)) { \
+ context = NULL; \
+ _lws_ref = NULL; \
+ } \
+ } \
+ \
+protected:
+
+ /* clang-format on */
+
+#endif // LWS_HELPER_H
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
new file mode 100644
index 0000000000..96acb99cc4
--- /dev/null
+++ b/modules/websocket/lws_peer.cpp
@@ -0,0 +1,240 @@
+/*************************************************************************/
+/* lws_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef JAVASCRIPT_ENABLED
+
+#include "lws_peer.h"
+#include "core/io/ip.h"
+
+// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows
+#if !defined(WINDOWS_ENABLED)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+
+#include "drivers/unix/socket_helpers.h"
+
+void LWSPeer::set_wsi(struct lws *p_wsi) {
+ wsi = p_wsi;
+};
+
+void LWSPeer::set_write_mode(WriteMode p_mode) {
+ write_mode = p_mode;
+}
+
+LWSPeer::WriteMode LWSPeer::get_write_mode() const {
+ return write_mode;
+}
+
+Error LWSPeer::read_wsi(void *in, size_t len) {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
+ uint32_t size = peer_data->in_size;
+ uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
+
+ if (peer_data->rbr.space_left() < len + 5) {
+ ERR_EXPLAIN("Buffer full! Dropping data");
+ ERR_FAIL_V(FAILED);
+ }
+
+ copymem(&(peer_data->input_buffer[size]), in, len);
+ size += len;
+
+ peer_data->in_size = size;
+ if (lws_is_final_fragment(wsi)) {
+ peer_data->rbr.write((uint8_t *)&size, 4);
+ peer_data->rbr.write((uint8_t *)&is_string, 1);
+ peer_data->rbr.write(peer_data->input_buffer, size);
+ peer_data->in_count++;
+ peer_data->in_size = 0;
+ }
+
+ return OK;
+}
+
+Error LWSPeer::write_wsi() {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
+ PoolVector<uint8_t> tmp;
+ int left = peer_data->rbw.data_left();
+ uint32_t to_write = 0;
+
+ if (left == 0 || peer_data->out_count == 0)
+ return OK;
+
+ peer_data->rbw.read((uint8_t *)&to_write, 4);
+ peer_data->out_count--;
+
+ if (left < to_write) {
+ peer_data->rbw.advance_read(left);
+ return FAILED;
+ }
+
+ tmp.resize(LWS_PRE + to_write);
+ peer_data->rbw.read(&(tmp.write()[LWS_PRE]), to_write);
+ lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode);
+ tmp.resize(0);
+
+ if (peer_data->out_count > 0)
+ lws_callback_on_writable(wsi); // we want to write more!
+
+ return OK;
+}
+
+Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
+ peer_data->rbw.write((uint8_t *)&p_buffer_size, 4);
+ peer_data->rbw.write(p_buffer, MIN(p_buffer_size, peer_data->rbw.space_left()));
+ peer_data->out_count++;
+
+ lws_callback_on_writable(wsi); // notify that we want to write
+ return OK;
+};
+
+Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
+
+ if (peer_data->in_count == 0)
+ return ERR_UNAVAILABLE;
+
+ uint32_t to_read = 0;
+ uint32_t left = 0;
+ uint8_t is_string = 0;
+ r_buffer_size = 0;
+
+ peer_data->rbr.read((uint8_t *)&to_read, 4);
+ peer_data->in_count--;
+ left = peer_data->rbr.data_left();
+
+ if (left < to_read + 1) {
+ peer_data->rbr.advance_read(left);
+ return FAILED;
+ }
+
+ peer_data->rbr.read(&is_string, 1);
+ peer_data->rbr.read(packet_buffer, to_read);
+ *r_buffer = packet_buffer;
+ r_buffer_size = to_read;
+ _was_string = is_string;
+
+ return OK;
+};
+
+int LWSPeer::get_available_packet_count() const {
+
+ if (!is_connected_to_host())
+ return 0;
+
+ return ((PeerData *)lws_wsi_user(wsi))->in_count;
+};
+
+bool LWSPeer::was_string_packet() const {
+
+ return _was_string;
+};
+
+bool LWSPeer::is_connected_to_host() const {
+
+ return wsi != NULL;
+};
+
+void LWSPeer::close() {
+ if (wsi != NULL) {
+ struct lws *tmp = wsi;
+ PeerData *data = ((PeerData *)lws_wsi_user(wsi));
+ data->force_close = true;
+ wsi = NULL;
+ lws_callback_on_writable(tmp); // notify that we want to disconnect
+ }
+};
+
+IP_Address LWSPeer::get_connected_host() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ int port = 0;
+
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+
+ int fd = lws_get_socket_fd(wsi);
+ ERR_FAIL_COND_V(fd == -1, IP_Address());
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, IP_Address());
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return ip;
+};
+
+uint16_t LWSPeer::get_connected_port() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ IP_Address ip;
+ int port = 0;
+
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+
+ int fd = lws_get_socket_fd(wsi);
+ ERR_FAIL_COND_V(fd == -1, 0);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, 0);
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return port;
+};
+
+LWSPeer::LWSPeer() {
+ wsi = NULL;
+ _was_string = false;
+ write_mode = WRITE_MODE_BINARY;
+};
+
+LWSPeer::~LWSPeer() {
+
+ close();
+};
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h
new file mode 100644
index 0000000000..e96b38b168
--- /dev/null
+++ b/modules/websocket/lws_peer.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* lws_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 LWSPEER_H
+#define LWSPEER_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "core/io/packet_peer.h"
+#include "core/ring_buffer.h"
+#include "libwebsockets.h"
+#include "lws_config.h"
+#include "websocket_peer.h"
+
+class LWSPeer : public WebSocketPeer {
+
+ GDCIIMPL(LWSPeer, WebSocketPeer);
+
+private:
+ enum {
+ PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type
+ };
+
+ uint8_t packet_buffer[PACKET_BUFFER_SIZE];
+ struct lws *wsi;
+ WriteMode write_mode;
+ bool _was_string;
+
+public:
+ struct PeerData {
+ uint32_t peer_id;
+ bool force_close;
+ RingBuffer<uint8_t> rbw;
+ RingBuffer<uint8_t> rbr;
+ mutable uint8_t input_buffer[PACKET_BUFFER_SIZE];
+ uint32_t in_size;
+ int in_count;
+ int out_count;
+ };
+
+ virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+
+ virtual void close();
+ virtual bool is_connected_to_host() const;
+ virtual IP_Address get_connected_host() const;
+ virtual uint16_t get_connected_port() const;
+
+ virtual WriteMode get_write_mode() const;
+ virtual void set_write_mode(WriteMode p_mode);
+ virtual bool was_string_packet() const;
+
+ void set_wsi(struct lws *wsi);
+ Error read_wsi(void *in, size_t len);
+ Error write_wsi();
+
+ LWSPeer();
+ ~LWSPeer();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // LSWPEER_H
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
new file mode 100644
index 0000000000..8d13dc7a98
--- /dev/null
+++ b/modules/websocket/lws_server.cpp
@@ -0,0 +1,195 @@
+/*************************************************************************/
+/* lws_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef JAVASCRIPT_ENABLED
+
+#include "lws_server.h"
+#include "core/os/os.h"
+
+Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
+
+ ERR_FAIL_COND_V(context != NULL, FAILED);
+
+ _is_multiplayer = gd_mp_api;
+
+ struct lws_context_creation_info info;
+ memset(&info, 0, sizeof info);
+
+ if (p_protocols.size() == 0) // default to binary protocol
+ p_protocols.append(String("binary"));
+
+ // Prepare lws protocol structs
+ _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref);
+
+ info.port = p_port;
+ info.user = _lws_ref;
+ info.protocols = _lws_ref->lws_structs;
+ info.gid = -1;
+ info.uid = -1;
+ //info.ws_ping_pong_interval = 5;
+
+ context = lws_create_context(&info);
+
+ if (context == NULL) {
+ _lws_free_ref(_lws_ref);
+ _lws_ref = NULL;
+ ERR_EXPLAIN("Unable to create LWS context");
+ ERR_FAIL_V(FAILED);
+ }
+
+ return OK;
+}
+
+bool LWSServer::is_listening() const {
+ return context != NULL;
+}
+
+int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
+
+ LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
+
+ switch (reason) {
+ case LWS_CALLBACK_HTTP:
+ // no http for now
+ // closing immediately returning -1;
+ return -1;
+
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ // check header here?
+ break;
+
+ case LWS_CALLBACK_ESTABLISHED: {
+ int32_t id = _gen_unique_id();
+
+ Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer));
+ peer->set_wsi(wsi);
+ _peer_map[id] = peer;
+
+ peer_data->peer_id = id;
+ peer_data->in_size = 0;
+ peer_data->in_count = 0;
+ peer_data->out_count = 0;
+ peer_data->rbw.resize(16);
+ peer_data->rbr.resize(16);
+ peer_data->force_close = false;
+
+ _on_connect(id, lws_get_protocol(wsi)->name);
+ break;
+ }
+
+ case LWS_CALLBACK_CLOSED: {
+ if (peer_data == NULL)
+ return 0;
+ int32_t id = peer_data->peer_id;
+ if (_peer_map.has(id)) {
+ _peer_map[id]->close();
+ _peer_map.erase(id);
+ }
+ peer_data->in_count = 0;
+ peer_data->out_count = 0;
+ peer_data->rbr.resize(0);
+ peer_data->rbw.resize(0);
+ _on_disconnect(id);
+ return 0; // we can end here
+ }
+
+ case LWS_CALLBACK_RECEIVE: {
+ int32_t id = peer_data->peer_id;
+ if (_peer_map.has(id)) {
+ static_cast<Ref<LWSPeer> >(_peer_map[id])->read_wsi(in, len);
+ if (_peer_map[id]->get_available_packet_count() > 0)
+ _on_peer_packet(id);
+ }
+ break;
+ }
+
+ case LWS_CALLBACK_SERVER_WRITEABLE: {
+ if (peer_data->force_close)
+ return -1;
+
+ int id = peer_data->peer_id;
+ if (_peer_map.has(id))
+ static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi();
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void LWSServer::stop() {
+ if (context == NULL)
+ return;
+
+ _peer_map.clear();
+ destroy_context();
+ context = NULL;
+}
+
+bool LWSServer::has_peer(int p_id) const {
+ return _peer_map.has(p_id);
+}
+
+Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_id), NULL);
+ return _peer_map[p_id];
+}
+
+IP_Address LWSServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int LWSServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void LWSServer::disconnect_peer(int p_peer_id) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close();
+}
+
+LWSServer::LWSServer() {
+ context = NULL;
+ _lws_ref = NULL;
+}
+
+LWSServer::~LWSServer() {
+ invalidate_lws_ref(); // we do not want any more callbacks
+ stop();
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
new file mode 100644
index 0000000000..9e3fb9b775
--- /dev/null
+++ b/modules/websocket/lws_server.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* lws_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 LWSSERVER_H
+#define LWSSERVER_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "core/reference.h"
+#include "lws_helper.h"
+#include "lws_peer.h"
+#include "websocket_server.h"
+
+class LWSServer : public WebSocketServer {
+
+ GDCIIMPL(LWSServer, WebSocketServer);
+
+ LWS_HELPER(LWSServer);
+
+private:
+ Map<int, Ref<LWSPeer> > peer_map;
+
+public:
+ Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+ void stop();
+ bool is_listening() const;
+ bool has_peer(int p_id) const;
+ Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
+ virtual void poll() { _lws_poll(); }
+
+ LWSServer();
+ ~LWSServer();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // LWSSERVER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
new file mode 100644
index 0000000000..721f3cc330
--- /dev/null
+++ b/modules/websocket/register_types.cpp
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* 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"
+#ifdef JAVASCRIPT_ENABLED
+#include "emscripten.h"
+#include "emws_client.h"
+#include "emws_peer.h"
+#include "emws_server.h"
+#else
+#include "lws_client.h"
+#include "lws_peer.h"
+#include "lws_server.h"
+#endif
+
+void register_websocket_types() {
+#ifdef JAVASCRIPT_ENABLED
+ EM_ASM({
+ var IDHandler = {};
+ IDHandler["ids"] = {};
+ IDHandler["has"] = function(id) {
+ return IDHandler.ids.hasOwnProperty(id);
+ };
+ IDHandler["add"] = function(obj) {
+ var id = crypto.getRandomValues(new Int32Array(32))[0];
+ IDHandler.ids[id] = obj;
+ return id;
+ };
+ IDHandler["get"] = function(id) {
+ return IDHandler.ids[id];
+ };
+ IDHandler["remove"] = function(id) {
+ delete IDHandler.ids[id];
+ };
+ Module["IDHandler"] = IDHandler;
+ });
+ EMWSPeer::make_default();
+ EMWSClient::make_default();
+ EMWSServer::make_default();
+#else
+ LWSPeer::make_default();
+ LWSClient::make_default();
+ LWSServer::make_default();
+#endif
+
+ ClassDB::register_virtual_class<WebSocketMultiplayerPeer>();
+ ClassDB::register_custom_instance_class<WebSocketServer>();
+ ClassDB::register_custom_instance_class<WebSocketClient>();
+ ClassDB::register_custom_instance_class<WebSocketPeer>();
+}
+
+void unregister_websocket_types() {}
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
new file mode 100644
index 0000000000..89ce93e286
--- /dev/null
+++ b/modules/websocket/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* 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_websocket_types();
+void unregister_websocket_types();
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
new file mode 100644
index 0000000000..7701163085
--- /dev/null
+++ b/modules/websocket/websocket_client.cpp
@@ -0,0 +1,140 @@
+/*************************************************************************/
+/* websocket_client.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "websocket_client.h"
+
+GDCINULL(WebSocketClient);
+
+WebSocketClient::WebSocketClient() {
+
+ verify_ssl = true;
+}
+
+WebSocketClient::~WebSocketClient() {
+}
+
+Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) {
+ _is_multiplayer = gd_mp_api;
+
+ String host = p_url;
+ String path = "/";
+ int p_len = -1;
+ int port = 80;
+ bool ssl = false;
+ if (host.begins_with("wss://")) {
+ ssl = true; // we should implement this
+ host = host.substr(6, host.length() - 6);
+ port = 443;
+ } else {
+ ssl = false;
+ if (host.begins_with("ws://"))
+ host = host.substr(5, host.length() - 5);
+ }
+
+ // Path
+ p_len = host.find("/");
+ if (p_len != -1) {
+ path = host.substr(p_len, host.length() - p_len);
+ host = host.substr(0, p_len);
+ }
+
+ // Port
+ p_len = host.find_last(":");
+ if (p_len != -1 && p_len == host.find(":")) {
+ port = host.substr(p_len, host.length() - p_len).to_int();
+ host = host.substr(0, p_len);
+ }
+
+ return connect_to_host(host, path, port, ssl, p_protocols);
+}
+
+void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) {
+
+ verify_ssl = p_verify_ssl;
+}
+
+bool WebSocketClient::is_verify_ssl_enabled() const {
+
+ return verify_ssl;
+}
+
+bool WebSocketClient::is_server() const {
+
+ return false;
+}
+
+void WebSocketClient::_on_peer_packet() {
+
+ if (_is_multiplayer) {
+ _process_multiplayer(get_peer(1), 1);
+ } else {
+ emit_signal("data_received");
+ }
+}
+
+void WebSocketClient::_on_connect(String p_protocol) {
+
+ if (_is_multiplayer) {
+ // need to wait for ID confirmation...
+ } else {
+ emit_signal("connection_established", p_protocol);
+ }
+}
+
+void WebSocketClient::_on_disconnect() {
+
+ if (_is_multiplayer) {
+ emit_signal("connection_failed");
+ } else {
+ emit_signal("connection_closed");
+ }
+}
+
+void WebSocketClient::_on_error() {
+
+ if (_is_multiplayer) {
+ emit_signal("connection_failed");
+ } else {
+ emit_signal("connection_error");
+ }
+}
+
+void WebSocketClient::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host);
+ ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
+ ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
+
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
+
+ ADD_SIGNAL(MethodInfo("data_received"));
+ ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
+ ADD_SIGNAL(MethodInfo("connection_closed"));
+ ADD_SIGNAL(MethodInfo("connection_error"));
+}
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
new file mode 100644
index 0000000000..6165f37d40
--- /dev/null
+++ b/modules/websocket/websocket_client.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* websocket_client.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBSOCKET_CLIENT_H
+#define WEBSOCKET_CLIENT_H
+
+#include "core/error_list.h"
+#include "websocket_multiplayer.h"
+#include "websocket_peer.h"
+
+class WebSocketClient : public WebSocketMultiplayerPeer {
+
+ GDCLASS(WebSocketClient, WebSocketMultiplayerPeer);
+ GDCICLASS(WebSocketClient);
+
+protected:
+ Ref<WebSocketPeer> _peer;
+ bool verify_ssl;
+
+ static void _bind_methods();
+
+public:
+ Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+
+ void set_verify_ssl_enabled(bool p_verify_ssl);
+ bool is_verify_ssl_enabled() const;
+
+ virtual void poll() = 0;
+ virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0;
+ virtual void disconnect_from_host() = 0;
+ virtual IP_Address get_connected_host() const = 0;
+ virtual uint16_t get_connected_port() const = 0;
+
+ virtual bool is_server() const;
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ void _on_peer_packet();
+ void _on_connect(String p_protocol);
+ void _on_disconnect();
+ void _on_error();
+
+ WebSocketClient();
+ ~WebSocketClient();
+};
+
+#endif // WEBSOCKET_CLIENT_H
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
new file mode 100644
index 0000000000..d27fb4d778
--- /dev/null
+++ b/modules/websocket/websocket_macros.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* websocket_macros.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBSOCKETMACTOS_H
+#define WEBSOCKETMACTOS_H
+
+/* clang-format off */
+#define GDCICLASS(CNAME) \
+public:\
+ static CNAME *(*_create)();\
+\
+ static Ref<CNAME > create_ref() {\
+\
+ if (!_create)\
+ return Ref<CNAME >();\
+ return Ref<CNAME >(_create());\
+ }\
+\
+ static CNAME *create() {\
+\
+ if (!_create)\
+ return NULL;\
+ return _create();\
+ }\
+protected:\
+
+#define GDCINULL(CNAME) \
+CNAME *(*CNAME::_create)() = NULL;
+
+#define GDCIIMPL(IMPNAME, CNAME) \
+public:\
+ static CNAME *_create() { return memnew(IMPNAME); }\
+ static void make_default() { CNAME::_create = IMPNAME::_create; }\
+protected:\
+/* clang-format on */
+
+#endif // WEBSOCKETMACTOS_H
diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp
new file mode 100644
index 0000000000..b948c439df
--- /dev/null
+++ b/modules/websocket/websocket_multiplayer.cpp
@@ -0,0 +1,361 @@
+/*************************************************************************/
+/* websocket_multiplayer.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 "websocket_multiplayer.h"
+#include "core/os/os.h"
+
+WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() {
+
+ _is_multiplayer = false;
+ _peer_id = 0;
+ _target_peer = 0;
+ _refusing = false;
+
+ _current_packet.source = 0;
+ _current_packet.destination = 0;
+ _current_packet.size = 0;
+ _current_packet.data = NULL;
+}
+
+WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() {
+
+ _clear();
+}
+
+int WebSocketMultiplayerPeer::_gen_unique_id() const {
+
+ uint32_t hash = 0;
+
+ while (hash == 0 || hash == 1) {
+
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec());
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash);
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_data_path().hash64(), hash);
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)this), hash); //rely on aslr heap
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack
+ hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
+ }
+
+ return hash;
+}
+void WebSocketMultiplayerPeer::_clear() {
+
+ _peer_map.clear();
+ if (_current_packet.data != NULL)
+ memfree(_current_packet.data);
+
+ for (List<Packet>::Element *E = _incoming_packets.front(); E; E = E->next()) {
+ memfree(E->get().data);
+ E->get().data = NULL;
+ }
+
+ _incoming_packets.clear();
+}
+
+void WebSocketMultiplayerPeer::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer);
+
+ ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source")));
+}
+
+//
+// PacketPeer
+//
+int WebSocketMultiplayerPeer::get_available_packet_count() const {
+
+ ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+
+ return _incoming_packets.size();
+}
+
+int WebSocketMultiplayerPeer::get_max_packet_size() const {
+
+ ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+
+ return MAX_PACKET_SIZE;
+}
+
+Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+
+ r_buffer_size = 0;
+ ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+
+ if (_current_packet.data != NULL) {
+ memfree(_current_packet.data);
+ _current_packet.data = NULL;
+ }
+
+ _current_packet = _incoming_packets.front()->get();
+ _incoming_packets.pop_front();
+
+ *r_buffer = _current_packet.data;
+ r_buffer_size = _current_packet.size;
+
+ return OK;
+}
+
+Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+
+ ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+
+ PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size);
+
+ if (is_server()) {
+ return _server_relay(1, _target_peer, &(buffer.read()[0]), buffer.size());
+ } else {
+ return get_peer(1)->put_packet(&(buffer.read()[0]), buffer.size());
+ }
+}
+
+//
+// NetworkedMultiplayerPeer
+//
+void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
+
+ // Websocket uses TCP, reliable
+}
+
+NetworkedMultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const {
+
+ // Websocket uses TCP, reliable
+ return TRANSFER_MODE_RELIABLE;
+}
+
+void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) {
+
+ _target_peer = p_target_peer;
+}
+
+int WebSocketMultiplayerPeer::get_packet_peer() const {
+
+ ERR_FAIL_COND_V(!_is_multiplayer, 1);
+ ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1);
+
+ return _incoming_packets.front()->get().source;
+}
+
+int WebSocketMultiplayerPeer::get_unique_id() const {
+
+ return _peer_id;
+}
+
+void WebSocketMultiplayerPeer::set_refuse_new_connections(bool p_enable) {
+
+ _refusing = p_enable;
+}
+
+bool WebSocketMultiplayerPeer::is_refusing_new_connections() const {
+
+ return _refusing;
+}
+
+void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) {
+
+ ERR_FAIL_COND(!p_peer.is_valid());
+ ERR_FAIL_COND(!p_peer->is_connected_to_host());
+
+ PoolVector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4);
+ p_peer->put_packet(&(message.read()[0]), message.size());
+}
+
+PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
+
+ PoolVector<uint8_t> out;
+ out.resize(PROTO_SIZE + p_data_size);
+
+ PoolVector<uint8_t>::Write w = out.write();
+ copymem(&w[0], &p_type, 1);
+ copymem(&w[1], &p_from, 4);
+ copymem(&w[5], &p_to, 4);
+ copymem(&w[PROTO_SIZE], p_data, p_data_size);
+
+ return out;
+}
+
+void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
+
+ // First of all, confirm the ID!
+ _send_sys(get_peer(p_peer_id), SYS_ID, p_peer_id);
+
+ // Then send the server peer (which will trigger connection_succeded in client)
+ _send_sys(get_peer(p_peer_id), SYS_ADD, 1);
+
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ uint32_t id = E->key();
+ if (p_peer_id == id)
+ continue; // Skip the newwly added peer (already confirmed)
+
+ // Send new peer to others
+ _send_sys(get_peer(id), SYS_ADD, p_peer_id);
+ // Send others to new peer
+ _send_sys(get_peer(p_peer_id), SYS_ADD, id);
+ }
+}
+
+void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ uint32_t id = E->key();
+ if (p_peer_id != id)
+ _send_sys(get_peer(id), SYS_DEL, p_peer_id);
+ }
+}
+
+void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) {
+ Packet packet;
+ packet.data = (uint8_t *)memalloc(p_data_size);
+ packet.size = p_data_size;
+ packet.source = p_source;
+ packet.destination = p_dest;
+ copymem(packet.data, &p_data[PROTO_SIZE], p_data_size);
+ _incoming_packets.push_back(packet);
+ emit_signal("peer_packet", p_source);
+}
+
+Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) {
+ if (p_to == 1) {
+
+ return OK; // Will not send to self
+
+ } else if (p_to == 0) {
+
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ if (E->key() != p_from)
+ E->get()->put_packet(p_buffer, p_buffer_size);
+ }
+ return OK; // Sent to all but sender
+
+ } else if (p_to < 0) {
+
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ if (E->key() != p_from && E->key() != -p_to)
+ E->get()->put_packet(p_buffer, p_buffer_size);
+ }
+ return OK; // Sent to all but sender and excluded
+
+ } else {
+
+ ERR_FAIL_COND_V(p_to == p_from, FAILED);
+
+ return get_peer(p_to)->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
+ }
+}
+
+void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) {
+
+ ERR_FAIL_COND(!p_peer.is_valid());
+
+ const uint8_t *in_buffer;
+ int size = 0;
+ int data_size = 0;
+
+ Error err = p_peer->get_packet(&in_buffer, size);
+
+ ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(size < PROTO_SIZE);
+
+ data_size = size - PROTO_SIZE;
+
+ uint8_t type = 0;
+ int32_t from = 0;
+ int32_t to = 0;
+ copymem(&type, in_buffer, 1);
+ copymem(&from, &in_buffer[1], 4);
+ copymem(&to, &in_buffer[5], 4);
+
+ if (is_server()) { // Server can resend
+
+ ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
+ ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
+
+ _server_relay(from, to, in_buffer, size); // Relay if needed
+
+ if (to == 1) { // This is for the server
+
+ _store_pkt(from, to, in_buffer, data_size);
+
+ } else if (to == 0) {
+
+ // Broadcast, for us too
+ _store_pkt(from, to, in_buffer, data_size);
+
+ } else if (to < 0) {
+
+ // All but one, for us if not excluded
+ if (_peer_id != -p_peer_id)
+ _store_pkt(from, to, in_buffer, data_size);
+
+ } else {
+
+ // Send to specific peer
+ ERR_FAIL_COND(!_peer_map.has(to));
+ get_peer(to)->put_packet(in_buffer, size);
+ }
+
+ } else {
+
+ if (type == SYS_NONE) { // Payload message
+
+ _store_pkt(from, to, in_buffer, data_size);
+ return;
+ }
+
+ // System message
+ ERR_FAIL_COND(data_size < 4);
+ int id = 0;
+ copymem(&id, &in_buffer[PROTO_SIZE], 4);
+
+ switch (type) {
+
+ case SYS_ADD: // Add peer
+ _peer_map[id] = Ref<WebSocketPeer>();
+ emit_signal("peer_connected", id);
+ if (id == 1) // We just connected to the server
+ emit_signal("connection_succeeded");
+ break;
+
+ case SYS_DEL: // Remove peer
+ _peer_map.erase(id);
+ emit_signal("peer_disconnected", id);
+ break;
+ case SYS_ID: // Helo, server assigned ID
+ _peer_id = id;
+ break;
+ default:
+ ERR_EXPLAIN("Invalid multiplayer message");
+ ERR_FAIL();
+ break;
+ }
+ }
+}
diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h
new file mode 100644
index 0000000000..8edfc5296e
--- /dev/null
+++ b/modules/websocket/websocket_multiplayer.h
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* websocket_multiplayer.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 WEBSOCKET_MULTIPLAYER_PEER_H
+#define WEBSOCKET_MULTIPLAYER_PEER_H
+
+#include "core/error_list.h"
+#include "core/io/networked_multiplayer_peer.h"
+#include "core/list.h"
+#include "websocket_peer.h"
+
+class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer {
+
+ GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer);
+
+private:
+ PoolVector<uint8_t> _make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
+ void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size);
+ Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size);
+
+protected:
+ enum {
+ SYS_NONE = 0,
+ SYS_ADD = 1,
+ SYS_DEL = 2,
+ SYS_ID = 3,
+
+ PROTO_SIZE = 9,
+ SYS_PACKET_SIZE = 13,
+ MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer
+ };
+
+ struct Packet {
+ int source;
+ int destination;
+ uint8_t *data;
+ uint32_t size;
+ };
+
+ List<Packet> _incoming_packets;
+ Map<int, Ref<WebSocketPeer> > _peer_map;
+ Packet _current_packet;
+
+ bool _is_multiplayer;
+ int _target_peer;
+ int _peer_id;
+ int _refusing;
+
+ static void _bind_methods();
+
+ void _send_add(int32_t p_peer_id);
+ void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id);
+ void _send_del(int32_t p_peer_id);
+ int _gen_unique_id() const;
+
+public:
+ /* NetworkedMultiplayerPeer */
+ void set_transfer_mode(TransferMode p_mode);
+ TransferMode get_transfer_mode() const;
+ void set_target_peer(int p_peer_id);
+ int get_packet_peer() const;
+ int get_unique_id() const;
+ virtual bool is_server() const = 0;
+ void set_refuse_new_connections(bool p_enable);
+ bool is_refusing_new_connections() const;
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ /* PacketPeer */
+ virtual int get_available_packet_count() const;
+ virtual int get_max_packet_size() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+
+ /* WebSocketPeer */
+ virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0;
+
+ void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id);
+ void _clear();
+
+ WebSocketMultiplayerPeer();
+ ~WebSocketMultiplayerPeer();
+};
+
+#endif // WEBSOCKET_MULTIPLAYER_PEER_H
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
new file mode 100644
index 0000000000..61f783e377
--- /dev/null
+++ b/modules/websocket/websocket_peer.cpp
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* websocket_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "websocket_peer.h"
+
+GDCINULL(WebSocketPeer);
+
+WebSocketPeer::WebSocketPeer() {
+}
+
+WebSocketPeer::~WebSocketPeer() {
+}
+
+void WebSocketPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_write_mode"), &WebSocketPeer::get_write_mode);
+ ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode);
+ ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host);
+ ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet);
+ ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close);
+ ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
+ ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
+
+ BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
+ BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+}
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
new file mode 100644
index 0000000000..ad451e9cc7
--- /dev/null
+++ b/modules/websocket/websocket_peer.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* websocket_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBSOCKETPEER_H
+#define WEBSOCKETPEER_H
+
+#include "core/error_list.h"
+#include "core/io/packet_peer.h"
+#include "core/ring_buffer.h"
+#include "websocket_macros.h"
+
+class WebSocketPeer : public PacketPeer {
+
+ GDCLASS(WebSocketPeer, PacketPeer);
+ GDCICLASS(WebSocketPeer);
+
+public:
+ enum WriteMode {
+ WRITE_MODE_TEXT,
+ WRITE_MODE_BINARY,
+ };
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_available_packet_count() const = 0;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0;
+ virtual int get_max_packet_size() const = 0;
+
+ virtual WriteMode get_write_mode() const = 0;
+ virtual void set_write_mode(WriteMode p_mode) = 0;
+
+ virtual void close() = 0;
+
+ virtual bool is_connected_to_host() const = 0;
+ virtual IP_Address get_connected_host() const = 0;
+ virtual uint16_t get_connected_port() const = 0;
+ virtual bool was_string_packet() const = 0;
+
+ WebSocketPeer();
+ ~WebSocketPeer();
+};
+
+VARIANT_ENUM_CAST(WebSocketPeer::WriteMode);
+#endif // WEBSOCKETPEER_H
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
new file mode 100644
index 0000000000..53dd7b51b7
--- /dev/null
+++ b/modules/websocket/websocket_server.cpp
@@ -0,0 +1,97 @@
+/*************************************************************************/
+/* websocket_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "websocket_server.h"
+
+GDCINULL(WebSocketServer);
+
+WebSocketServer::WebSocketServer() {
+ _peer_id = 1;
+}
+
+WebSocketServer::~WebSocketServer() {
+}
+
+void WebSocketServer::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening);
+ ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
+ ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port);
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "id"), &WebSocketServer::disconnect_peer);
+
+ ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
+ ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id")));
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
+ if (is_listening())
+ return CONNECTION_CONNECTED;
+
+ return CONNECTION_DISCONNECTED;
+};
+
+bool WebSocketServer::is_server() const {
+
+ return true;
+}
+
+void WebSocketServer::_on_peer_packet(int32_t p_peer_id) {
+
+ if (_is_multiplayer) {
+ _process_multiplayer(get_peer(p_peer_id), p_peer_id);
+ } else {
+ emit_signal("data_received", p_peer_id);
+ }
+}
+
+void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) {
+
+ if (_is_multiplayer) {
+ // Send add to clients
+ _send_add(p_peer_id);
+ emit_signal("peer_connected", p_peer_id);
+ } else {
+ emit_signal("client_connected", p_peer_id, p_protocol);
+ }
+}
+
+void WebSocketServer::_on_disconnect(int32_t p_peer_id) {
+
+ if (_is_multiplayer) {
+ // Send delete to clients
+ _send_del(p_peer_id);
+ emit_signal("peer_disconnected", p_peer_id);
+ } else {
+ emit_signal("client_disconnected", p_peer_id);
+ }
+}
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
new file mode 100644
index 0000000000..64935f8a58
--- /dev/null
+++ b/modules/websocket/websocket_server.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* websocket_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 WEBSOCKET_H
+#define WEBSOCKET_H
+
+#include "core/reference.h"
+#include "websocket_multiplayer.h"
+#include "websocket_peer.h"
+
+class WebSocketServer : public WebSocketMultiplayerPeer {
+
+ GDCLASS(WebSocketServer, WebSocketMultiplayerPeer);
+ GDCICLASS(WebSocketServer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void poll() = 0;
+ virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0;
+ virtual void stop() = 0;
+ virtual bool is_listening() const = 0;
+ virtual bool has_peer(int p_id) const = 0;
+ virtual Ref<WebSocketPeer> get_peer(int p_id) const = 0;
+ virtual bool is_server() const;
+ ConnectionStatus get_connection_status() const;
+
+ virtual IP_Address get_peer_address(int p_peer_id) const = 0;
+ virtual int get_peer_port(int p_peer_id) const = 0;
+ virtual void disconnect_peer(int p_peer_id) = 0;
+
+ void _on_peer_packet(int32_t p_peer_id);
+ void _on_connect(int32_t p_peer_id, String p_protocol);
+ void _on_disconnect(int32_t p_peer_id);
+
+ WebSocketServer();
+ ~WebSocketServer();
+};
+
+#endif // WEBSOCKET_H