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.py5
-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.h32
-rw-r--r--modules/bullet/area_bullet.cpp24
-rw-r--r--modules/bullet/area_bullet.h4
-rw-r--r--modules/bullet/bullet_physics_server.cpp27
-rw-r--r--modules/bullet/collision_object_bullet.cpp10
-rw-r--r--modules/bullet/config.py2
-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_result_callbacks.cpp43
-rw-r--r--modules/bullet/godot_result_callbacks.h12
-rw-r--r--modules/bullet/rigid_body_bullet.cpp8
-rw-r--r--modules/bullet/rigid_body_bullet.h4
-rw-r--r--modules/bullet/shape_bullet.cpp58
-rw-r--r--modules/bullet/shape_bullet.h7
-rw-r--r--modules/bullet/space_bullet.cpp113
-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/SCsub19
-rw-r--r--modules/freetype/config.py2
-rw-r--r--modules/gdnative/SCsub46
-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.xml2
-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.json55
-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/nativescript/godot_nativescript.h12
-rw-r--r--modules/gdnative/include/net/godot_net.h118
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp22
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp128
-rw-r--r--modules/gdnative/nativescript/nativescript.h10
-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.h8
-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.cpp27
-rw-r--r--modules/gdscript/gdscript_compiler.h3
-rw-r--r--modules/gdscript/gdscript_editor.cpp109
-rw-r--r--modules/gdscript/gdscript_function.cpp41
-rw-r--r--modules/gdscript/gdscript_function.h23
-rw-r--r--modules/gdscript/gdscript_functions.cpp62
-rw-r--r--modules/gdscript/gdscript_functions.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp140
-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_editor_plugin.cpp43
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h1
-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/SCsub2
-rwxr-xr-xmodules/mbedtls/config.py2
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp26
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h2
-rw-r--r--modules/mobile_vr/config.py4
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml2
-rw-r--r--modules/mono/SCsub75
-rw-r--r--modules/mono/config.py105
-rw-r--r--modules/mono/csharp_script.cpp131
-rw-r--r--modules/mono/csharp_script.h13
-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.cpp254
-rw-r--r--modules/mono/editor/bindings_generator.h46
-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.cpp10
-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.cpp94
-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/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.cs215
-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.cs3
-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.cpp339
-rw-r--r--modules/mono/mono_gd/gd_mono.h63
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp119
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h17
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp44
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp73
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h3
-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/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.xml2
-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.cpp15
-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.cpp41
-rw-r--r--modules/visual_script/visual_script_editor.h5
-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.cpp221
-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/SCsub7
-rw-r--r--modules/webp/SCsub2
-rw-r--r--modules/webp/config.py2
-rw-r--r--modules/websocket/SCsub85
-rw-r--r--modules/websocket/config.py15
-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.cpp4
-rw-r--r--modules/websocket/emws_client.h4
-rw-r--r--modules/websocket/emws_peer.cpp10
-rw-r--r--modules/websocket/emws_peer.h4
-rw-r--r--modules/websocket/emws_server.cpp17
-rw-r--r--modules/websocket/emws_server.h7
-rw-r--r--modules/websocket/lws_client.cpp27
-rw-r--r--modules/websocket/lws_client.h4
-rw-r--r--modules/websocket/lws_helper.h14
-rw-r--r--modules/websocket/lws_peer.cpp48
-rw-r--r--modules/websocket/lws_peer.h4
-rw-r--r--modules/websocket/lws_server.cpp22
-rw-r--r--modules/websocket/lws_server.h7
-rw-r--r--modules/websocket/register_types.cpp4
-rw-r--r--modules/websocket/register_types.h4
-rw-r--r--modules/websocket/websocket_client.cpp20
-rw-r--r--modules/websocket/websocket_client.h8
-rw-r--r--modules/websocket/websocket_macros.h4
-rw-r--r--modules/websocket/websocket_multiplayer.cpp4
-rw-r--r--modules/websocket/websocket_multiplayer.h4
-rw-r--r--modules/websocket/websocket_peer.cpp6
-rw-r--r--modules/websocket/websocket_peer.h4
-rw-r--r--modules/websocket/websocket_server.cpp7
-rw-r--r--modules/websocket/websocket_server.h8
311 files changed, 13334 insertions, 2377 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/bmp/config.py b/modules/bmp/config.py
new file mode 100644
index 0000000000..1c8cd12a2d
--- /dev/null
+++ b/modules/bmp/config.py
@@ -0,0 +1,5 @@
+def can_build(env, platform):
+ return True
+
+def configure(env):
+ pass
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/bmp/register_types.h b/modules/bmp/register_types.h
new file mode 100644
index 0000000000..d8755db397
--- /dev/null
+++ b/modules/bmp/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_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/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 61ce26e9fd..6246a295ec 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -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;
}
@@ -784,21 +786,26 @@ int BulletPhysicsServer::body_get_max_contacts_reported(RID p_body) const {
}
void BulletPhysicsServer::body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) {
- WARN_PRINT("Not supported by bullet and even Godot");
+ // 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) {
@@ -979,11 +986,11 @@ 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;
}
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/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml
index 8c8647e097..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.1-dev">
+<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 8ed84e1dec..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.1-dev">
+<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_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 7c051f8f17..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 {
@@ -106,7 +110,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
if (gObj->getType() == CollisionObjectBullet::TYPE_AREA)
return false;
- if (m_self_object->has_collision_exception(gObj))
+ if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object))
return false;
}
return true;
@@ -168,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;
@@ -181,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);
@@ -206,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 {
@@ -243,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) {
+ if (m_penetration_distance > depth) { // Has penetration?
- bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
-
- 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 ed6d4b7d6d..363051f24c 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -185,24 +185,18 @@ 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 &pointInWorldOnB, btScalar depth);
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 75b4cc054a..2494063c22 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -255,6 +255,7 @@ RigidBodyBullet::RigidBodyBullet() :
linearDamp(0),
angularDamp(0),
can_sleep(true),
+ omit_forces_integration(false),
force_integration_callback(NULL),
isTransformChanged(false),
previousActiveState(true),
@@ -334,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;
@@ -437,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:
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index 2d529f6dc7..b9511243c7 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -198,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
@@ -254,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;
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 5d8d391bd9..76d9614465 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -125,14 +125,13 @@ 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, bool p_slips_on_slope) {
@@ -337,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();
@@ -387,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 {
@@ -410,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();
@@ -420,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);
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 2acba90e36..abeea0f9ce 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -85,7 +85,7 @@ 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 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);
};
@@ -199,7 +199,8 @@ public:
PoolVector<real_t> heights;
int width;
int depth;
- real_t cell_size;
+ real_t min_height;
+ real_t max_height;
HeightMapShapeBullet();
@@ -209,7 +210,7 @@ public:
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 {
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index e20a79f667..971fd39509 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -122,7 +122,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
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()));
@@ -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()));
@@ -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
@@ -838,23 +841,20 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
#endif
- btTransform body_safe_position;
- G_TO_B(p_from, body_safe_position);
- UNSCALE_BT_BASIS(body_safe_position);
+ btTransform body_transform;
+ G_TO_B(p_from, body_transform);
+ UNSCALE_BT_BASIS(body_transform);
-#if PERFORM_INITIAL_UNSTACK
- btVector3 recover_initial_position(0, 0, 0);
+ btVector3 initial_recover_motion(0, 0, 0);
{ /// Phase one - multi shapes depenetration using margin
for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- if (!recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, recover_initial_position)) {
+ if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) {
break;
}
}
-
// Add recover movement in order to make it safe
- body_safe_position.getOrigin() += recover_initial_position;
+ body_transform.getOrigin() += initial_recover_motion;
}
-#endif
btVector3 motion;
G_TO_B(p_motion, motion);
@@ -884,7 +884,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex)));
- btTransform shape_world_from = body_safe_position * p_body->get_kinematic_utilities()->shapes[shIndex].transform;
+ btTransform shape_world_from = body_transform * p_body->get_kinematic_utilities()->shapes[shIndex].transform;
btTransform shape_world_to(shape_world_from);
shape_world_to.getOrigin() += motion;
@@ -893,7 +893,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
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,
@@ -902,65 +902,49 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
}
}
- body_safe_position.getOrigin() += motion;
+ body_transform.getOrigin() += motion;
}
bool has_penetration = false;
- { /// Phase three - Recover + contact test with margin
+ { /// Phase three - contact test with margin
- btVector3 delta_recover_movement(0, 0, 0);
+ btVector3 __rec(0, 0, 0);
RecoverResult r_recover_result;
- bool l_has_penetration;
- real_t l_penetration_distance = 1e20;
- for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- l_has_penetration = recover_from_penetration(p_body, body_safe_position, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, delta_recover_movement, &r_recover_result);
+ has_penetration = recover_from_penetration(p_body, body_transform, 0, p_infinite_inertia, __rec, &r_recover_result);
- if (r_result) {
-#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);
- l_penetration_distance = r_recover_result.penetration_distance;
+ if (has_penetration) {
- const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object);
- CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer());
+ const btRigidBody *btRigid = static_cast<const btRigidBody *>(r_recover_result.other_collision_object);
+ CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(btRigid->getUserPointer());
- B_TO_G(motion, r_result->remainder); // is the remaining movements
- r_result->remainder = p_motion - r_result->remainder;
- B_TO_G(r_recover_result.pointWorld, r_result->collision_point);
- B_TO_G(r_recover_result.normal, r_result->collision_normal);
- B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity); // It calculates velocity at point and assign it using special function Bullet_to_Godot
- r_result->collider = collisionObject->get_self();
- r_result->collider_id = collisionObject->get_instance_id();
- r_result->collider_shape = r_recover_result.other_compound_shape_index;
- r_result->collision_local_shape = r_recover_result.local_shape_most_recovered;
+ B_TO_G(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;
- else
- has_penetration = true;
+ r_result->remainder = Vector3();
}
}
}
@@ -998,7 +982,7 @@ public:
}
void reset() {
- result_collision_objects.empty();
+ result_collision_objects.clear();
}
};
@@ -1033,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()) || (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()))
+ 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()) {
@@ -1112,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/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 55a7a1ac77..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.1-dev">
+<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 a34a650a4d..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"])
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 acfb83bc10..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,7 +25,8 @@ 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))
@@ -31,9 +34,10 @@ def _build_gdnative_api_struct_header(api):
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,8 +66,8 @@ 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 += ['};', '']
@@ -88,8 +93,9 @@ def _build_gdnative_api_struct_header(api):
return ret_val
- for name in api['extensions']:
- out += generate_extension_struct(name, api['extensions'][name], False)
+ for ext in api['extensions']:
+ name = ext['name']
+ out += generate_extension_struct(name, ext, False)
out += [
'typedef struct godot_gdnative_core_api_struct {',
@@ -151,12 +157,14 @@ def _build_gdnative_api_struct_source(api):
return ret_val
- for name in api['extensions']:
- out += get_extension_struct_definition(name, api['extensions'][name], False)
+ 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']
@@ -189,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])
@@ -214,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 += ['']
@@ -232,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))
@@ -267,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 998460eee1..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.1-dev">
+<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 4e87cbf450..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.1-dev">
+<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 ca1bae0598..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.1-dev">
+<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 6a71cd8d4d..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.1-dev">
+<class name="NativeScript" inherits="Script" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml
index 3783d9d0a4..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.1-dev">
+<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 a8919f7130..217fd87c3e 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5756,8 +5756,9 @@
}
]
},
- "extensions": {
- "nativescript": {
+ "extensions": [
+ {
+ "name": "nativescript",
"type": "NATIVESCRIPT",
"version": {
"major": 1,
@@ -5822,6 +5823,23 @@
]
},
{
+ "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": [
@@ -5925,7 +5943,8 @@
}
]
},
- "pluginscript": {
+ {
+ "name": "pluginscript",
"type": "PLUGINSCRIPT",
"version": {
"major": 1,
@@ -5942,8 +5961,9 @@
}
]
},
- "arvr": {
- "type": "ARVR",
+ {
+ "name": "android",
+ "type": "ANDROID",
"version": {
"major": 1,
"minor": 0
@@ -5951,6 +5971,29 @@
"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",
"return_type": "void",
"arguments": [
@@ -6038,5 +6081,5 @@
}
]
}
- }
+ ]
}
diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h
new file mode 100644
index 0000000000..832dac9ac3
--- /dev/null
+++ b/modules/gdnative/include/android/godot_android.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* godot_android.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_ANDROID_GDN_H
+#define GODOT_ANDROID_GDN_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __ANDROID__
+#include <jni.h>
+#else
+#define JNIEnv void
+#define jobject void *
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEnv *GDAPI godot_android_get_env();
+jobject GDAPI godot_android_get_activity();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !GODOT_ANDROID_GDN_H */
diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h
index b9aedc0bef..63de62b507 100644
--- a/modules/gdnative/include/arvr/godot_arvr.h
+++ b/modules/gdnative/include/arvr/godot_arvr.h
@@ -37,7 +37,15 @@
extern "C" {
#endif
+// For future versions of the API we should only add new functions at the end of the structure and use the
+// version info to detect whether a call is available
+
+// Use these to populate version in your plugin
+#define GODOTVR_API_MAJOR 1
+#define GODOTVR_API_MINOR 0
+
typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
void *(*constructor)(godot_object *);
void (*destructor)(void *);
godot_string (*get_name)(const void *);
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 747328bc41..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 {
@@ -214,16 +217,19 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
// 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 {
- void *(*alloc_instance_binding_data)(void *, godot_object *);
- void (*free_instance_binding_data)(void *, void *);
+ GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *);
+ GDCALLINGCONV void (*free_instance_binding_data)(void *, void *);
void *data;
- void (*free_func)(void *);
+ 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);
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
new file mode 100644
index 0000000000..bfa688592d
--- /dev/null
+++ b/modules/gdnative/include/net/godot_net.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* godot_net.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_NATIVENET_H
+#define GODOT_NATIVENET_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For future versions of the API we should only add new functions at the end of the structure and use the
+// version info to detect whether a call is available
+
+// Use these to populate version in your plugin
+#define GODOT_NET_API_MAJOR 3
+#define GODOT_NET_API_MINOR 1
+
+typedef struct {
+
+ godot_gdnative_api_version version; /* version of our API */
+ godot_object *data; /* User reference */
+
+ /* This is StreamPeer */
+ godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes);
+ godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int &r_received);
+ godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes);
+ godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int &r_sent);
+
+ int (*get_available_bytes)(const void *user);
+
+ void *next; /* For extension? */
+} godot_net_stream_peer;
+
+/* Binds a StreamPeerGDNative to the provided interface */
+void godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ void *next; /* For extension? */
+} godot_net_packet_peer;
+
+/* Binds a PacketPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ /* This is NetworkedMultiplayerPeer */
+ void (*set_transfer_mode)(void *, godot_int);
+ godot_int (*get_transfer_mode)(const void *);
+ // 0 = broadcast, 1 = server, <0 = all but abs(value)
+ void (*set_target_peer)(void *, godot_int);
+ godot_int (*get_packet_peer)(const void *);
+ godot_bool (*is_server)(const void *);
+ void (*poll)(void *);
+ // Must be > 0, 1 is for server
+ int32_t (*get_unique_id)(const void *);
+ void (*set_refuse_new_connections)(void *, godot_bool);
+ godot_bool (*is_refusing_new_connections)(const void *);
+ godot_int (*get_connection_status)(const void *);
+
+ void *next; /* For extension? Or maybe not... */
+} godot_net_multiplayer_peer;
+
+/* Binds a MultiplayerPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index aea595d0f0..ace2ecac5c 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -313,6 +313,14 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
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;
@@ -331,13 +339,11 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
const Object *o = (Object *)p_object;
if (!o->get_script_instance()) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a script!");
- ERR_FAIL_V(NULL);
+ return NULL;
} else {
NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr());
if (!script) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a nativescript attached");
- ERR_FAIL_V(NULL);
+ return NULL;
}
if (script->get_script_desc())
@@ -347,10 +353,6 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
return NULL;
}
-#ifdef __cplusplus
-}
-#endif
-
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);
}
@@ -362,3 +364,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i
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 f2e9bef467..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);
@@ -363,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());
}
}
@@ -528,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) {
@@ -760,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();
@@ -770,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();
@@ -800,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() {
@@ -872,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();
@@ -916,18 +906,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
gdn = E->get();
}
- 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);
- }
- }
-
for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) {
// free property stuff first
@@ -952,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
}
@@ -1182,8 +1182,11 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
}
if (!(*binding_data)[p_idx]) {
+
+ const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+
// 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, (godot_object *)p_object);
+ (*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];
@@ -1225,6 +1228,27 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_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);
@@ -1362,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();
@@ -1375,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 17b6ddc747..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"
@@ -195,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);
@@ -240,6 +241,8 @@ private:
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;
@@ -323,6 +326,9 @@ public:
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/net/SCsub b/modules/gdnative/net/SCsub
new file mode 100644
index 0000000000..53f9271128
--- /dev/null
+++ b/modules/gdnative/net/SCsub
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import os
+import methods
+
+Import('env')
+Import('env_modules')
+
+env_net_gdnative = env_modules.Clone()
+
+env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
+env_net_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
new file mode 100644
index 0000000000..e2d710b5ad
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_peer_gdnative.h"
+
+MultiplayerPeerGDNative::MultiplayerPeerGDNative() {
+ interface = NULL;
+}
+
+MultiplayerPeerGDNative::~MultiplayerPeerGDNative() {
+}
+
+void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) {
+ interface = p_interface;
+}
+
+Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
+}
+
+Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
+}
+
+int MultiplayerPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
+}
+
+/* NetworkedMultiplayerPeer */
+void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_transfer_mode(interface->data, (godot_int)p_mode);
+}
+
+NetworkedMultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
+ ERR_FAIL_COND_V(interface == NULL, TRANSFER_MODE_UNRELIABLE);
+ return (TransferMode)interface->get_transfer_mode(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_target_peer(interface->data, p_peer_id);
+}
+
+int MultiplayerPeerGDNative::get_packet_peer() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_packet_peer(interface->data);
+}
+
+bool MultiplayerPeerGDNative::is_server() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->is_server(interface->data);
+}
+
+void MultiplayerPeerGDNative::poll() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->poll(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_unique_id() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_unique_id(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_refuse_new_connections(interface->data, p_enable);
+}
+
+bool MultiplayerPeerGDNative::is_refusing_new_connections() const {
+ ERR_FAIL_COND_V(interface == NULL, true);
+ return interface->is_refusing_new_connections(interface->data);
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const {
+ ERR_FAIL_COND_V(interface == NULL, CONNECTION_DISCONNECTED);
+ return (ConnectionStatus)interface->get_connection_status(interface->data);
+}
+
+void MultiplayerPeerGDNative::_bind_methods() {
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) {
+
+ ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl);
+}
+}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
new file mode 100644
index 0000000000..c8c95b3dd7
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_PEER_GDNATIVE_H
+#define MULTIPLAYER_PEER_GDNATIVE_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer {
+ GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer)
+
+protected:
+ static void _bind_methods();
+ const godot_net_multiplayer_peer *interface;
+
+public:
+ MultiplayerPeerGDNative();
+ ~MultiplayerPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
+
+ /* Specific to PacketPeer */
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const;
+ virtual int get_available_packet_count() const;
+
+ /* Specific to NetworkedMultiplayerPeer */
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual TransferMode get_transfer_mode() const;
+ virtual void set_target_peer(int p_peer_id);
+
+ virtual int get_packet_peer() const;
+
+ virtual bool is_server() const;
+
+ virtual void poll();
+
+ virtual int get_unique_id() const;
+
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual ConnectionStatus get_connection_status() const;
+};
+
+#endif // MULTIPLAYER_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp
new file mode 100644
index 0000000000..ceae79edc0
--- /dev/null
+++ b/modules/gdnative/net/packet_peer_gdnative.cpp
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* packet_peer_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "packet_peer_gdnative.h"
+
+PacketPeerGDNative::PacketPeerGDNative() {
+ interface = NULL;
+}
+
+PacketPeerGDNative::~PacketPeerGDNative() {
+}
+
+void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) {
+ interface = p_impl;
+}
+
+void PacketPeerGDNative::_bind_methods() {
+}
+
+Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
+}
+
+Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
+}
+
+int PacketPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
+}
+
+int PacketPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) {
+
+ ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl);
+}
+}
diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h
new file mode 100644
index 0000000000..71814177ed
--- /dev/null
+++ b/modules/gdnative/net/packet_peer_gdnative.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* packet_peer_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PACKET_PEER_GDNATIVE_H
+#define PACKET_PEER_GDNATIVE_H
+
+#include "core/io/packet_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class PacketPeerGDNative : public PacketPeer {
+ GDCLASS(PacketPeerGDNative, PacketPeer)
+
+protected:
+ static void _bind_methods();
+ const godot_net_packet_peer *interface;
+
+public:
+ PacketPeerGDNative();
+ ~PacketPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_packet_peer(const godot_net_packet_peer *p_impl);
+
+ /* Specific to PacketPeer */
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const;
+ virtual int get_available_packet_count() const;
+};
+
+#endif // PACKET_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp
new file mode 100644
index 0000000000..c3fb4d8008
--- /dev/null
+++ b/modules/gdnative/net/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "multiplayer_peer_gdnative.h"
+#include "packet_peer_gdnative.h"
+#include "stream_peer_gdnative.h"
+
+void register_net_types() {
+ ClassDB::register_class<MultiplayerPeerGDNative>();
+ ClassDB::register_class<PacketPeerGDNative>();
+ ClassDB::register_class<StreamPeerGDNative>();
+}
+
+void unregister_net_types() {
+}
diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h
new file mode 100644
index 0000000000..9545a2ba8f
--- /dev/null
+++ b/modules/gdnative/net/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_net_types();
+void unregister_net_types();
diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp
new file mode 100644
index 0000000000..4d1f2ec9a5
--- /dev/null
+++ b/modules/gdnative/net/stream_peer_gdnative.cpp
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* stream_peer_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "stream_peer_gdnative.h"
+
+StreamPeerGDNative::StreamPeerGDNative() {
+ interface = NULL;
+}
+
+StreamPeerGDNative::~StreamPeerGDNative() {
+}
+
+void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) {
+ interface = p_interface;
+}
+
+void StreamPeerGDNative::_bind_methods() {
+}
+
+Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_data(interface->data, p_data, p_bytes));
+}
+
+Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, r_sent));
+}
+
+Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_data(interface->data, p_buffer, p_bytes));
+}
+
+Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, r_received));
+}
+
+int StreamPeerGDNative::get_available_bytes() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_bytes(interface->data);
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface) {
+ ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface);
+}
+}
diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h
new file mode 100644
index 0000000000..654234e6ab
--- /dev/null
+++ b/modules/gdnative/net/stream_peer_gdnative.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* stream_peer_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef STREAM_PEER_GDNATIVE_H
+#define STREAM_PEER_GDNATIVE_H
+
+#include "core/io/stream_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class StreamPeerGDNative : public StreamPeer {
+
+ GDCLASS(StreamPeerGDNative, StreamPeer);
+
+protected:
+ static void _bind_methods();
+ godot_net_stream_peer *interface;
+
+public:
+ StreamPeerGDNative();
+ ~StreamPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_stream_peer(godot_net_stream_peer *p_interface);
+
+ /* Specific to StreamPeer */
+ Error put_data(const uint8_t *p_data, int p_bytes);
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
+ Error get_data(uint8_t *p_buffer, int p_bytes);
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+ int get_available_bytes() const;
+};
+
+#endif // STREAM_PEER_GDNATIVE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index 931ab0bfe4..13026e8e7a 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -138,11 +138,11 @@ void PluginScriptInstance::notification(int p_notification) {
_desc->notification(_data, p_notification);
}
-ScriptInstance::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
+MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
return _script->get_rpc_mode(p_method);
}
-ScriptInstance::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
+MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
return _script->get_rset_mode(p_variable);
}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 3c7b360ad9..8be6a4ccaa 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -76,8 +76,8 @@ public:
void set_path(const String &p_path);
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void refcount_incremented();
virtual bool refcount_decremented();
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 5ae7926f1b..c3a623e9a1 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -84,35 +84,20 @@ StringName PluginScript::get_instance_base_type() const {
}
void PluginScript::update_exports() {
-// TODO
#ifdef TOOLS_ENABLED
-#if 0
ASSERT_SCRIPT_VALID();
- if (/*changed &&*/ placeholders.size()) { //hm :(
+ if (placeholders.size()) {
//update placeholders if any
Map<StringName, Variant> propdefvalues;
List<PropertyInfo> propinfos;
- const String *props = (const String *)pybind_get_prop_list(_py_exposed_class);
- for (int i = 0; props[i] != ""; ++i) {
- const String propname = props[i];
- pybind_get_prop_default_value(_py_exposed_class, propname.c_str(), (godot_variant *)&propdefvalues[propname]);
- pybind_prop_info raw_info;
- pybind_get_prop_info(_py_exposed_class, propname.c_str(), &raw_info);
- PropertyInfo info;
- info.type = (Variant::Type)raw_info.type;
- info.name = propname;
- info.hint = (PropertyHint)raw_info.hint;
- info.hint_string = *(String *)&raw_info.hint_string;
- info.usage = raw_info.usage;
- propinfos.push_back(info);
- }
+
+ get_script_property_list(&propinfos);
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propinfos, propdefvalues);
+ E->get()->update(propinfos, _properties_default_values);
}
}
#endif
-#endif
}
// TODO: rename p_this "p_owner" ?
@@ -245,9 +230,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rpc_mode is passed as an optional field and is not part of MethodInfo
Variant var = v["rpc_mode"];
if (var == Variant()) {
- _methods_rpc_mode[mi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ _methods_rpc_mode[mi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
- _methods_rpc_mode[mi.name] = ScriptInstance::RPCMode(int(var));
+ _methods_rpc_mode[mi.name] = MultiplayerAPI::RPCMode(int(var));
}
}
Array *signals = (Array *)&manifest.signals;
@@ -265,9 +250,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rset_mode is passed as an optional field and is not part of PropertyInfo
Variant var = v["rset_mode"];
if (var == Variant()) {
- _methods_rpc_mode[pi.name] = ScriptInstance::RPC_MODE_DISABLED;
+ _methods_rpc_mode[pi.name] = MultiplayerAPI::RPC_MODE_DISABLED;
} else {
- _methods_rpc_mode[pi.name] = ScriptInstance::RPCMode(int(var));
+ _methods_rpc_mode[pi.name] = MultiplayerAPI::RPCMode(int(var));
}
}
// Manifest's attributes must be explicitly freed
@@ -402,23 +387,23 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
-ScriptInstance::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
- ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
- const Map<StringName, ScriptInstance::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
+MultiplayerAPI::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
+ ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _methods_rpc_mode.find(p_method);
if (e != NULL) {
return e->get();
} else {
- return ScriptInstance::RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
-ScriptInstance::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
- ASSERT_SCRIPT_VALID_V(ScriptInstance::RPC_MODE_DISABLED);
- const Map<StringName, ScriptInstance::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
+MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
+ ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
+ const Map<StringName, MultiplayerAPI::RPCMode>::Element *e = _variables_rset_mode.find(p_variable);
if (e != NULL) {
return e->get();
} else {
- return ScriptInstance::RPC_MODE_DISABLED;
+ return MultiplayerAPI::RPC_MODE_DISABLED;
}
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 1be9e907c2..31c6c4d67f 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -62,8 +62,8 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
- Map<StringName, ScriptInstance::RPCMode> _variables_rset_mode;
- Map<StringName, ScriptInstance::RPCMode> _methods_rpc_mode;
+ Map<StringName, MultiplayerAPI::RPCMode> _variables_rset_mode;
+ Map<StringName, MultiplayerAPI::RPCMode> _methods_rpc_mode;
Set<Object *> _instances;
//exported members
@@ -116,8 +116,8 @@ public:
virtual int get_member_line(const StringName &p_member) const;
- ScriptInstance::RPCMode get_rpc_mode(const StringName &p_method) const;
- ScriptInstance::RPCMode get_rset_mode(const StringName &p_variable) const;
+ MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
PluginScript();
void init(PluginScriptLanguage *language);
diff --git a/modules/gdnative/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 0dc69c3688..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.1-dev">
+<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 254d968e1b..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.1-dev">
+<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 1abcc4762f..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.1-dev">
+<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 048948dada..5c834966c5 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -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 505562324f..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()));
@@ -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 278585cb01..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);
@@ -1198,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);
@@ -1277,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();
@@ -1488,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;
@@ -1787,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"));
@@ -1798,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 ac7fd94141..9650563ee6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -461,18 +461,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} 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;
@@ -713,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) {
@@ -967,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);
}
@@ -1302,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;
}
}
@@ -1321,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);
}
@@ -1340,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);
}
@@ -1377,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);
}
@@ -1387,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);
}
@@ -3362,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);
@@ -3437,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();
@@ -3452,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();
@@ -3827,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 {
@@ -3920,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;
}
@@ -3959,7 +3967,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
}
- rpc_mode = ScriptInstance::RPC_MODE_REMOTE;
+ rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE;
continue;
} break;
@@ -3980,7 +3988,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
- rpc_mode = ScriptInstance::RPC_MODE_MASTER;
+ rpc_mode = MultiplayerAPI::RPC_MODE_MASTER;
continue;
} break;
case GDScriptTokenizer::TK_PR_SLAVE: {
@@ -4000,9 +4008,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
- rpc_mode = ScriptInstance::RPC_MODE_SLAVE;
+ rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE;
continue;
} break;
+ case GDScriptTokenizer::TK_PR_REMOTESYNC:
case GDScriptTokenizer::TK_PR_SYNC: {
//may be fallthrough from export, ignore if so
@@ -4015,7 +4024,37 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- rpc_mode = ScriptInstance::RPC_MODE_SYNC;
+ rpc_mode = MultiplayerAPI::RPC_MODE_SYNC;
+ continue;
+ } break;
+ case GDScriptTokenizer::TK_PR_MASTERSYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC;
+ continue;
+ } break;
+ case GDScriptTokenizer::TK_PR_SLAVESYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC;
continue;
} break;
case GDScriptTokenizer::TK_PR_VAR: {
@@ -4045,7 +4084,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
- rpc_mode = ScriptInstance::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
@@ -4054,7 +4093,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
#endif
tokenizer->advance();
- Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport);
+ Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL);
if (!subexpr) {
if (_recover_from_completion()) {
break;
@@ -4087,7 +4126,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member._export.type=Variant::DICTIONARY;
- } else*/ {
+ } else*/
+ {
if (subexpr->type != Node::TYPE_CONSTANT) {
@@ -4469,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 36d4d7cb59..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.1-dev">
+<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_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 90a4b65ed6..fc5972c810 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -101,8 +101,8 @@ void GridMapEditor::_menu_option(int p_option) {
}
if (edit_axis != new_axis) {
- int item1 = options->get_popup()->get_item_id(MENU_OPTION_NEXT_LEVEL);
- int item2 = options->get_popup()->get_item_id(MENU_OPTION_PREV_LEVEL);
+ 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"));
@@ -671,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;
}
@@ -800,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++) {
@@ -1067,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);
@@ -1176,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];
}
}
@@ -1278,6 +1287,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
selection.active = false;
updating = false;
+ accumulated_floor_delta = 0.0;
}
GridMapEditor::~GridMapEditor() {
@@ -1290,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 9b8db2daeb..7c5feda125 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -76,6 +76,7 @@ class GridMapEditor : public VBoxContainer {
Panel *panel;
MenuButton *options;
SpinBox *floor;
+ double accumulated_floor_delta;
ToolButton *mode_thumbnail;
ToolButton *mode_list;
HBoxContainer *spatial_editor_hb;
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
index b846ae38ad..38198c9105 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -85,7 +85,7 @@ if env['builtin_mbedtls']:
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.Append(CPPPATH=["#thirdparty/mbedtls/include/"])
+ 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
index 5f133eba90..1c8cd12a2d 100755
--- a/modules/mbedtls/config.py
+++ b/modules/mbedtls/config.py
@@ -1,4 +1,4 @@
-def can_build(platform):
+def can_build(env, platform):
return True
def configure(env):
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
index 4135eb40ff..a63e53ec1f 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.cpp
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -293,28 +293,10 @@ void StreamPeerMbedTLS::initialize_ssl() {
mbedtls_debug_set_threshold(1);
#endif
- 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);
- }
- }
+ PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array();
+
+ if (cert_array.size() > 0)
+ _load_certs(cert_array);
available = true;
}
diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h
index ce17614d85..2b96a194a1 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -32,8 +32,6 @@
#define STREAM_PEER_OPEN_SSL_H
#include "io/stream_peer_ssl.h"
-#include "os/file_access.h"
-#include "project_settings.h"
#include "mbedtls/config.h"
#include "mbedtls/ctr_drbg.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 d3f2548320..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.1-dev">
+<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 aa8626e6da..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'
@@ -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 7c1846dcc2..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,15 +198,17 @@ 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'))
@@ -172,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 fb45136575..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
}
@@ -280,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"
@@ -287,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;
@@ -325,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);
@@ -422,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
@@ -454,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());
@@ -758,7 +780,7 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
}
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
NodeDock::singleton->update_lists();
}
}
@@ -1286,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;
@@ -1313,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;
@@ -1334,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) {
@@ -1550,7 +1578,6 @@ bool CSharpScript::_update_exports() {
}
bool CSharpScript::_update_signals() {
-#ifdef TOOLS_ENABLED
if (!valid)
return false;
@@ -1581,8 +1608,6 @@ bool CSharpScript::_update_signals() {
}
return changed;
-#endif
- return false;
}
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
@@ -1705,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) {
@@ -1933,8 +1964,7 @@ 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
@@ -1948,6 +1978,18 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
#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) {
@@ -2024,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);
@@ -2135,9 +2172,7 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
void CSharpScript::update_signals() {
-#ifdef TOOLS_ENABLED
_update_signals();
-#endif
}
Ref<Script> CSharpScript::get_base_script() const {
@@ -2269,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());
@@ -2278,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 ffb1d2e0f4..cae2bbf40a 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -90,15 +90,15 @@ class CSharpScript : public Script {
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;
- Map<StringName, Vector<Argument> > _signals;
- bool signals_invalidated;
-
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
@@ -192,7 +192,7 @@ class CSharpInstance : public ScriptInstance {
void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
- RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const;
+ MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const;
public:
MonoObject *get_mono_object() const;
@@ -213,8 +213,8 @@ public:
virtual void refcount_incremented();
virtual bool refcount_decremented();
- virtual RPCMode get_rpc_mode(const StringName &p_method) const;
- virtual RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void notification(int p_notification);
void call_notification_no_check(MonoObject *p_mono_object, int p_notification);
@@ -294,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 0c2bb948ea..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.1-dev">
+<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 9bd57f1d4d..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.1-dev">
+<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 51f07523e7..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.1-dev">
+<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 952e033565..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);
@@ -2246,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);
@@ -2375,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)
@@ -2390,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) {
@@ -2461,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);
@@ -2488,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 9b5a9cea88..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,8 +528,8 @@ 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_val, ArgumentInterface &r_iarg);
void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype);
@@ -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 975cf0f030..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"));
@@ -316,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 20378a0162..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) {
@@ -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);
}
}
@@ -407,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());
+
+ // Get correct issue idx from issue list
+ int issue_idx = this->issues_list->get_item_metadata(p_idx);
- const BuildIssue &issue = issues[p_idx];
+ ERR_FAIL_INDEX(issue_idx, issues.size());
+
+ const BuildIssue &issue = issues[issue_idx];
if (issue.project_file.empty() && issue.file.empty())
return;
@@ -437,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/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 c6cdc069ef..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, yx, zx);
- this.y = new Vector3(xy, yy, zy);
- this.z = new Vector3(xz, yz, 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 476396e9a3..0d20a12563 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -1,75 +1,85 @@
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 (real_t)Math.Cosh(s);
+ }
+
+ public static int Decimals(real_t step)
{
return 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
index d8a6cabb83..3957387be9 100644
--- a/modules/mono/glue/cs_files/SignalAttribute.cs
+++ b/modules/mono/glue/cs_files/SignalAttribute.cs
@@ -5,8 +5,5 @@ namespace Godot
[AttributeUsage(AttributeTargets.Delegate)]
public class SignalAttribute : Attribute
{
- public SignalAttribute()
- {
- }
}
}
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 39474f8cbc..0646580eaa 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -42,7 +42,10 @@
#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
@@ -71,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 {
@@ -93,9 +120,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
return mono_is_debugger_attached();
}
-#endif
-#ifdef DEBUG_ENABLED
void gdmono_debug_init() {
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
@@ -122,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());
@@ -176,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");
@@ -208,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);
@@ -219,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
@@ -271,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();
@@ -317,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;
}
@@ -334,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
@@ -346,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
@@ -360,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");
@@ -385,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() {
@@ -441,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;
@@ -501,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);
@@ -562,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 ef39b8549d..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) {
@@ -121,10 +143,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
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();
+ if (stored_assembly)
+ return (*stored_assembly)->get_assembly();
String path;
- MonoAssembly *res = NULL;
+ GDMonoAssembly *res = NULL;
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
@@ -132,68 +155,73 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
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;
}
}
}
- if (res) return res;
+ 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");
@@ -213,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;
@@ -373,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 8a5fa19626..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; }
@@ -115,6 +122,8 @@ public:
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_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_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 8fd437223f..6572408ab5 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -195,9 +195,9 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
// Transform
#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \
- m_in.basis[0].x, m_in.basis[1].x, m_in.basis[2].x, \
- m_in.basis[0].y, m_in.basis[1].y, m_in.basis[2].y, \
- m_in.basis[0].z, m_in.basis[1].z, m_in.basis[2].z, \
+ m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \
+ m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \
+ m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \
m_in.origin.x, m_in.origin.y, m_in.origin.z \
};
#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 42e307cf08..fbdbeaa3f7 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -120,6 +120,9 @@ void MonoCache::clear_members() {
class_SyncAttribute = NULL;
class_MasterAttribute = NULL;
class_SlaveAttribute = NULL;
+ class_RemoteSyncAttribute = NULL;
+ class_MasterSyncAttribute = NULL;
+ class_SlaveSyncAttribute = NULL;
class_GodotMethodAttribute = NULL;
field_GodotMethodAttribute_methodName = NULL;
@@ -143,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() {
@@ -208,6 +211,9 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
+ CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute));
+ CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute));
+ CACHE_CLASS_AND_CHECK(SlaveSyncAttribute, GODOT_API_CLASS(SlaveSyncAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
@@ -245,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() {
@@ -305,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) {
@@ -321,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())
@@ -420,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 1a34180d15..fc13a00e85 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -112,6 +112,9 @@ struct MonoCache {
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
+ GDMonoClass *class_RemoteSyncAttribute;
+ GDMonoClass *class_MasterSyncAttribute;
+ GDMonoClass *class_SlaveSyncAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_SlaveAttribute;
GDMonoClass *class_GodotMethodAttribute;
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py
index 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/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 26f0501df3..75e8903ff8 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="RegEx" inherits="Reference" category="Core" version="3.1-dev">
+<class name="RegEx" inherits="Reference" category="Core" version="3.1">
<brief_description>
Class for searching text for patterns using regular expressions.
</brief_description>
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 550411d3e0..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.1-dev">
+<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 8d311ae1b8..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.1-dev">
+<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 5872dd35ff..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.1-dev">
+<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 6d2de4a9ad..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.1-dev">
+<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 550844128d..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.1-dev">
+<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 975b294f30..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.1-dev">
+<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 b529589b98..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.1-dev">
+<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 9c2ec3b849..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.1-dev">
+<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 dc946cfcbd..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.1-dev">
+<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 5c20f27ec2..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.1-dev">
+<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 a3313a43d9..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.1-dev">
+<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 274adb5423..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.1-dev">
+<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 2efe8d9bd6..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.1-dev">
+<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 2165c403d4..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.1-dev">
+<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 f076c70715..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.1-dev">
+<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 1c4542336f..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.1-dev">
+<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 7eea609db4..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.1-dev">
+<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 26196f278e..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.1-dev">
+<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 0b93c3092c..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.1-dev">
+<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 18daa42797..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.1-dev">
+<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 dc025cfb27..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.1-dev">
+<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 05e9a6cb81..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.1-dev">
+<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 d0db8cbab3..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.1-dev">
+<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 983d8882a7..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.1-dev">
+<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 bc876900f3..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.1-dev">
+<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 4316bf146d..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.1-dev">
+<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 d815476e2c..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.1-dev">
+<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 038fd8c9cd..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.1-dev">
+<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 fa65a89c4a..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.1-dev">
+<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 243ab03b49..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.1-dev">
+<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 c2c5464047..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.1-dev">
+<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 68a57191de..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.1-dev">
+<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 19abc27053..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.1-dev">
+<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 88aac85f59..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.1-dev">
+<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 ac962a071d..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.1-dev">
+<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 a3144582cb..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.1-dev">
+<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 ca50f811c3..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.1-dev">
+<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 3d4bdcfd0a..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.1-dev">
+<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 c77b674b64..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.1-dev">
+<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 f1aad841ca..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.1-dev">
+<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 c2847fcada..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.1-dev">
+<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 0180f34c72..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.1-dev">
+<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 d34d0b7127..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.1-dev">
+<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 ea9e6438cf..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.1-dev">
+<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 4bdfeab35b..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.1-dev">
+<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 76c218294e..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.1-dev">
+<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 0262ad5dfb..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.1-dev">
+<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 46a6ea7a30..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.1-dev">
+<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 a1129ffbd7..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.1-dev">
+<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 ad3a016c0d..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.1-dev">
+<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 ef680547ca..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) {
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 69503e631c..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;
@@ -3236,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);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 80bbf142d9..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();
@@ -246,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 c5654a5a20..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) {
@@ -467,12 +467,12 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
static const char *op_names[] = {
//comparison
- "Equal", //OP_EQUAL,
- "NotEqual", //OP_NOT_EQUAL,
- "Less", //OP_LESS,
- "LessEqual", //OP_LESS_EQUAL,
- "Greater", //OP_GREATER,
- "GreaterEq", //OP_GREATER_EQUAL,
+ "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,11 +500,6 @@ 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[] = {
//comparison
L"A = B", //OP_EQUAL,
@@ -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 7c8e1a46b1..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.1-dev">
+<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 d52e2324a1..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.1-dev">
+<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 0ebafa022d..c681e2b34f 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -263,7 +263,7 @@ 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:
@@ -337,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:
@@ -379,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)
- elif not env["android_arch"] == 'arm64v8':
+ 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/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
index 1a36e05863..15a88773e7 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -7,48 +7,57 @@ Import('env_modules')
env_lws = env_modules.Clone()
-thirdparty_dir = "#thirdparty/lws/"
+thirdparty_dir = "#thirdparty/libwebsockets/"
helper_dir = "win32helpers/"
thirdparty_sources = [
- "client/client.c",
- "client/client-handshake.c",
- "client/client-parser.c",
- "client/ssl-client.c",
-
- "ext/extension.c",
- "ext/extension-permessage-deflate.c",
-
- "server/fops-zip.c",
- "server/lejp-conf.c",
- "server/parsers.c",
- "server/ranges.c",
- "server/server.c",
- "server/server-handshake.c",
- "server/ssl-server.c",
+
+ "core/alloc.c",
+ "core/context.c",
+ "core/libwebsockets.c",
+ "core/output.c",
+ "core/pollfd.c",
+ "core/service.c",
+
+ "event-libs/poll/poll.c",
"misc/base64-decode.c",
"misc/lejp.c",
"misc/sha-1.c",
- "alloc.c",
- "context.c",
- "handshake.c",
- "header.c",
- "libwebsockets.c",
- "minilex.c",
- "output.c",
- "pollfd.c",
- "service.c",
- "ssl.c",
-
- "mbedtls_wrapper/library/ssl_cert.c",
- "mbedtls_wrapper/library/ssl_pkey.c",
- "mbedtls_wrapper/library/ssl_stack.c",
- "mbedtls_wrapper/library/ssl_methods.c",
- "mbedtls_wrapper/library/ssl_lib.c",
- "mbedtls_wrapper/library/ssl_x509.c",
- "mbedtls_wrapper/platform/ssl_port.c",
- "mbedtls_wrapper/platform/ssl_pm.c",
+ "roles/h1/ops-h1.c",
+ "roles/http/header.c",
+ "roles/http/client/client.c",
+ "roles/http/client/client-handshake.c",
+ "roles/http/server/fops-zip.c",
+ "roles/http/server/lejp-conf.c",
+ "roles/http/server/parsers.c",
+ "roles/http/server/server.c",
+ "roles/listen/ops-listen.c",
+ "roles/pipe/ops-pipe.c",
+ "roles/raw/ops-raw.c",
+
+ "roles/ws/client-ws.c",
+ "roles/ws/client-parser-ws.c",
+ "roles/ws/ops-ws.c",
+ "roles/ws/server-ws.c",
+
+ "tls/tls.c",
+ "tls/tls-client.c",
+ "tls/tls-server.c",
+
+ "tls/mbedtls/wrapper/library/ssl_cert.c",
+ "tls/mbedtls/wrapper/library/ssl_pkey.c",
+ "tls/mbedtls/wrapper/library/ssl_stack.c",
+ "tls/mbedtls/wrapper/library/ssl_methods.c",
+ "tls/mbedtls/wrapper/library/ssl_lib.c",
+ "tls/mbedtls/wrapper/library/ssl_x509.c",
+ "tls/mbedtls/wrapper/platform/ssl_port.c",
+ "tls/mbedtls/wrapper/platform/ssl_pm.c",
+ "tls/mbedtls/lws-genhash.c",
+ "tls/mbedtls/mbedtls-client.c",
+ "tls/mbedtls/lws-genrsa.c",
+ "tls/mbedtls/ssl.c",
+ "tls/mbedtls/mbedtls-server.c"
]
if env_lws["platform"] == "android": # Builtin getifaddrs
@@ -68,12 +77,12 @@ else:
env_lws.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.Append(CPPPATH=[thirdparty_dir])
- wrapper_includes = ["#thirdparty/lws/mbedtls_wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
- env_lws.Append(CPPPATH=wrapper_includes)
+ 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.Append(CPPPATH=[mbedtls_includes])
+ env_lws.Prepend(CPPPATH=[mbedtls_includes])
if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
diff --git a/modules/websocket/config.py b/modules/websocket/config.py
index fb920482f5..f59ef432b4 100644
--- a/modules/websocket/config.py
+++ b/modules/websocket/config.py
@@ -1,7 +1,16 @@
-
-def can_build(platform):
+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
index 38fe520fc1..1405fa98b0 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index 8801f37007..6c2fa23b53 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 93665e6428..e0b987b4d7 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -148,12 +148,14 @@ void EMWSPeer::close() {
IP_Address EMWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(IP_Address());
};
uint16_t EMWSPeer::get_connected_port() const {
- return 1025;
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(0);
};
EMWSPeer::EMWSPeer() {
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index a50d1874ba..e06f725265 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index 60e9133225..3eb93e4152 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -58,6 +58,19 @@ PoolVector<String> EMWSServer::get_protocols() const {
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() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index 59f1d76346..9ec4ce72c8 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -46,6 +46,9 @@ public:
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;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index 604b1886ad..06f97aaf05 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -31,6 +31,8 @@
#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) {
@@ -64,6 +66,9 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
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) {
@@ -87,7 +92,14 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
i.host = hbuf;
i.path = pbuf;
i.port = p_port;
- i.ssl_connection = p_ssl;
+
+ 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;
@@ -104,6 +116,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
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);
@@ -122,7 +141,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
destroy_context();
return -1; // we should close the connection (would probably happen anyway)
- case LWS_CALLBACK_CLOSED:
+ case LWS_CALLBACK_CLIENT_CLOSED:
peer_data->in_count = 0;
peer_data->out_count = 0;
peer_data->rbw.resize(0);
diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h
index 2e082175df..8850683cb5 100644
--- a/modules/websocket/lws_client.h
+++ b/modules/websocket/lws_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
index a832d458b6..a4920c3d54 100644
--- a/modules/websocket/lws_helper.h
+++ b/modules/websocket/lws_helper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -30,6 +30,9 @@
#ifndef LWS_HELPER_H
#define LWS_HELPER_H
+#define LWS_BUF_SIZE 65536
+#define LWS_PACKET_SIZE LWS_BUF_SIZE
+
#include "core/io/stream_peer.h"
#include "core/os/os.h"
#include "core/reference.h"
@@ -124,6 +127,7 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback,
/* LWS protocol structs */
ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2));
+ memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2));
CharString strings = p_names.join(",").ascii();
int str_len = strings.length();
@@ -145,13 +149,15 @@ static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback,
structs_ptr[0].name = "http-only";
structs_ptr[0].callback = p_callback;
structs_ptr[0].per_session_data_size = data_size;
- structs_ptr[0].rx_buffer_size = 0;
+ structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE;
/* add user defined protocols */
for (i = 0; i < len; i++) {
structs_ptr[i + 1].name = (const char *)&names_ptr[pos];
structs_ptr[i + 1].callback = p_callback;
structs_ptr[i + 1].per_session_data_size = data_size;
- structs_ptr[i + 1].rx_buffer_size = 0;
+ structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE;
pos += pnr[i].ascii().length() + 1;
names_ptr[pos - 1] = '\0';
}
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index fdaa79f9d4..96acb99cc4 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -32,6 +32,14 @@
#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;
};
@@ -178,12 +186,44 @@ void LWSPeer::close() {
IP_Address LWSPeer::get_connected_host() const {
- return IP_Address();
+ 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 {
- return 1025;
+ 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() {
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h
index 0a62b65d24..e96b38b168 100644
--- a/modules/websocket/lws_peer.h
+++ b/modules/websocket/lws_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 8a47ba557d..8d13dc7a98 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -164,6 +164,24 @@ Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
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;
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index 5f7ac4850a..9e3fb9b775 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -52,6 +52,9 @@ public:
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();
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 39d03ff1f0..721f3cc330 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
index 010d88789b..89ce93e286 100644
--- a/modules/websocket/register_types.h
+++ b/modules/websocket/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index f92a386988..7701163085 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -32,6 +32,8 @@
GDCINULL(WebSocketClient);
WebSocketClient::WebSocketClient() {
+
+ verify_ssl = true;
}
WebSocketClient::~WebSocketClient() {
@@ -72,6 +74,16 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco
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;
@@ -116,6 +128,10 @@ void WebSocketClient::_on_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")));
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 0e87825222..6165f37d40 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -41,12 +41,16 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
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;
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index b5c2159806..d27fb4d778 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp
index 8cd4dff38b..b948c439df 100644
--- a/modules/websocket/websocket_multiplayer.cpp
+++ b/modules/websocket/websocket_multiplayer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h
index e8e795e97f..8edfc5296e 100644
--- a/modules/websocket/websocket_multiplayer.h
+++ b/modules/websocket/websocket_multiplayer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index a6fbb4481b..61f783e377 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -43,6 +43,8 @@ void WebSocketPeer::_bind_methods() {
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
index f4d8ce3e38..ad451e9cc7 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index ba77019f55..53dd7b51b7 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -44,6 +44,9 @@ void WebSocketServer::_bind_methods() {
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")));
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index db188811fd..64935f8a58 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -52,6 +52,10 @@ public:
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);