summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/cscript/SCsub5
-rw-r--r--modules/cscript/config.py11
-rw-r--r--modules/cscript/godot_c.cpp2
-rw-r--r--modules/cscript/godot_c.h568
-rw-r--r--modules/cscript/register_types.cpp (renamed from modules/gdscript/gd_pretty_print.h)15
-rw-r--r--modules/cscript/register_types.h (renamed from modules/gdscript/gd_pretty_print.cpp)10
-rw-r--r--modules/enet/SCsub8
-rw-r--r--modules/enet/callbacks.c53
-rw-r--r--modules/enet/compress.c654
-rw-r--r--modules/enet/config.py11
-rw-r--r--modules/enet/enet/callbacks.h27
-rw-r--r--modules/enet/enet/enet.h596
-rw-r--r--modules/enet/enet/list.h43
-rw-r--r--modules/enet/enet/protocol.h198
-rw-r--r--modules/enet/enet/time.h18
-rw-r--r--modules/enet/enet/types.h13
-rw-r--r--modules/enet/enet/unix.h47
-rw-r--r--modules/enet/enet/utility.h12
-rw-r--r--modules/enet/enet/win32.h57
-rw-r--r--modules/enet/host.c492
-rw-r--r--modules/enet/list.c75
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp650
-rw-r--r--modules/enet/networked_multiplayer_enet.h114
-rw-r--r--modules/enet/packet.c165
-rw-r--r--modules/enet/peer.c1004
-rw-r--r--modules/enet/protocol.c1914
-rw-r--r--modules/enet/register_types.cpp51
-rw-r--r--modules/enet/register_types.h30
-rw-r--r--modules/enet/unix.c616
-rw-r--r--modules/enet/win32.c422
-rw-r--r--modules/gdscript/gd_compiler.cpp38
-rw-r--r--modules/gdscript/gd_editor.cpp151
-rw-r--r--modules/gdscript/gd_function.cpp40
-rw-r--r--modules/gdscript/gd_function.h16
-rw-r--r--modules/gdscript/gd_functions.cpp96
-rw-r--r--modules/gdscript/gd_functions.h1
-rw-r--r--modules/gdscript/gd_parser.cpp179
-rw-r--r--modules/gdscript/gd_parser.h13
-rw-r--r--modules/gdscript/gd_script.cpp280
-rw-r--r--modules/gdscript/gd_script.h119
-rw-r--r--modules/gdscript/gd_tokenizer.cpp18
-rw-r--r--modules/gdscript/gd_tokenizer.h4
-rw-r--r--modules/gdscript/register_types.cpp41
-rw-r--r--modules/gridmap/grid_map.cpp4
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp49
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h1
-rw-r--r--modules/ik/ik.cpp4
-rw-r--r--modules/ik/ik.h4
-rw-r--r--modules/register_module_types.h28
-rw-r--r--modules/visual_script/SCsub5
-rw-r--r--modules/visual_script/config.py11
-rw-r--r--modules/visual_script/register_types.cpp131
-rw-r--r--modules/visual_script/register_types.h30
-rw-r--r--modules/visual_script/visual_script.cpp2841
-rw-r--r--modules/visual_script/visual_script.h618
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp1197
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h110
-rw-r--r--modules/visual_script/visual_script_editor.cpp3495
-rw-r--r--modules/visual_script/visual_script_editor.h243
-rw-r--r--modules/visual_script/visual_script_expression.cpp1440
-rw-r--r--modules/visual_script/visual_script_expression.h269
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp1940
-rw-r--r--modules/visual_script/visual_script_flow_control.h341
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp2520
-rw-r--r--modules/visual_script/visual_script_func_nodes.h349
-rw-r--r--modules/visual_script/visual_script_nodes.cpp3958
-rw-r--r--modules/visual_script/visual_script_nodes.h1068
-rw-r--r--modules/visual_script/visual_script_yield_nodes.cpp625
-rw-r--r--modules/visual_script/visual_script_yield_nodes.h128
69 files changed, 30020 insertions, 266 deletions
diff --git a/modules/cscript/SCsub b/modules/cscript/SCsub
new file mode 100644
index 0000000000..403fe68f66
--- /dev/null
+++ b/modules/cscript/SCsub
@@ -0,0 +1,5 @@
+Import('env')
+
+env.add_source_files(env.modules_sources,"*.cpp")
+
+Export('env')
diff --git a/modules/cscript/config.py b/modules/cscript/config.py
new file mode 100644
index 0000000000..ea7e83378a
--- /dev/null
+++ b/modules/cscript/config.py
@@ -0,0 +1,11 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+
diff --git a/modules/cscript/godot_c.cpp b/modules/cscript/godot_c.cpp
new file mode 100644
index 0000000000..d5c1b53dfe
--- /dev/null
+++ b/modules/cscript/godot_c.cpp
@@ -0,0 +1,2 @@
+#include "godot_c.h"
+
diff --git a/modules/cscript/godot_c.h b/modules/cscript/godot_c.h
new file mode 100644
index 0000000000..b0465d8524
--- /dev/null
+++ b/modules/cscript/godot_c.h
@@ -0,0 +1,568 @@
+#ifndef GODOT_C_H
+#define GODOT_C_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if defined(GDAPI_BUILT_IN) || !defined(WINDOWS_ENABLED)
+#define GDAPI
+#elif defined(GDAPI_EXPORTS)
+#define GDAPI __declspec(dllexport)
+#else
+#define GDAPI __declspec(dllimport)
+#endif
+
+
+#define GODOT_API_VERSION 1
+
+
+typedef int godot_bool;
+
+#define GODOT_FALSE 0
+#define GODOT_TRUE 1
+
+////// Image
+
+
+#define GODOT_IMAGE_FORMAT_GRAYSCALE 0
+#define GODOT_IMAGE_FORMAT_INTENSITY 1
+#define GODOT_IMAGE_FORMAT_GRAYSCALE_ALPHA 2
+#define GODOT_IMAGE_FORMAT_RGB 3
+#define GODOT_IMAGE_FORMAT_RGBA 4
+#define GODOT_IMAGE_FORMAT_INDEXED 5
+#define GODOT_IMAGE_FORMAT_INDEXED_ALPHA 6
+#define GODOT_IMAGE_FORMAT_YUV_422 7
+#define GODOT_IMAGE_FORMAT_YUV_444 8
+#define GODOT_IMAGE_FORMAT_BC1 9
+#define GODOT_IMAGE_FORMAT_BC2 10
+#define GODOT_IMAGE_FORMAT_BC3 11
+#define GODOT_IMAGE_FORMAT_BC4 12
+#define GODOT_IMAGE_FORMAT_BC5 13
+#define GODOT_IMAGE_FORMAT_PVRTC2 14
+#define GODOT_IMAGE_FORMAT_PVRTC2_ALPHA 15
+#define GODOT_IMAGE_FORMAT_PVRTC4 16
+#define GODOT_IMAGE_FORMAT_PVRTC4_ALPHA 17
+#define GODOT_IMAGE_FORMAT_ETC 18
+#define GODOT_IMAGE_FORMAT_ATC 19
+#define GODOT_IMAGE_FORMAT_ATC_ALPHA_EXPLICIT 20
+#define GODOT_IMAGE_FORMAT_ATC_ALPHA_INTERPOLATED 21
+
+typedef void* godot_image;
+
+godot_image GDAPI godot_image_create_empty();
+godot_image GDAPI godot_image_create(int p_width,int p_height,int p_format,int p_use_mipmaps);
+godot_image GDAPI godot_image_create_with_data(int p_width,int p_height,int p_format,int p_use_mipmaps,unsigned char* p_buffer);
+int GDAPI godot_image_get_width(godot_image p_image);
+int GDAPI godot_image_get_height(godot_image p_image);
+int GDAPI godot_image_get_format(godot_image p_image);
+int GDAPI godot_image_get_mipmap_count(godot_image p_image);
+godot_image GDAPI godot_image_copy(godot_image p_image);
+void GDAPI godot_image_free(godot_image p_image);
+
+
+////// RID
+
+typedef void* godot_rid;
+
+godot_rid GDAPI godot_rid_create();
+godot_rid GDAPI godot_rid_copy(godot_rid p_rid);
+void GDAPI godot_rid_free(godot_rid p_rid);
+
+////// Variant (forward declared)
+
+typedef void* godot_variant;
+
+
+////// Dictionary
+
+typedef void* godot_dictionary;
+
+godot_dictionary GDAPI godot_dictionary_create();
+void GDAPI godot_dictionary_has(godot_dictionary p_dictionary,godot_variant p_key);
+godot_variant GDAPI godot_dictionary_get(godot_dictionary p_dictionary,godot_variant p_key);
+void GDAPI godot_dictionary_insert(godot_dictionary p_dictionary,godot_variant p_key,godot_variant p_value);
+void GDAPI godot_dictionary_remove(godot_dictionary p_dictionary,godot_variant p_key);
+void GDAPI godot_dictionary_clear(godot_dictionary p_dictionary);
+int GDAPI godot_dictionary_get_size(godot_dictionary p_dictionary);
+void GDAPI godot_dictionary_get_keys(godot_dictionary p_dictionary,godot_variant* p_keys);
+godot_dictionary GDAPI godot_dictionary_copy(godot_dictionary p_dictionary);
+void GDAPI godot_dictionary_free(godot_dictionary p_dictionary);
+
+////// Array
+
+typedef void* godot_array;
+
+godot_array GDAPI godot_array_create();
+godot_variant GDAPI godot_array_get(godot_array p_array,int p_index);
+void GDAPI godot_array_set(godot_array p_array,int p_index,godot_variant p_value);
+void GDAPI godot_array_resize(godot_array p_array,int p_size);
+void GDAPI godot_array_insert(godot_array p_array,int p_position,godot_variant p_value);
+void GDAPI godot_array_remove(godot_array p_array,int p_position);
+void GDAPI godot_array_clear(godot_array p_array);
+int GDAPI godot_array_get_size(godot_array p_array);
+int GDAPI godot_array_find(godot_array p_array,godot_variant p_value,int p_from_pos=-1);
+godot_array GDAPI godot_array_copy(godot_array p_array);
+void GDAPI godot_array_free(godot_array p_array);
+
+////// InputEvent
+
+#define INPUT_EVENT_BUTTON_LEFT 1
+#define INPUT_EVENT_BUTTON_RIGHT 2
+#define INPUT_EVENT_BUTTON_MIDDLE 3
+#define INPUT_EVENT_BUTTON_WHEEL_UP 4
+#define INPUT_EVENT_BUTTON_WHEEL_DOWN 5
+#define INPUT_EVENT_BUTTON_WHEEL_LEFT 6
+#define INPUT_EVENT_BUTTON_WHEEL_RIGHT 7
+#define INPUT_EVENT_BUTTON_MASK_LEFT (1<<(INPUT_EVENT_BUTTON_LEFT-1))
+#define INPUT_EVENT_BUTTON_MASK_RIGHT (1<<(INPUT_EVENT_BUTTON_RIGHT-1))
+#define INPUT_EVENT_BUTTON_MASK_MIDDLE (1<<(INPUT_EVENT_BUTTON_MIDDLE-1))
+
+#define INPUT_EVENT_TYPE_NONE 0
+#define INPUT_EVENT_TYPE_KEY 1
+#define INPUT_EVENT_TYPE_MOUSE_MOTION 2
+#define INPUT_EVENT_TYPE_MOUSE_BUTTON 3
+#define INPUT_EVENT_TYPE_JOYSTICK_MOTION 4
+#define INPUT_EVENT_TYPE_JOYSTICK_BUTTON 5
+#define INPUT_EVENT_TYPE_SCREEN_TOUCH 6
+#define INPUT_EVENT_TYPE_SCREEN_DRAG 7
+#define INPUT_EVENT_TYPE_ACTION 8
+
+typedef void* godot_input_event;
+
+
+godot_input_event GDAPI godot_input_event_create();
+godot_input_event GDAPI godot_input_event_copy(godot_input_event p_input_event);
+void GDAPI godot_input_event_free(godot_input_event p_input_event);
+
+int GDAPI godot_input_event_get_type(godot_input_event p_event);
+int GDAPI godot_input_event_get_device(godot_input_event p_event);
+
+godot_bool GDAPI godot_input_event_mod_has_alt(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mod_has_ctrl(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mod_has_command(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mod_has_shift(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mod_has_meta(godot_input_event p_event);
+
+int GDAPI godot_input_event_key_get_scancode(godot_input_event p_event);
+int GDAPI godot_input_event_key_get_unicode(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_key_is_pressed(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_key_is_echo(godot_input_event p_event);
+
+int GDAPI godot_input_event_mouse_get_x(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_get_y(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_get_global_x(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_get_global_y(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_get_button_mask(godot_input_event p_event);
+
+int GDAPI godot_input_event_mouse_button_get_button_index(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mouse_button_is_pressed(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_mouse_button_is_doubleclick(godot_input_event p_event);
+
+int GDAPI godot_input_event_mouse_motion_get_relative_x(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_motion_get_relative_y(godot_input_event p_event);
+
+int GDAPI godot_input_event_mouse_motion_get_speed_x(godot_input_event p_event);
+int GDAPI godot_input_event_mouse_motion_get_speed_y(godot_input_event p_event);
+
+int GDAPI godot_input_event_joystick_motion_get_axis(godot_input_event p_event);
+float GDAPI godot_input_event_joystick_motion_get_axis_value(godot_input_event p_event);
+
+int GDAPI godot_input_event_joystick_button_get_button_index(godot_input_event p_event);
+godot_bool GDAPI godot_input_event_joystick_button_is_pressed(godot_input_event p_event);
+float GDAPI godot_input_event_joystick_button_get_pressure(godot_input_event p_event);
+
+
+int GDAPI godot_input_event_screen_touch_get_index(godot_input_event p_event);
+int GDAPI godot_input_event_screen_touch_get_x(godot_input_event p_event);
+int GDAPI godot_input_event_screen_touch_get_y(godot_input_event p_event);
+int GDAPI godot_input_event_screen_touch_is_pressed(godot_input_event p_event);
+
+int GDAPI godot_input_event_screen_drag_get_index(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_x(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_y(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_relative_x(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_relative_y(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_speed_x(godot_input_event p_event);
+int GDAPI godot_input_event_screen_drag_get_speed_y(godot_input_event p_event);
+
+int GDAPI godot_input_event_is_action(godot_input_event p_event,char *p_action);
+int GDAPI godot_input_event_is_action_pressed(godot_input_event p_event,char *p_action);
+
+////// ByteArray
+
+typedef void* godot_byte_array;
+
+godot_byte_array GDAPI godot_byte_array_create();
+godot_byte_array GDAPI godot_byte_array_copy(godot_byte_array p_byte_array);
+void GDAPI godot_byte_array_free(godot_byte_array p_byte_array);
+
+int GDAPI godot_byte_array_get_size(godot_byte_array p_byte_array);
+unsigned char GDAPI godot_byte_array_get(godot_byte_array p_byte_array,int p_index);
+void GDAPI godot_byte_array_set(godot_byte_array p_byte_array,int p_index,unsigned char p_value);
+void GDAPI godot_byte_array_remove(godot_byte_array p_byte_array,int p_index);
+void GDAPI godot_byte_array_clear(godot_byte_array p_byte_array);
+
+typedef void* godot_byte_array_lock;
+
+godot_byte_array_lock GDAPI godot_byte_array_get_lock(godot_byte_array p_byte_array);
+unsigned char GDAPI *godot_byte_array_lock_get_pointer(godot_byte_array_lock p_byte_array_lock);
+void GDAPI godot_byte_array_lock_free(godot_byte_array_lock p_byte_array_lock);
+
+
+godot_image GDAPI godot_image_create_with_array(int p_width,int p_height,int p_format,int p_use_mipmaps,godot_array p_array);
+godot_byte_array GDAPI godot_image_get_data(godot_image p_image);
+
+
+////// IntArray
+
+typedef void* godot_int_array;
+
+godot_int_array GDAPI godot_int_array_create();
+godot_int_array GDAPI godot_int_array_copy(godot_int_array p_int_array);
+void GDAPI godot_int_array_free(godot_int_array p_int_array);
+
+int GDAPI godot_int_array_get_size(godot_int_array p_int_array);
+int GDAPI godot_int_array_get(godot_int_array p_int_array,int p_index);
+void GDAPI godot_int_array_set(godot_int_array p_int_array,int p_index,int p_value);
+void GDAPI godot_int_array_remove(godot_int_array p_int_array,int p_index);
+void GDAPI godot_int_array_clear(godot_int_array p_int_array);
+
+typedef void* godot_int_array_lock;
+
+godot_int_array_lock GDAPI godot_int_array_get_lock(godot_int_array p_int_array);
+int GDAPI *godot_int_array_lock_get_pointer(godot_int_array_lock p_int_array_lock);
+void GDAPI godot_int_array_lock_free(godot_int_array_lock p_int_array_lock);
+
+////// RealArray
+
+typedef void* godot_real_array;
+
+
+godot_real_array GDAPI godot_real_array_create();
+godot_real_array GDAPI godot_real_array_copy(godot_real_array p_real_array);
+void GDAPI godot_real_array_free(godot_real_array p_real_array);
+
+int GDAPI godot_real_array_get_size(godot_real_array p_real_array);
+float GDAPI godot_real_array_get(godot_real_array p_real_array,int p_index);
+void GDAPI godot_real_array_set(godot_real_array p_real_array,int p_index,float p_value);
+void GDAPI godot_real_array_remove(godot_real_array p_real_array,int p_index);
+void GDAPI godot_real_array_clear(godot_real_array p_real_array);
+
+typedef void* godot_real_array_lock;
+
+godot_real_array_lock GDAPI godot_real_array_get_lock(godot_real_array p_real_array);
+float GDAPI *godot_real_array_lock_get_pointer(godot_real_array_lock p_real_array_lock);
+void GDAPI godot_real_array_lock_free(godot_real_array_lock p_real_array_lock);
+
+
+////// StringArray
+
+typedef void* godot_string_array;
+
+
+godot_string_array GDAPI godot_string_array_create();
+godot_string_array GDAPI godot_string_array_copy(godot_string_array p_string_array);
+void GDAPI godot_string_array_free(godot_string_array p_string_array);
+
+int GDAPI godot_string_array_get_size(godot_string_array p_string_array);
+int GDAPI godot_string_array_get(godot_string_array p_string_array,int p_index,unsigned char* p_string,int p_max_len);
+void GDAPI godot_string_array_set(godot_string_array p_string_array,int p_index,unsigned char *p_string);
+void GDAPI godot_string_array_remove(godot_string_array p_string_array,int p_index);
+void GDAPI godot_string_array_clear(godot_string_array p_string_array);
+
+////// Vector2Array
+
+typedef void* godot_vector2_array;
+
+godot_vector2_array GDAPI godot_vector2_array_create();
+godot_vector2_array GDAPI godot_vector2_array_copy(godot_vector2_array p_vector2_array);
+void GDAPI godot_vector2_array_free(godot_vector2_array p_vector2_array);
+
+int GDAPI godot_vector2_array_get_size(godot_vector2_array p_vector2_array);
+int GDAPI godot_vector2_array_get_stride(godot_vector2_array p_vector2_array);
+void GDAPI godot_vector2_array_get(godot_vector2_array p_vector2_array,int p_index,float* p_vector2);
+void GDAPI godot_vector2_array_set(godot_vector2_array p_vector2_array,int p_index,float *p_vector2);
+void GDAPI godot_vector2_array_remove(godot_vector2_array p_vector2_array,int p_index);
+void GDAPI godot_vector2_array_clear(godot_vector2_array p_vector2_array);
+
+
+typedef void* godot_vector2_array_lock;
+
+godot_vector2_array_lock GDAPI godot_vector2_array_get_lock(godot_vector2_array p_vector2_array);
+float GDAPI *godot_vector2_array_lock_get_pointer(godot_vector2_array_lock p_vector2_array_lock);
+void GDAPI godot_vector2_array_lock_free(godot_vector2_array_lock p_vector2_array_lock);
+
+////// Vector3Array
+
+typedef void* godot_vector3_array;
+
+godot_vector3_array GDAPI godot_vector3_array_create();
+godot_vector3_array GDAPI godot_vector3_array_copy(godot_vector3_array p_vector3_array);
+void GDAPI godot_vector3_array_free(godot_vector3_array p_vector3_array);
+
+int GDAPI godot_vector3_array_get_size(godot_vector3_array p_vector3_array);
+int GDAPI godot_vector3_array_get_stride(godot_vector3_array p_vector3_array);
+void GDAPI godot_vector3_array_get(godot_vector3_array p_vector3_array,int p_index,float* p_vector3);
+void GDAPI godot_vector3_array_set(godot_vector3_array p_vector3_array,int p_index,float *p_vector3);
+void GDAPI godot_vector3_array_remove(godot_vector3_array p_vector3_array,int p_index);
+void GDAPI godot_vector3_array_clear(godot_vector3_array p_vector3_array);
+
+
+typedef void* godot_vector3_array_lock;
+
+godot_vector3_array_lock GDAPI godot_vector3_array_get_lock(godot_vector3_array p_vector3_array);
+float GDAPI *godot_vector3_array_lock_get_pointer(godot_vector3_array_lock p_vector3_array_lock);
+void GDAPI godot_vector3_array_lock_free(godot_vector3_array_lock p_vector3_array_lock);
+
+////// ColorArray
+
+typedef void* godot_color_array;
+
+godot_color_array GDAPI godot_color_array_create();
+godot_color_array GDAPI godot_color_array_copy(godot_color_array p_color_array);
+void GDAPI godot_color_array_free(godot_color_array p_color_array);
+
+int GDAPI godot_color_array_get_size(godot_color_array p_color_array);
+int GDAPI godot_color_array_get_stride(godot_color_array p_color_array);
+void GDAPI godot_color_array_get(godot_color_array p_color_array,int p_index,float* p_color);
+void GDAPI godot_color_array_set(godot_color_array p_color_array,int p_index,float *p_color);
+void GDAPI godot_color_array_remove(godot_color_array p_color_array,int p_index);
+void GDAPI godot_color_array_clear(godot_color_array p_color_array);
+
+
+typedef void* godot_color_array_lock;
+
+godot_color_array_lock GDAPI godot_color_array_get_lock(godot_color_array p_color_array);
+float GDAPI *godot_color_array_lock_get_pointer(godot_color_array_lock p_color_array_lock);
+void GDAPI godot_color_array_lock_free(godot_color_array_lock p_color_array_lock);
+
+
+////// Instance (forward declared)
+
+typedef void *godot_instance;
+
+////// Variant
+
+#define GODOT_VARIANT_NIL 0
+#define GODOT_VARIANT_BOOL 1
+#define GODOT_VARIANT_INT 2
+#define GODOT_VARIANT_REAL 3
+#define GODOT_VARIANT_STRING 4
+#define GODOT_VARIANT_VECTOR2 5
+#define GODOT_VARIANT_RECT2 6
+#define GODOT_VARIANT_VECTOR3 7
+#define GODOT_VARIANT_MATRIX32 8
+#define GODOT_VARIANT_PLANE 9
+#define GODOT_VARIANT_QUAT 10
+#define GODOT_VARIANT_AABB 11
+#define GODOT_VARIANT_MATRIX3 12
+#define GODOT_VARIANT_TRANSFORM 13
+#define GODOT_VARIANT_COLOR 14
+#define GODOT_VARIANT_IMAGE 15
+#define GODOT_VARIANT_NODE_PATH 16
+#define GODOT_VARIANT_RID 17
+#define GODOT_VARIANT_OBJECT 18
+#define GODOT_VARIANT_INPUT_EVENT 19
+#define GODOT_VARIANT_DICTIONARY 20
+#define GODOT_VARIANT_ARRAY 21
+#define GODOT_VARIANT_BYTE_ARRAY 22
+#define GODOT_VARIANT_INT_ARRAY 23
+#define GODOT_VARIANT_REAL_ARRAY 24
+#define GODOT_VARIANT_STRING_ARRAY 25
+#define GODOT_VARIANT_VECTOR2_ARRAY 26
+#define GODOT_VARIANT_VECTOR3_ARRAY 27
+#define GODOT_VARIANT_COLOR_ARRAY 28
+#define GODOT_VARIANT_MAX 29
+
+godot_variant *godot_variant_new();
+
+int GDAPI godot_variant_get_type(godot_variant p_variant);
+
+void GDAPI godot_variant_set_null(godot_variant p_variant);
+void GDAPI godot_variant_set_bool(godot_variant p_variant,godot_bool p_bool);
+void GDAPI godot_variant_set_int(godot_variant p_variant,int p_int);
+void GDAPI godot_variant_set_float(godot_variant p_variant,int p_float);
+void GDAPI godot_variant_set_string(godot_variant p_variant,char *p_string);
+void GDAPI godot_variant_set_vector2(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_rect2(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_vector3(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_matrix32(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_plane(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_aabb(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_matrix3(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_transform(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_color(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_set_image(godot_variant p_variant,godot_image *p_image);
+void GDAPI godot_variant_set_node_path(godot_variant p_variant,char *p_path);
+void GDAPI godot_variant_set_rid(godot_variant p_variant,char *p_path);
+void GDAPI godot_variant_set_instance(godot_variant p_variant,godot_instance p_instance);
+void GDAPI godot_variant_set_input_event(godot_variant p_variant,godot_input_event p_instance);
+void GDAPI godot_variant_set_dictionary(godot_variant p_variant,godot_dictionary p_dictionary);
+void GDAPI godot_variant_set_array(godot_variant p_variant,godot_array p_array);
+void GDAPI godot_variant_set_byte_array(godot_variant p_variant,godot_byte_array p_array);
+void GDAPI godot_variant_set_int_array(godot_variant p_variant,godot_byte_array p_array);
+void GDAPI godot_variant_set_string_array(godot_variant p_variant,godot_string_array p_array);
+void GDAPI godot_variant_set_vector2_array(godot_variant p_variant,godot_vector2_array p_array);
+void GDAPI godot_variant_set_vector3_array(godot_variant p_variant,godot_vector3_array p_array);
+void GDAPI godot_variant_set_color_array(godot_variant p_variant,godot_color_array p_array);
+
+godot_bool GDAPI godot_variant_get_bool(godot_variant p_variant);
+int GDAPI godot_variant_get_int(godot_variant p_variant);
+float GDAPI godot_variant_get_float(godot_variant p_variant);
+int GDAPI godot_variant_get_string(godot_variant p_variant,char *p_string,int p_bufsize);
+void GDAPI godot_variant_get_vector2(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_rect2(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_vector3(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_matrix32(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_plane(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_aabb(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_matrix3(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_transform(godot_variant p_variant,float *p_elems);
+void GDAPI godot_variant_get_color(godot_variant p_variant,float *p_elems);
+godot_image GDAPI *godot_variant_get_image(godot_variant p_variant);
+int GDAPI godot_variant_get_node_path(godot_variant p_variant,char *p_path, int p_bufsize);
+godot_rid GDAPI godot_variant_get_rid(godot_variant p_variant);
+godot_instance GDAPI godot_variant_get_instance(godot_variant p_variant);
+void GDAPI godot_variant_get_input_event(godot_variant p_variant,godot_input_event);
+void GDAPI godot_variant_get_dictionary(godot_variant p_variant,godot_dictionary);
+godot_array GDAPI godot_variant_get_array(godot_variant p_variant);
+godot_byte_array GDAPI godot_variant_get_byte_array(godot_variant p_variant);
+godot_byte_array GDAPI godot_variant_get_int_array(godot_variant p_variant);
+godot_string_array GDAPI godot_variant_get_string_array(godot_variant p_variant);
+godot_vector2_array GDAPI godot_variant_get_vector2_array(godot_variant p_variant);
+godot_vector3_array GDAPI godot_variant_get_vector3_array(godot_variant p_variant);
+godot_color_array GDAPI godot_variant_get_color_array(godot_variant p_variant);
+
+
+void GDAPI godot_variant_delete(godot_variant p_variant);
+
+////// Class
+///
+
+char GDAPI **godot_class_get_list(); //get list of classes in array to array of strings, must be freed, use godot_list_free()
+
+int GDAPI godot_class_get_base(char* p_class,char *p_base,int p_max_len);
+int GDAPI godot_class_get_name(char* p_class,char *p_base,int p_max_len);
+
+char GDAPI **godot_class_get_method_list(char* p_class); //free with godot_list_free()
+int GDAPI godot_class_method_get_argument_count(char* p_class,char *p_method);
+int GDAPI godot_class_method_get_argument_type(char* p_class,char *p_method,int p_argument);
+godot_variant GDAPI godot_class_method_get_argument_default_value(char* p_class,char *p_method,int p_argument);
+
+char GDAPI **godot_class_get_constant_list(char* p_class); //free with godot_list_free()
+int GDAPI godot_class_constant_get_value(char* p_class,char *p_constant);
+
+
+////// Instance
+
+typedef int godot_call_error;
+
+#define GODOT_CALL_OK
+#define GODOT_CALL_ERROR_WRONG_ARGUMENTS
+#define GODOT_CALL_ERROR_INVALID_INSTANCE
+
+godot_instance GDAPI godot_instance_new(char* p_class);
+int GDAPI godot_instance_get_class(godot_instance p_instance,char* p_class,int p_max_len);
+
+typedef struct {
+ char *name;
+ int hint;
+ char *hint_string;
+ int usage;
+} godot_property_info;
+
+godot_call_error GDAPI godot_instance_call(godot_instance p_instance, char* p_method, ...);
+godot_call_error GDAPI godot_instance_call_ret(godot_instance p_instance, godot_variant r_return, char* p_method, ...);
+godot_bool GDAPI godot_instance_set(godot_instance p_instance, char* p_prop,godot_variant p_value);
+godot_variant GDAPI godot_instance_get(godot_instance p_instance, char* p_prop);
+
+
+#define GODOT_PROPERTY_HINT_NONE 0 ///< no hint provided.
+#define GODOT_PROPERTY_HINT_RANGE 1///< hint_text = "min,max,step,slider; //slider is optional"
+#define GODOT_PROPERTY_HINT_EXP_RANGE 2///< hint_text = "min,max,step", exponential edit
+#define GODOT_PROPERTY_HINT_ENUM 3///< hint_text= "val1,val2,val3,etc"
+#define GODOT_PROPERTY_HINT_EXP_EASING 4/// exponential easing funciton (Math::ease)
+#define GODOT_PROPERTY_HINT_LENGTH 5///< hint_text= "length" (as integer)
+#define GODOT_PROPERTY_HINT_SPRITE_FRAME 6
+#define GODOT_PROPERTY_HINT_KEY_ACCEL 7///< hint_text= "length" (as integer)
+#define GODOT_PROPERTY_HINT_FLAGS 8///< hint_text= "flag1,flag2,etc" (as bit flags)
+#define GODOT_PROPERTY_HINT_ALL_FLAGS 9
+#define GODOT_PROPERTY_HINT_FILE 10 ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
+#define GODOT_PROPERTY_HINT_DIR 11 ///< a directort path must be passed
+#define GODOT_PROPERTY_HINT_GLOBAL_FILE 12///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
+#define GODOT_PROPERTY_HINT_GLOBAL_DIR 13 ///< a directort path must be passed
+#define GODOT_PROPERTY_HINT_RESOURCE_TYPE 14///< a resource object type
+#define GODOT_PROPERTY_HINT_MULTILINE_TEXT 15///< used for string properties that can contain multiple lines
+#define GODOT_PROPERTY_HINT_COLOR_NO_ALPHA 16///< used for ignoring alpha component when editing a color
+#define GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSY 17
+#define GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS 18
+#define GODOT_PROPERTY_HINT_OBJECT_ID 19
+
+
+#define GODOT_PROPERTY_USAGE_STORAGE 1
+#define GODOT_PROPERTY_USAGE_EDITOR 2
+#define GODOT_PROPERTY_USAGE_NETWORK 4
+#define GODOT_PROPERTY_USAGE_EDITOR_HELPER 8
+#define GODOT_PROPERTY_USAGE_CHECKABLE 16 //used for editing global variables
+#define GODOT_PROPERTY_USAGE_CHECKED 32 //used for editing global variables
+#define GODOT_PROPERTY_USAGE_INTERNATIONALIZED 64 //hint for internationalized strings
+#define GODOT_PROPERTY_USAGE_BUNDLE 128 //used for optimized bundles
+#define GODOT_PROPERTY_USAGE_CATEGORY 256
+#define GODOT_PROPERTY_USAGE_STORE_IF_NONZERO 512 //only store if nonzero
+#define GODOT_PROPERTY_USAGE_STORE_IF_NONONE 1024 //only store if false
+#define GODOT_PROPERTY_USAGE_NO_INSTANCE_STATE 2048
+#define GODOT_PROPERTY_USAGE_RESTART_IF_CHANGED 4096
+#define GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE 8192
+#define GODOT_PROPERTY_USAGE_STORE_IF_NULL 16384
+#define GODOT_PROPERTY_USAGE_ANIMATE_AS_TRIGGER 32768
+
+#define GODOT_PROPERTY_USAGE_DEFAULT GODOT_PROPERTY_USAGE_STORAGE|GODOT_PROPERTY_USAGE_EDITOR|GODOT_PROPERTY_USAGE_NETWORK
+#define GODOT_PROPERTY_USAGE_DEFAULT_INTL GODOT_PROPERTY_USAGE_STORAGE|GODOT_PROPERTY_USAGE_EDITOR|GODOT_PROPERTY_USAGE_NETWORK|GODOT_PROPERTY_USAGE_INTERNATIONALIZED
+#define GODOT_PROPERTY_USAGE_NOEDITOR GODOT_PROPERTY_USAGE_STORAGE|GODOT_PROPERTY_USAGE_NETWORK
+
+
+godot_property_info GDAPI **godot_instance_get_property_list(godot_instance p_instance);
+void GDAPI godot_instance_free_property_list(godot_instance p_instance,godot_property_info** p_list);
+
+
+
+void GDAPI godot_list_free(char **p_name); //helper to free all the class list
+
+
+////// Script API
+
+typedef void* (godot_script_instance_func)(godot_instance); //passed an instance, return a pointer to your userdata
+typedef void (godot_script_free_func)(godot_instance,void*); //passed an instance, please free your userdata
+
+void GDAPI godot_script_register(char* p_base,char* p_name,godot_script_instance_func p_instance_func,godot_script_free_func p_free_func);
+void GDAPI godot_script_unregister(char* p_name);
+
+typedef GDAPI godot_variant (godot_script_func)(godot_instance,void*,godot_variant*,int); //instance,userdata,arguments,argument count. Return something or NULL. Arguments must not be freed.
+
+
+void GDAPI godot_script_add_function(char* p_name,char* p_function_name,godot_script_func p_func);
+void GDAPI godot_script_add_validated_function(char* p_name,char* p_function_name,godot_script_func p_func,int* p_arg_types,int p_arg_count,godot_variant* p_default_args,int p_default_arg_count);
+
+typedef void (godot_set_property_func)(godot_instance,void*,godot_variant); //instance,userdata,value. Value must not be freed.
+typedef godot_variant (godot_get_property_func)(godot_instance,void*); //instance,userdata. Return a value or NULL.
+
+void GDAPI godot_script_add_property(char* p_name,char* p_path,godot_set_property_func p_set_func,godot_get_property_func p_get_func);
+void GDAPI godot_script_add_listed_property(char* p_name,char* p_path,godot_set_property_func p_set_func,godot_get_property_func p_get_func,int p_type,int p_hint,char* p_hint_string,int p_usage);
+
+
+////// System Functions
+
+//using these will help Godot track how much memory is in use in debug mode
+void GDAPI *godot_alloc(int p_bytes);
+void GDAPI *godot_realloc(void* p_ptr,int p_bytes);
+void GDAPI godot_free(void* p_ptr);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // GODOT_C_H
diff --git a/modules/gdscript/gd_pretty_print.h b/modules/cscript/register_types.cpp
index 0106d873d9..267e5245ed 100644
--- a/modules/gdscript/gd_pretty_print.h
+++ b/modules/cscript/register_types.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_pretty_print.h */
+/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -26,15 +26,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_PRETTY_PRINT_H
-#define GD_PRETTY_PRINT_H
+#include "register_types.h"
+void register_cscript_types() {
+}
+void unregister_cscript_types() {
-class GDPrettyPrint {
-public:
- GDPrettyPrint();
-};
-
-#endif // GD_PRETTY_PRINT_H
+}
diff --git a/modules/gdscript/gd_pretty_print.cpp b/modules/cscript/register_types.h
index cca3cd3984..a0f41eee5d 100644
--- a/modules/gdscript/gd_pretty_print.cpp
+++ b/modules/cscript/register_types.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_pretty_print.cpp */
+/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -26,9 +26,5 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gd_pretty_print.h"
-
-GDPrettyPrint::GDPrettyPrint() {
-
-
-}
+void register_cscript_types();
+void unregister_cscript_types();
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
new file mode 100644
index 0000000000..d2bc8801e4
--- /dev/null
+++ b/modules/enet/SCsub
@@ -0,0 +1,8 @@
+Import('env')
+
+env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources,"*.c")
+#TODO: Make it possible to build against system enet
+env.Append(CPPPATH = ["#modules/enet"])
+
+Export('env')
diff --git a/modules/enet/callbacks.c b/modules/enet/callbacks.c
new file mode 100644
index 0000000000..b3990af1fb
--- /dev/null
+++ b/modules/enet/callbacks.c
@@ -0,0 +1,53 @@
+/**
+ @file callbacks.c
+ @brief ENet callback functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+static ENetCallbacks callbacks = { malloc, free, abort };
+
+int
+enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits)
+{
+ if (version < ENET_VERSION_CREATE (1, 3, 0))
+ return -1;
+
+ if (inits -> malloc != NULL || inits -> free != NULL)
+ {
+ if (inits -> malloc == NULL || inits -> free == NULL)
+ return -1;
+
+ callbacks.malloc = inits -> malloc;
+ callbacks.free = inits -> free;
+ }
+
+ if (inits -> no_memory != NULL)
+ callbacks.no_memory = inits -> no_memory;
+
+ return enet_initialize ();
+}
+
+ENetVersion
+enet_linked_version (void)
+{
+ return ENET_VERSION;
+}
+
+void *
+enet_malloc (size_t size)
+{
+ void * memory = callbacks.malloc (size);
+
+ if (memory == NULL)
+ callbacks.no_memory ();
+
+ return memory;
+}
+
+void
+enet_free (void * memory)
+{
+ callbacks.free (memory);
+}
+
diff --git a/modules/enet/compress.c b/modules/enet/compress.c
new file mode 100644
index 0000000000..784489a787
--- /dev/null
+++ b/modules/enet/compress.c
@@ -0,0 +1,654 @@
+/**
+ @file compress.c
+ @brief An adaptive order-2 PPM range coder
+*/
+#define ENET_BUILDING_LIB 1
+#include <string.h>
+#include "enet/enet.h"
+
+typedef struct _ENetSymbol
+{
+ /* binary indexed tree of symbols */
+ enet_uint8 value;
+ enet_uint8 count;
+ enet_uint16 under;
+ enet_uint16 left, right;
+
+ /* context defined by this symbol */
+ enet_uint16 symbols;
+ enet_uint16 escapes;
+ enet_uint16 total;
+ enet_uint16 parent;
+} ENetSymbol;
+
+/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */
+enum
+{
+ ENET_RANGE_CODER_TOP = 1<<24,
+ ENET_RANGE_CODER_BOTTOM = 1<<16,
+
+ ENET_CONTEXT_SYMBOL_DELTA = 3,
+ ENET_CONTEXT_SYMBOL_MINIMUM = 1,
+ ENET_CONTEXT_ESCAPE_MINIMUM = 1,
+
+ ENET_SUBCONTEXT_ORDER = 2,
+ ENET_SUBCONTEXT_SYMBOL_DELTA = 2,
+ ENET_SUBCONTEXT_ESCAPE_DELTA = 5
+};
+
+/* context exclusion roughly halves compression speed, so disable for now */
+#undef ENET_CONTEXT_EXCLUSION
+
+typedef struct _ENetRangeCoder
+{
+ /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */
+ ENetSymbol symbols[4096];
+} ENetRangeCoder;
+
+void *
+enet_range_coder_create (void)
+{
+ ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder));
+ if (rangeCoder == NULL)
+ return NULL;
+
+ return rangeCoder;
+}
+
+void
+enet_range_coder_destroy (void * context)
+{
+ ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+ if (rangeCoder == NULL)
+ return;
+
+ enet_free (rangeCoder);
+}
+
+#define ENET_SYMBOL_CREATE(symbol, value_, count_) \
+{ \
+ symbol = & rangeCoder -> symbols [nextSymbol ++]; \
+ symbol -> value = value_; \
+ symbol -> count = count_; \
+ symbol -> under = count_; \
+ symbol -> left = 0; \
+ symbol -> right = 0; \
+ symbol -> symbols = 0; \
+ symbol -> escapes = 0; \
+ symbol -> total = 0; \
+ symbol -> parent = 0; \
+}
+
+#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \
+{ \
+ ENET_SYMBOL_CREATE (context, 0, 0); \
+ (context) -> escapes = escapes_; \
+ (context) -> total = escapes_ + 256*minimum; \
+ (context) -> symbols = 0; \
+}
+
+static enet_uint16
+enet_symbol_rescale (ENetSymbol * symbol)
+{
+ enet_uint16 total = 0;
+ for (;;)
+ {
+ symbol -> count -= symbol->count >> 1;
+ symbol -> under = symbol -> count;
+ if (symbol -> left)
+ symbol -> under += enet_symbol_rescale (symbol + symbol -> left);
+ total += symbol -> under;
+ if (! symbol -> right) break;
+ symbol += symbol -> right;
+ }
+ return total;
+}
+
+#define ENET_CONTEXT_RESCALE(context, minimum) \
+{ \
+ (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \
+ (context) -> escapes -= (context) -> escapes >> 1; \
+ (context) -> total += (context) -> escapes + 256*minimum; \
+}
+
+#define ENET_RANGE_CODER_OUTPUT(value) \
+{ \
+ if (outData >= outEnd) \
+ return 0; \
+ * outData ++ = value; \
+}
+
+#define ENET_RANGE_CODER_ENCODE(under, count, total) \
+{ \
+ encodeRange /= (total); \
+ encodeLow += (under) * encodeRange; \
+ encodeRange *= (count); \
+ for (;;) \
+ { \
+ if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \
+ { \
+ if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
+ encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
+ } \
+ ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
+ encodeRange <<= 8; \
+ encodeLow <<= 8; \
+ } \
+}
+
+#define ENET_RANGE_CODER_FLUSH \
+{ \
+ while (encodeLow) \
+ { \
+ ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
+ encodeLow <<= 8; \
+ } \
+}
+
+#define ENET_RANGE_CODER_FREE_SYMBOLS \
+{ \
+ if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \
+ { \
+ nextSymbol = 0; \
+ ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \
+ predicted = 0; \
+ order = 0; \
+ } \
+}
+
+#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \
+{ \
+ under_ = value*minimum; \
+ count_ = minimum; \
+ if (! (context) -> symbols) \
+ { \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ (context) -> symbols = symbol_ - (context); \
+ } \
+ else \
+ { \
+ ENetSymbol * node = (context) + (context) -> symbols; \
+ for (;;) \
+ { \
+ if (value_ < node -> value) \
+ { \
+ node -> under += update; \
+ if (node -> left) { node += node -> left; continue; } \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ node -> left = symbol_ - node; \
+ } \
+ else \
+ if (value_ > node -> value) \
+ { \
+ under_ += node -> under; \
+ if (node -> right) { node += node -> right; continue; } \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ node -> right = symbol_ - node; \
+ } \
+ else \
+ { \
+ count_ += node -> count; \
+ under_ += node -> under - node -> count; \
+ node -> under += update; \
+ node -> count += update; \
+ symbol_ = node; \
+ } \
+ break; \
+ } \
+ } \
+}
+
+#ifdef ENET_CONTEXT_EXCLUSION
+static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#define ENET_CONTEXT_WALK(context, body) \
+{ \
+ const ENetSymbol * node = (context) + (context) -> symbols; \
+ const ENetSymbol * stack [256]; \
+ size_t stackSize = 0; \
+ while (node -> left) \
+ { \
+ stack [stackSize ++] = node; \
+ node += node -> left; \
+ } \
+ for (;;) \
+ { \
+ body; \
+ if (node -> right) \
+ { \
+ node += node -> right; \
+ while (node -> left) \
+ { \
+ stack [stackSize ++] = node; \
+ node += node -> left; \
+ } \
+ } \
+ else \
+ if (stackSize <= 0) \
+ break; \
+ else \
+ node = stack [-- stackSize]; \
+ } \
+}
+
+#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \
+ENET_CONTEXT_WALK(context, { \
+ if (node -> value != value_) \
+ { \
+ enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \
+ if (node -> value < value_) \
+ under -= parentCount; \
+ total -= parentCount; \
+ } \
+})
+#endif
+
+size_t
+enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit)
+{
+ ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+ enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
+ const enet_uint8 * inData, * inEnd;
+ enet_uint32 encodeLow = 0, encodeRange = ~0;
+ ENetSymbol * root;
+ enet_uint16 predicted = 0;
+ size_t order = 0, nextSymbol = 0;
+
+ if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0)
+ return 0;
+
+ inData = (const enet_uint8 *) inBuffers -> data;
+ inEnd = & inData [inBuffers -> dataLength];
+ inBuffers ++;
+ inBufferCount --;
+
+ ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+ for (;;)
+ {
+ ENetSymbol * subcontext, * symbol;
+#ifdef ENET_CONTEXT_EXCLUSION
+ const ENetSymbol * childContext = & emptyContext;
+#endif
+ enet_uint8 value;
+ enet_uint16 count, under, * parent = & predicted, total;
+ if (inData >= inEnd)
+ {
+ if (inBufferCount <= 0)
+ break;
+ inData = (const enet_uint8 *) inBuffers -> data;
+ inEnd = & inData [inBuffers -> dataLength];
+ inBuffers ++;
+ inBufferCount --;
+ }
+ value = * inData ++;
+
+ for (subcontext = & rangeCoder -> symbols [predicted];
+ subcontext != root;
+#ifdef ENET_CONTEXT_EXCLUSION
+ childContext = subcontext,
+#endif
+ subcontext = & rangeCoder -> symbols [subcontext -> parent])
+ {
+ ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
+ * parent = symbol - rangeCoder -> symbols;
+ parent = & symbol -> parent;
+ total = subcontext -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
+ ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0);
+#endif
+ if (count > 0)
+ {
+ ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total);
+ }
+ else
+ {
+ if (subcontext -> escapes > 0 && subcontext -> escapes < total)
+ ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total);
+ subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
+ subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
+ }
+ subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
+ if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+ ENET_CONTEXT_RESCALE (subcontext, 0);
+ if (count > 0) goto nextInput;
+ }
+
+ ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM);
+ * parent = symbol - rangeCoder -> symbols;
+ parent = & symbol -> parent;
+ total = root -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
+ ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM);
+#endif
+ ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total);
+ root -> total += ENET_CONTEXT_SYMBOL_DELTA;
+ if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+ ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+ nextInput:
+ if (order >= ENET_SUBCONTEXT_ORDER)
+ predicted = rangeCoder -> symbols [predicted].parent;
+ else
+ order ++;
+ ENET_RANGE_CODER_FREE_SYMBOLS;
+ }
+
+ ENET_RANGE_CODER_FLUSH;
+
+ return (size_t) (outData - outStart);
+}
+
+#define ENET_RANGE_CODER_SEED \
+{ \
+ if (inData < inEnd) decodeCode |= * inData ++ << 24; \
+ if (inData < inEnd) decodeCode |= * inData ++ << 16; \
+ if (inData < inEnd) decodeCode |= * inData ++ << 8; \
+ if (inData < inEnd) decodeCode |= * inData ++; \
+}
+
+#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total)))
+
+#define ENET_RANGE_CODER_DECODE(under, count, total) \
+{ \
+ decodeLow += (under) * decodeRange; \
+ decodeRange *= (count); \
+ for (;;) \
+ { \
+ if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \
+ { \
+ if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
+ decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
+ } \
+ decodeCode <<= 8; \
+ if (inData < inEnd) \
+ decodeCode |= * inData ++; \
+ decodeRange <<= 8; \
+ decodeLow <<= 8; \
+ } \
+}
+
+#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \
+{ \
+ under_ = 0; \
+ count_ = minimum; \
+ if (! (context) -> symbols) \
+ { \
+ createRoot; \
+ } \
+ else \
+ { \
+ ENetSymbol * node = (context) + (context) -> symbols; \
+ for (;;) \
+ { \
+ enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \
+ visitNode; \
+ if (code >= after) \
+ { \
+ under_ += node -> under; \
+ if (node -> right) { node += node -> right; continue; } \
+ createRight; \
+ } \
+ else \
+ if (code < after - before) \
+ { \
+ node -> under += update; \
+ if (node -> left) { node += node -> left; continue; } \
+ createLeft; \
+ } \
+ else \
+ { \
+ value_ = node -> value; \
+ count_ += node -> count; \
+ under_ = after - before; \
+ node -> under += update; \
+ node -> count += update; \
+ symbol_ = node; \
+ } \
+ break; \
+ } \
+ } \
+}
+
+#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
+ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0)
+
+#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
+ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \
+ { \
+ value_ = code / minimum; \
+ under_ = code - code%minimum; \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ (context) -> symbols = symbol_ - (context); \
+ }, \
+ exclude (node -> value, after, before), \
+ { \
+ value_ = node->value + 1 + (code - after)/minimum; \
+ under_ = code - (code - after)%minimum; \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ node -> right = symbol_ - node; \
+ }, \
+ { \
+ value_ = node->value - 1 - (after - before - code - 1)/minimum; \
+ under_ = code - (after - before - code - 1)%minimum; \
+ ENET_SYMBOL_CREATE (symbol_, value_, update); \
+ node -> left = symbol_ - node; \
+ }) \
+
+#ifdef ENET_CONTEXT_EXCLUSION
+typedef struct _ENetExclude
+{
+ enet_uint8 value;
+ enet_uint16 under;
+} ENetExclude;
+
+#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \
+{ \
+ enet_uint16 under = 0; \
+ nextExclude = excludes; \
+ ENET_CONTEXT_WALK (context, { \
+ under += rangeCoder -> symbols [node -> parent].count + minimum; \
+ nextExclude -> value = node -> value; \
+ nextExclude -> under = under; \
+ nextExclude ++; \
+ }); \
+ total -= under; \
+}
+
+#define ENET_CONTEXT_EXCLUDED(value_, after, before) \
+{ \
+ size_t low = 0, high = nextExclude - excludes; \
+ for(;;) \
+ { \
+ size_t mid = (low + high) >> 1; \
+ const ENetExclude * exclude = & excludes [mid]; \
+ if (value_ < exclude -> value) \
+ { \
+ if (low + 1 < high) \
+ { \
+ high = mid; \
+ continue; \
+ } \
+ if (exclude > excludes) \
+ after -= exclude [-1].under; \
+ } \
+ else \
+ { \
+ if (value_ > exclude -> value) \
+ { \
+ if (low + 1 < high) \
+ { \
+ low = mid; \
+ continue; \
+ } \
+ } \
+ else \
+ before = 0; \
+ after -= exclude -> under; \
+ } \
+ break; \
+ } \
+}
+#endif
+
+#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before)
+
+size_t
+enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit)
+{
+ ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+ enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
+ const enet_uint8 * inEnd = & inData [inLimit];
+ enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0;
+ ENetSymbol * root;
+ enet_uint16 predicted = 0;
+ size_t order = 0, nextSymbol = 0;
+#ifdef ENET_CONTEXT_EXCLUSION
+ ENetExclude excludes [256];
+ ENetExclude * nextExclude = excludes;
+#endif
+
+ if (rangeCoder == NULL || inLimit <= 0)
+ return 0;
+
+ ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+ ENET_RANGE_CODER_SEED;
+
+ for (;;)
+ {
+ ENetSymbol * subcontext, * symbol, * patch;
+#ifdef ENET_CONTEXT_EXCLUSION
+ const ENetSymbol * childContext = & emptyContext;
+#endif
+ enet_uint8 value = 0;
+ enet_uint16 code, under, count, bottom, * parent = & predicted, total;
+
+ for (subcontext = & rangeCoder -> symbols [predicted];
+ subcontext != root;
+#ifdef ENET_CONTEXT_EXCLUSION
+ childContext = subcontext,
+#endif
+ subcontext = & rangeCoder -> symbols [subcontext -> parent])
+ {
+ if (subcontext -> escapes <= 0)
+ continue;
+ total = subcontext -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > 0)
+ ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0);
+#endif
+ if (subcontext -> escapes >= total)
+ continue;
+ code = ENET_RANGE_CODER_READ (total);
+ if (code < subcontext -> escapes)
+ {
+ ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total);
+ continue;
+ }
+ code -= subcontext -> escapes;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > 0)
+ {
+ ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED);
+ }
+ else
+#endif
+ {
+ ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED);
+ }
+ bottom = symbol - rangeCoder -> symbols;
+ ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total);
+ subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
+ if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+ ENET_CONTEXT_RESCALE (subcontext, 0);
+ goto patchContexts;
+ }
+
+ total = root -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > 0)
+ ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM);
+#endif
+ code = ENET_RANGE_CODER_READ (total);
+ if (code < root -> escapes)
+ {
+ ENET_RANGE_CODER_DECODE (0, root -> escapes, total);
+ break;
+ }
+ code -= root -> escapes;
+#ifdef ENET_CONTEXT_EXCLUSION
+ if (childContext -> total > 0)
+ {
+ ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED);
+ }
+ else
+#endif
+ {
+ ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED);
+ }
+ bottom = symbol - rangeCoder -> symbols;
+ ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total);
+ root -> total += ENET_CONTEXT_SYMBOL_DELTA;
+ if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+ ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+ patchContexts:
+ for (patch = & rangeCoder -> symbols [predicted];
+ patch != subcontext;
+ patch = & rangeCoder -> symbols [patch -> parent])
+ {
+ ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
+ * parent = symbol - rangeCoder -> symbols;
+ parent = & symbol -> parent;
+ if (count <= 0)
+ {
+ patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
+ patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
+ }
+ patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
+ if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+ ENET_CONTEXT_RESCALE (patch, 0);
+ }
+ * parent = bottom;
+
+ ENET_RANGE_CODER_OUTPUT (value);
+
+ if (order >= ENET_SUBCONTEXT_ORDER)
+ predicted = rangeCoder -> symbols [predicted].parent;
+ else
+ order ++;
+ ENET_RANGE_CODER_FREE_SYMBOLS;
+ }
+
+ return (size_t) (outData - outStart);
+}
+
+/** @defgroup host ENet host functions
+ @{
+*/
+
+/** Sets the packet compressor the host should use to the default range coder.
+ @param host host to enable the range coder for
+ @returns 0 on success, < 0 on failure
+*/
+int
+enet_host_compress_with_range_coder (ENetHost * host)
+{
+ ENetCompressor compressor;
+ memset (& compressor, 0, sizeof (compressor));
+ compressor.context = enet_range_coder_create();
+ if (compressor.context == NULL)
+ return -1;
+ compressor.compress = enet_range_coder_compress;
+ compressor.decompress = enet_range_coder_decompress;
+ compressor.destroy = enet_range_coder_destroy;
+ enet_host_compress (host, & compressor);
+ return 0;
+}
+
+/** @} */
+
+
diff --git a/modules/enet/config.py b/modules/enet/config.py
new file mode 100644
index 0000000000..ea7e83378a
--- /dev/null
+++ b/modules/enet/config.py
@@ -0,0 +1,11 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+
diff --git a/modules/enet/enet/callbacks.h b/modules/enet/enet/callbacks.h
new file mode 100644
index 0000000000..340a4a9896
--- /dev/null
+++ b/modules/enet/enet/callbacks.h
@@ -0,0 +1,27 @@
+/**
+ @file callbacks.h
+ @brief ENet callbacks
+*/
+#ifndef __ENET_CALLBACKS_H__
+#define __ENET_CALLBACKS_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetCallbacks
+{
+ void * (ENET_CALLBACK * malloc) (size_t size);
+ void (ENET_CALLBACK * free) (void * memory);
+ void (ENET_CALLBACK * no_memory) (void);
+} ENetCallbacks;
+
+/** @defgroup callbacks ENet internal callbacks
+ @{
+ @ingroup private
+*/
+extern void * enet_malloc (size_t);
+extern void enet_free (void *);
+
+/** @} */
+
+#endif /* __ENET_CALLBACKS_H__ */
+
diff --git a/modules/enet/enet/enet.h b/modules/enet/enet/enet.h
new file mode 100644
index 0000000000..650b199ee5
--- /dev/null
+++ b/modules/enet/enet/enet.h
@@ -0,0 +1,596 @@
+/**
+ @file enet.h
+ @brief ENet public header file
+*/
+#ifndef __ENET_ENET_H__
+#define __ENET_ENET_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include "enet/win32.h"
+#else
+#include "enet/unix.h"
+#endif
+
+#include "enet/types.h"
+#include "enet/protocol.h"
+#include "enet/list.h"
+#include "enet/callbacks.h"
+
+#define ENET_VERSION_MAJOR 1
+#define ENET_VERSION_MINOR 3
+#define ENET_VERSION_PATCH 13
+#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch))
+#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF)
+#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF)
+#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF)
+#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH)
+
+typedef enet_uint32 ENetVersion;
+
+struct _ENetHost;
+struct _ENetEvent;
+struct _ENetPacket;
+
+typedef enum _ENetSocketType
+{
+ ENET_SOCKET_TYPE_STREAM = 1,
+ ENET_SOCKET_TYPE_DATAGRAM = 2
+} ENetSocketType;
+
+typedef enum _ENetSocketWait
+{
+ ENET_SOCKET_WAIT_NONE = 0,
+ ENET_SOCKET_WAIT_SEND = (1 << 0),
+ ENET_SOCKET_WAIT_RECEIVE = (1 << 1),
+ ENET_SOCKET_WAIT_INTERRUPT = (1 << 2)
+} ENetSocketWait;
+
+typedef enum _ENetSocketOption
+{
+ ENET_SOCKOPT_NONBLOCK = 1,
+ ENET_SOCKOPT_BROADCAST = 2,
+ ENET_SOCKOPT_RCVBUF = 3,
+ ENET_SOCKOPT_SNDBUF = 4,
+ ENET_SOCKOPT_REUSEADDR = 5,
+ ENET_SOCKOPT_RCVTIMEO = 6,
+ ENET_SOCKOPT_SNDTIMEO = 7,
+ ENET_SOCKOPT_ERROR = 8,
+ ENET_SOCKOPT_NODELAY = 9
+} ENetSocketOption;
+
+typedef enum _ENetSocketShutdown
+{
+ ENET_SOCKET_SHUTDOWN_READ = 0,
+ ENET_SOCKET_SHUTDOWN_WRITE = 1,
+ ENET_SOCKET_SHUTDOWN_READ_WRITE = 2
+} ENetSocketShutdown;
+
+#define ENET_HOST_ANY 0
+#define ENET_HOST_BROADCAST 0xFFFFFFFFU
+#define ENET_PORT_ANY 0
+
+/**
+ * Portable internet address structure.
+ *
+ * The host must be specified in network byte-order, and the port must be in host
+ * byte-order. The constant ENET_HOST_ANY may be used to specify the default
+ * server host. The constant ENET_HOST_BROADCAST may be used to specify the
+ * broadcast address (255.255.255.255). This makes sense for enet_host_connect,
+ * but not for enet_host_create. Once a server responds to a broadcast, the
+ * address is updated from ENET_HOST_BROADCAST to the server's actual IP address.
+ */
+typedef struct _ENetAddress
+{
+ enet_uint32 host;
+ enet_uint16 port;
+} ENetAddress;
+
+/**
+ * Packet flag bit constants.
+ *
+ * The host must be specified in network byte-order, and the port must be in
+ * host byte-order. The constant ENET_HOST_ANY may be used to specify the
+ * default server host.
+
+ @sa ENetPacket
+*/
+typedef enum _ENetPacketFlag
+{
+ /** packet must be received by the target peer and resend attempts should be
+ * made until the packet is delivered */
+ ENET_PACKET_FLAG_RELIABLE = (1 << 0),
+ /** packet will not be sequenced with other packets
+ * not supported for reliable packets
+ */
+ ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1),
+ /** packet will not allocate data, and user must supply it instead */
+ ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2),
+ /** packet will be fragmented using unreliable (instead of reliable) sends
+ * if it exceeds the MTU */
+ ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3),
+
+ /** whether the packet has been sent from all queues it has been entered into */
+ ENET_PACKET_FLAG_SENT = (1<<8)
+} ENetPacketFlag;
+
+typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *);
+
+/**
+ * ENet packet structure.
+ *
+ * An ENet data packet that may be sent to or received from a peer. The shown
+ * fields should only be read and never modified. The data field contains the
+ * allocated data for the packet. The dataLength fields specifies the length
+ * of the allocated data. The flags field is either 0 (specifying no flags),
+ * or a bitwise-or of any combination of the following flags:
+ *
+ * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer
+ * and resend attempts should be made until the packet is delivered
+ *
+ * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets
+ * (not supported for reliable packets)
+ *
+ * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead
+ *
+ * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable
+ * (instead of reliable) sends if it exceeds the MTU
+ *
+ * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into
+ @sa ENetPacketFlag
+ */
+typedef struct _ENetPacket
+{
+ size_t referenceCount; /**< internal use only */
+ enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */
+ enet_uint8 * data; /**< allocated data for packet */
+ size_t dataLength; /**< length of data */
+ ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */
+ void * userData; /**< application private data, may be freely modified */
+} ENetPacket;
+
+typedef struct _ENetAcknowledgement
+{
+ ENetListNode acknowledgementList;
+ enet_uint32 sentTime;
+ ENetProtocol command;
+} ENetAcknowledgement;
+
+typedef struct _ENetOutgoingCommand
+{
+ ENetListNode outgoingCommandList;
+ enet_uint16 reliableSequenceNumber;
+ enet_uint16 unreliableSequenceNumber;
+ enet_uint32 sentTime;
+ enet_uint32 roundTripTimeout;
+ enet_uint32 roundTripTimeoutLimit;
+ enet_uint32 fragmentOffset;
+ enet_uint16 fragmentLength;
+ enet_uint16 sendAttempts;
+ ENetProtocol command;
+ ENetPacket * packet;
+} ENetOutgoingCommand;
+
+typedef struct _ENetIncomingCommand
+{
+ ENetListNode incomingCommandList;
+ enet_uint16 reliableSequenceNumber;
+ enet_uint16 unreliableSequenceNumber;
+ ENetProtocol command;
+ enet_uint32 fragmentCount;
+ enet_uint32 fragmentsRemaining;
+ enet_uint32 * fragments;
+ ENetPacket * packet;
+} ENetIncomingCommand;
+
+typedef enum _ENetPeerState
+{
+ ENET_PEER_STATE_DISCONNECTED = 0,
+ ENET_PEER_STATE_CONNECTING = 1,
+ ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
+ ENET_PEER_STATE_CONNECTION_PENDING = 3,
+ ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
+ ENET_PEER_STATE_CONNECTED = 5,
+ ENET_PEER_STATE_DISCONNECT_LATER = 6,
+ ENET_PEER_STATE_DISCONNECTING = 7,
+ ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8,
+ ENET_PEER_STATE_ZOMBIE = 9
+} ENetPeerState;
+
+#ifndef ENET_BUFFER_MAXIMUM
+#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
+#endif
+
+enum
+{
+ ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
+ ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
+ ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
+ ENET_HOST_DEFAULT_MTU = 1400,
+ ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
+ ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
+
+ ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500,
+ ENET_PEER_DEFAULT_PACKET_THROTTLE = 32,
+ ENET_PEER_PACKET_THROTTLE_SCALE = 32,
+ ENET_PEER_PACKET_THROTTLE_COUNTER = 7,
+ ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
+ ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
+ ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000,
+ ENET_PEER_PACKET_LOSS_SCALE = (1 << 16),
+ ENET_PEER_PACKET_LOSS_INTERVAL = 10000,
+ ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024,
+ ENET_PEER_TIMEOUT_LIMIT = 32,
+ ENET_PEER_TIMEOUT_MINIMUM = 5000,
+ ENET_PEER_TIMEOUT_MAXIMUM = 30000,
+ ENET_PEER_PING_INTERVAL = 500,
+ ENET_PEER_UNSEQUENCED_WINDOWS = 64,
+ ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024,
+ ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32,
+ ENET_PEER_RELIABLE_WINDOWS = 16,
+ ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000,
+ ENET_PEER_FREE_RELIABLE_WINDOWS = 8
+};
+
+typedef struct _ENetChannel
+{
+ enet_uint16 outgoingReliableSequenceNumber;
+ enet_uint16 outgoingUnreliableSequenceNumber;
+ enet_uint16 usedReliableWindows;
+ enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS];
+ enet_uint16 incomingReliableSequenceNumber;
+ enet_uint16 incomingUnreliableSequenceNumber;
+ ENetList incomingReliableCommands;
+ ENetList incomingUnreliableCommands;
+} ENetChannel;
+
+/**
+ * An ENet peer which data packets may be sent or received from.
+ *
+ * No fields should be modified unless otherwise specified.
+ */
+typedef struct _ENetPeer
+{
+ ENetListNode dispatchList;
+ struct _ENetHost * host;
+ enet_uint16 outgoingPeerID;
+ enet_uint16 incomingPeerID;
+ enet_uint32 connectID;
+ enet_uint8 outgoingSessionID;
+ enet_uint8 incomingSessionID;
+ ENetAddress address; /**< Internet address of the peer */
+ void * data; /**< Application private data, may be freely modified */
+ ENetPeerState state;
+ ENetChannel * channels;
+ size_t channelCount; /**< Number of channels allocated for communication with peer */
+ enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */
+ enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */
+ enet_uint32 incomingBandwidthThrottleEpoch;
+ enet_uint32 outgoingBandwidthThrottleEpoch;
+ enet_uint32 incomingDataTotal;
+ enet_uint32 outgoingDataTotal;
+ enet_uint32 lastSendTime;
+ enet_uint32 lastReceiveTime;
+ enet_uint32 nextTimeout;
+ enet_uint32 earliestTimeout;
+ enet_uint32 packetLossEpoch;
+ enet_uint32 packetsSent;
+ enet_uint32 packetsLost;
+ enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
+ enet_uint32 packetLossVariance;
+ enet_uint32 packetThrottle;
+ enet_uint32 packetThrottleLimit;
+ enet_uint32 packetThrottleCounter;
+ enet_uint32 packetThrottleEpoch;
+ enet_uint32 packetThrottleAcceleration;
+ enet_uint32 packetThrottleDeceleration;
+ enet_uint32 packetThrottleInterval;
+ enet_uint32 pingInterval;
+ enet_uint32 timeoutLimit;
+ enet_uint32 timeoutMinimum;
+ enet_uint32 timeoutMaximum;
+ enet_uint32 lastRoundTripTime;
+ enet_uint32 lowestRoundTripTime;
+ enet_uint32 lastRoundTripTimeVariance;
+ enet_uint32 highestRoundTripTimeVariance;
+ enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
+ enet_uint32 roundTripTimeVariance;
+ enet_uint32 mtu;
+ enet_uint32 windowSize;
+ enet_uint32 reliableDataInTransit;
+ enet_uint16 outgoingReliableSequenceNumber;
+ ENetList acknowledgements;
+ ENetList sentReliableCommands;
+ ENetList sentUnreliableCommands;
+ ENetList outgoingReliableCommands;
+ ENetList outgoingUnreliableCommands;
+ ENetList dispatchedCommands;
+ int needsDispatch;
+ enet_uint16 incomingUnsequencedGroup;
+ enet_uint16 outgoingUnsequencedGroup;
+ enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32];
+ enet_uint32 eventData;
+ size_t totalWaitingData;
+} ENetPeer;
+
+/** An ENet packet compressor for compressing UDP packets before socket sends or receives.
+ */
+typedef struct _ENetCompressor
+{
+ /** Context data for the compressor. Must be non-NULL. */
+ void * context;
+ /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
+ size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+ /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
+ size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+ /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */
+ void (ENET_CALLBACK * destroy) (void * context);
+} ENetCompressor;
+
+/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */
+typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount);
+
+/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */
+typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event);
+
+/** An ENet host for communicating with peers.
+ *
+ * No fields should be modified unless otherwise stated.
+
+ @sa enet_host_create()
+ @sa enet_host_destroy()
+ @sa enet_host_connect()
+ @sa enet_host_service()
+ @sa enet_host_flush()
+ @sa enet_host_broadcast()
+ @sa enet_host_compress()
+ @sa enet_host_compress_with_range_coder()
+ @sa enet_host_channel_limit()
+ @sa enet_host_bandwidth_limit()
+ @sa enet_host_bandwidth_throttle()
+ */
+typedef struct _ENetHost
+{
+ ENetSocket socket;
+ ENetAddress address; /**< Internet address of the host */
+ enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */
+ enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */
+ enet_uint32 bandwidthThrottleEpoch;
+ enet_uint32 mtu;
+ enet_uint32 randomSeed;
+ int recalculateBandwidthLimits;
+ ENetPeer * peers; /**< array of peers allocated for this host */
+ size_t peerCount; /**< number of peers allocated for this host */
+ size_t channelLimit; /**< maximum number of channels allowed for connected peers */
+ enet_uint32 serviceTime;
+ ENetList dispatchQueue;
+ int continueSending;
+ size_t packetSize;
+ enet_uint16 headerFlags;
+ ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
+ size_t commandCount;
+ ENetBuffer buffers [ENET_BUFFER_MAXIMUM];
+ size_t bufferCount;
+ ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */
+ ENetCompressor compressor;
+ enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU];
+ ENetAddress receivedAddress;
+ enet_uint8 * receivedData;
+ size_t receivedDataLength;
+ enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */
+ enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */
+ enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */
+ enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */
+ ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */
+ size_t connectedPeers;
+ size_t bandwidthLimitedPeers;
+ size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */
+ size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */
+ size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */
+} ENetHost;
+
+/**
+ * An ENet event type, as specified in @ref ENetEvent.
+ */
+typedef enum _ENetEventType
+{
+ /** no event occurred within the specified time limit */
+ ENET_EVENT_TYPE_NONE = 0,
+
+ /** a connection request initiated by enet_host_connect has completed.
+ * The peer field contains the peer which successfully connected.
+ */
+ ENET_EVENT_TYPE_CONNECT = 1,
+
+ /** a peer has disconnected. This event is generated on a successful
+ * completion of a disconnect initiated by enet_peer_disconnect, if
+ * a peer has timed out, or if a connection request intialized by
+ * enet_host_connect has timed out. The peer field contains the peer
+ * which disconnected. The data field contains user supplied data
+ * describing the disconnection, or 0, if none is available.
+ */
+ ENET_EVENT_TYPE_DISCONNECT = 2,
+
+ /** a packet has been received from a peer. The peer field specifies the
+ * peer which sent the packet. The channelID field specifies the channel
+ * number upon which the packet was received. The packet field contains
+ * the packet that was received; this packet must be destroyed with
+ * enet_packet_destroy after use.
+ */
+ ENET_EVENT_TYPE_RECEIVE = 3
+} ENetEventType;
+
+/**
+ * An ENet event as returned by enet_host_service().
+
+ @sa enet_host_service
+ */
+typedef struct _ENetEvent
+{
+ ENetEventType type; /**< type of the event */
+ ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */
+ enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */
+ enet_uint32 data; /**< data associated with the event, if appropriate */
+ ENetPacket * packet; /**< packet associated with the event, if appropriate */
+} ENetEvent;
+
+/** @defgroup global ENet global functions
+ @{
+*/
+
+/**
+ Initializes ENet globally. Must be called prior to using any functions in
+ ENet.
+ @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize (void);
+
+/**
+ Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored.
+
+ @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use
+ @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults
+ @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits);
+
+/**
+ Shuts down ENet globally. Should be called when a program that has
+ initialized ENet exits.
+*/
+ENET_API void enet_deinitialize (void);
+
+/**
+ Gives the linked version of the ENet library.
+ @returns the version number
+*/
+ENET_API ENetVersion enet_linked_version (void);
+
+/** @} */
+
+/** @defgroup private ENet private implementation functions */
+
+/**
+ Returns the wall-time in milliseconds. Its initial value is unspecified
+ unless otherwise set.
+ */
+ENET_API enet_uint32 enet_time_get (void);
+/**
+ Sets the current wall-time in milliseconds.
+ */
+ENET_API void enet_time_set (enet_uint32);
+
+/** @defgroup socket ENet socket functions
+ @{
+*/
+ENET_API ENetSocket enet_socket_create (ENetSocketType);
+ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *);
+ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *);
+ENET_API int enet_socket_listen (ENetSocket, int);
+ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
+ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *);
+ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
+ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
+ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
+ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int);
+ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *);
+ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown);
+ENET_API void enet_socket_destroy (ENetSocket);
+ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32);
+
+/** @} */
+
+/** @defgroup Address ENet address functions
+ @{
+*/
+/** Attempts to resolve the host named by the parameter hostName and sets
+ the host field in the address parameter if successful.
+ @param address destination to store resolved address
+ @param hostName host name to lookup
+ @retval 0 on success
+ @retval < 0 on failure
+ @returns the address of the given hostName in address on success
+*/
+ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName);
+
+/** Gives the printable form of the IP address specified in the address parameter.
+ @param address address printed
+ @param hostName destination for name, must not be NULL
+ @param nameLength maximum length of hostName.
+ @returns the null-terminated name of the host in hostName on success
+ @retval 0 on success
+ @retval < 0 on failure
+*/
+ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength);
+
+/** Attempts to do a reverse lookup of the host field in the address parameter.
+ @param address address used for reverse lookup
+ @param hostName destination for name, must not be NULL
+ @param nameLength maximum length of hostName.
+ @returns the null-terminated name of the host in hostName on success
+ @retval 0 on success
+ @retval < 0 on failure
+*/
+ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
+
+/** @} */
+
+ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32);
+ENET_API void enet_packet_destroy (ENetPacket *);
+ENET_API int enet_packet_resize (ENetPacket *, size_t);
+ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t);
+
+ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32);
+ENET_API void enet_host_destroy (ENetHost *);
+ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32);
+ENET_API int enet_host_check_events (ENetHost *, ENetEvent *);
+ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32);
+ENET_API void enet_host_flush (ENetHost *);
+ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *);
+ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *);
+ENET_API int enet_host_compress_with_range_coder (ENetHost * host);
+ENET_API void enet_host_channel_limit (ENetHost *, size_t);
+ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
+extern void enet_host_bandwidth_throttle (ENetHost *);
+extern enet_uint32 enet_host_random_seed (void);
+
+ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
+ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
+ENET_API void enet_peer_ping (ENetPeer *);
+ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32);
+ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+ENET_API void enet_peer_reset (ENetPeer *);
+ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32);
+ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32);
+ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32);
+ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+extern int enet_peer_throttle (ENetPeer *, enet_uint32);
+extern void enet_peer_reset_queues (ENetPeer *);
+extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
+extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
+extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);
+extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16);
+extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *);
+extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *);
+extern void enet_peer_on_connect (ENetPeer *);
+extern void enet_peer_on_disconnect (ENetPeer *);
+
+ENET_API void * enet_range_coder_create (void);
+ENET_API void enet_range_coder_destroy (void *);
+ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t);
+ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t);
+
+extern size_t enet_protocol_command_size (enet_uint8);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ENET_ENET_H__ */
+
diff --git a/modules/enet/enet/list.h b/modules/enet/enet/list.h
new file mode 100644
index 0000000000..d7b2600848
--- /dev/null
+++ b/modules/enet/enet/list.h
@@ -0,0 +1,43 @@
+/**
+ @file list.h
+ @brief ENet list management
+*/
+#ifndef __ENET_LIST_H__
+#define __ENET_LIST_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetListNode
+{
+ struct _ENetListNode * next;
+ struct _ENetListNode * previous;
+} ENetListNode;
+
+typedef ENetListNode * ENetListIterator;
+
+typedef struct _ENetList
+{
+ ENetListNode sentinel;
+} ENetList;
+
+extern void enet_list_clear (ENetList *);
+
+extern ENetListIterator enet_list_insert (ENetListIterator, void *);
+extern void * enet_list_remove (ENetListIterator);
+extern ENetListIterator enet_list_move (ENetListIterator, void *, void *);
+
+extern size_t enet_list_size (ENetList *);
+
+#define enet_list_begin(list) ((list) -> sentinel.next)
+#define enet_list_end(list) (& (list) -> sentinel)
+
+#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
+
+#define enet_list_next(iterator) ((iterator) -> next)
+#define enet_list_previous(iterator) ((iterator) -> previous)
+
+#define enet_list_front(list) ((void *) (list) -> sentinel.next)
+#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
+
+#endif /* __ENET_LIST_H__ */
+
diff --git a/modules/enet/enet/protocol.h b/modules/enet/enet/protocol.h
new file mode 100644
index 0000000000..f8c73d8a66
--- /dev/null
+++ b/modules/enet/enet/protocol.h
@@ -0,0 +1,198 @@
+/**
+ @file protocol.h
+ @brief ENet protocol
+*/
+#ifndef __ENET_PROTOCOL_H__
+#define __ENET_PROTOCOL_H__
+
+#include "enet/types.h"
+
+enum
+{
+ ENET_PROTOCOL_MINIMUM_MTU = 576,
+ ENET_PROTOCOL_MAXIMUM_MTU = 4096,
+ ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
+ ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096,
+ ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536,
+ ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1,
+ ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255,
+ ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF,
+ ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024
+};
+
+typedef enum _ENetProtocolCommand
+{
+ ENET_PROTOCOL_COMMAND_NONE = 0,
+ ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1,
+ ENET_PROTOCOL_COMMAND_CONNECT = 2,
+ ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3,
+ ENET_PROTOCOL_COMMAND_DISCONNECT = 4,
+ ENET_PROTOCOL_COMMAND_PING = 5,
+ ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6,
+ ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7,
+ ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8,
+ ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9,
+ ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10,
+ ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
+ ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12,
+ ENET_PROTOCOL_COMMAND_COUNT = 13,
+
+ ENET_PROTOCOL_COMMAND_MASK = 0x0F
+} ENetProtocolCommand;
+
+typedef enum _ENetProtocolFlag
+{
+ ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7),
+ ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6),
+
+ ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14),
+ ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15),
+ ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME,
+
+ ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12),
+ ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12
+} ENetProtocolFlag;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#define ENET_PACKED
+#elif defined(__GNUC__) || defined(__clang__)
+#define ENET_PACKED __attribute__ ((packed))
+#else
+#define ENET_PACKED
+#endif
+
+typedef struct _ENetProtocolHeader
+{
+ enet_uint16 peerID;
+ enet_uint16 sentTime;
+} ENET_PACKED ENetProtocolHeader;
+
+typedef struct _ENetProtocolCommandHeader
+{
+ enet_uint8 command;
+ enet_uint8 channelID;
+ enet_uint16 reliableSequenceNumber;
+} ENET_PACKED ENetProtocolCommandHeader;
+
+typedef struct _ENetProtocolAcknowledge
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 receivedReliableSequenceNumber;
+ enet_uint16 receivedSentTime;
+} ENET_PACKED ENetProtocolAcknowledge;
+
+typedef struct _ENetProtocolConnect
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 outgoingPeerID;
+ enet_uint8 incomingSessionID;
+ enet_uint8 outgoingSessionID;
+ enet_uint32 mtu;
+ enet_uint32 windowSize;
+ enet_uint32 channelCount;
+ enet_uint32 incomingBandwidth;
+ enet_uint32 outgoingBandwidth;
+ enet_uint32 packetThrottleInterval;
+ enet_uint32 packetThrottleAcceleration;
+ enet_uint32 packetThrottleDeceleration;
+ enet_uint32 connectID;
+ enet_uint32 data;
+} ENET_PACKED ENetProtocolConnect;
+
+typedef struct _ENetProtocolVerifyConnect
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 outgoingPeerID;
+ enet_uint8 incomingSessionID;
+ enet_uint8 outgoingSessionID;
+ enet_uint32 mtu;
+ enet_uint32 windowSize;
+ enet_uint32 channelCount;
+ enet_uint32 incomingBandwidth;
+ enet_uint32 outgoingBandwidth;
+ enet_uint32 packetThrottleInterval;
+ enet_uint32 packetThrottleAcceleration;
+ enet_uint32 packetThrottleDeceleration;
+ enet_uint32 connectID;
+} ENET_PACKED ENetProtocolVerifyConnect;
+
+typedef struct _ENetProtocolBandwidthLimit
+{
+ ENetProtocolCommandHeader header;
+ enet_uint32 incomingBandwidth;
+ enet_uint32 outgoingBandwidth;
+} ENET_PACKED ENetProtocolBandwidthLimit;
+
+typedef struct _ENetProtocolThrottleConfigure
+{
+ ENetProtocolCommandHeader header;
+ enet_uint32 packetThrottleInterval;
+ enet_uint32 packetThrottleAcceleration;
+ enet_uint32 packetThrottleDeceleration;
+} ENET_PACKED ENetProtocolThrottleConfigure;
+
+typedef struct _ENetProtocolDisconnect
+{
+ ENetProtocolCommandHeader header;
+ enet_uint32 data;
+} ENET_PACKED ENetProtocolDisconnect;
+
+typedef struct _ENetProtocolPing
+{
+ ENetProtocolCommandHeader header;
+} ENET_PACKED ENetProtocolPing;
+
+typedef struct _ENetProtocolSendReliable
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendReliable;
+
+typedef struct _ENetProtocolSendUnreliable
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 unreliableSequenceNumber;
+ enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendUnreliable;
+
+typedef struct _ENetProtocolSendUnsequenced
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 unsequencedGroup;
+ enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendUnsequenced;
+
+typedef struct _ENetProtocolSendFragment
+{
+ ENetProtocolCommandHeader header;
+ enet_uint16 startSequenceNumber;
+ enet_uint16 dataLength;
+ enet_uint32 fragmentCount;
+ enet_uint32 fragmentNumber;
+ enet_uint32 totalLength;
+ enet_uint32 fragmentOffset;
+} ENET_PACKED ENetProtocolSendFragment;
+
+typedef union _ENetProtocol
+{
+ ENetProtocolCommandHeader header;
+ ENetProtocolAcknowledge acknowledge;
+ ENetProtocolConnect connect;
+ ENetProtocolVerifyConnect verifyConnect;
+ ENetProtocolDisconnect disconnect;
+ ENetProtocolPing ping;
+ ENetProtocolSendReliable sendReliable;
+ ENetProtocolSendUnreliable sendUnreliable;
+ ENetProtocolSendUnsequenced sendUnsequenced;
+ ENetProtocolSendFragment sendFragment;
+ ENetProtocolBandwidthLimit bandwidthLimit;
+ ENetProtocolThrottleConfigure throttleConfigure;
+} ENET_PACKED ENetProtocol;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
+
+#endif /* __ENET_PROTOCOL_H__ */
+
diff --git a/modules/enet/enet/time.h b/modules/enet/enet/time.h
new file mode 100644
index 0000000000..c82a546035
--- /dev/null
+++ b/modules/enet/enet/time.h
@@ -0,0 +1,18 @@
+/**
+ @file time.h
+ @brief ENet time constants and macros
+*/
+#ifndef __ENET_TIME_H__
+#define __ENET_TIME_H__
+
+#define ENET_TIME_OVERFLOW 86400000
+
+#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
+#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
+
+#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
+
+#endif /* __ENET_TIME_H__ */
+
diff --git a/modules/enet/enet/types.h b/modules/enet/enet/types.h
new file mode 100644
index 0000000000..ab010a4b13
--- /dev/null
+++ b/modules/enet/enet/types.h
@@ -0,0 +1,13 @@
+/**
+ @file types.h
+ @brief type definitions for ENet
+*/
+#ifndef __ENET_TYPES_H__
+#define __ENET_TYPES_H__
+
+typedef unsigned char enet_uint8; /**< unsigned 8-bit type */
+typedef unsigned short enet_uint16; /**< unsigned 16-bit type */
+typedef unsigned int enet_uint32; /**< unsigned 32-bit type */
+
+#endif /* __ENET_TYPES_H__ */
+
diff --git a/modules/enet/enet/unix.h b/modules/enet/enet/unix.h
new file mode 100644
index 0000000000..a59e340606
--- /dev/null
+++ b/modules/enet/enet/unix.h
@@ -0,0 +1,47 @@
+/**
+ @file unix.h
+ @brief ENet Unix header
+*/
+#ifndef __ENET_UNIX_H__
+#define __ENET_UNIX_H__
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#ifdef MSG_MAXIOVLEN
+#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN
+#endif
+
+typedef int ENetSocket;
+
+#define ENET_SOCKET_NULL -1
+
+#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
+#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
+#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
+
+typedef struct
+{
+ void * data;
+ size_t dataLength;
+} ENetBuffer;
+
+#define ENET_CALLBACK
+
+#define ENET_API extern
+
+typedef fd_set ENetSocketSet;
+
+#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
+#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
+#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
+#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
+
+#endif /* __ENET_UNIX_H__ */
+
diff --git a/modules/enet/enet/utility.h b/modules/enet/enet/utility.h
new file mode 100644
index 0000000000..e48a476be3
--- /dev/null
+++ b/modules/enet/enet/utility.h
@@ -0,0 +1,12 @@
+/**
+ @file utility.h
+ @brief ENet utility header
+*/
+#ifndef __ENET_UTILITY_H__
+#define __ENET_UTILITY_H__
+
+#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#endif /* __ENET_UTILITY_H__ */
+
diff --git a/modules/enet/enet/win32.h b/modules/enet/enet/win32.h
new file mode 100644
index 0000000000..e73ca9d052
--- /dev/null
+++ b/modules/enet/enet/win32.h
@@ -0,0 +1,57 @@
+/**
+ @file win32.h
+ @brief ENet Win32 header
+*/
+#ifndef __ENET_WIN32_H__
+#define __ENET_WIN32_H__
+
+#ifdef _MSC_VER
+#ifdef ENET_BUILDING_LIB
+#pragma warning (disable: 4267) // size_t to int conversion
+#pragma warning (disable: 4244) // 64bit to 32bit int
+#pragma warning (disable: 4018) // signed/unsigned mismatch
+#pragma warning (disable: 4146) // unary minus operator applied to unsigned type
+#endif
+#endif
+
+#include <stdlib.h>
+#include <winsock2.h>
+
+typedef SOCKET ENetSocket;
+
+#define ENET_SOCKET_NULL INVALID_SOCKET
+
+#define ENET_HOST_TO_NET_16(value) (htons (value))
+#define ENET_HOST_TO_NET_32(value) (htonl (value))
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value))
+#define ENET_NET_TO_HOST_32(value) (ntohl (value))
+
+typedef struct
+{
+ size_t dataLength;
+ void * data;
+} ENetBuffer;
+
+#define ENET_CALLBACK __cdecl
+
+#ifdef ENET_DLL
+#ifdef ENET_BUILDING_LIB
+#define ENET_API __declspec( dllexport )
+#else
+#define ENET_API __declspec( dllimport )
+#endif /* ENET_BUILDING_LIB */
+#else /* !ENET_DLL */
+#define ENET_API extern
+#endif /* ENET_DLL */
+
+typedef fd_set ENetSocketSet;
+
+#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
+#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
+#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
+#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
+
+#endif /* __ENET_WIN32_H__ */
+
+
diff --git a/modules/enet/host.c b/modules/enet/host.c
new file mode 100644
index 0000000000..3be6c0922c
--- /dev/null
+++ b/modules/enet/host.c
@@ -0,0 +1,492 @@
+/**
+ @file host.c
+ @brief ENet host management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include <string.h>
+#include "enet/enet.h"
+
+/** @defgroup host ENet host functions
+ @{
+*/
+
+/** Creates a host for communicating to peers.
+
+ @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host.
+ @param peerCount the maximum number of peers that should be allocated for the host.
+ @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
+ @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+ @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+
+ @returns the host on success and NULL on failure
+
+ @remarks ENet will strategically drop packets on specific sides of a connection between hosts
+ to ensure the host'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.
+*/
+ENetHost *
+enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+ ENetHost * host;
+ ENetPeer * currentPeer;
+
+ if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
+ return NULL;
+
+ host = (ENetHost *) enet_malloc (sizeof (ENetHost));
+ if (host == NULL)
+ return NULL;
+ memset (host, 0, sizeof (ENetHost));
+
+ host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
+ if (host -> peers == NULL)
+ {
+ enet_free (host);
+
+ return NULL;
+ }
+ memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
+
+ host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
+ if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
+ {
+ if (host -> socket != ENET_SOCKET_NULL)
+ enet_socket_destroy (host -> socket);
+
+ enet_free (host -> peers);
+ enet_free (host);
+
+ return NULL;
+ }
+
+ enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
+ enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
+ enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
+ enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
+
+ if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)
+ host -> address = * address;
+
+ if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+ channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+ else
+ if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+ channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+
+ host -> randomSeed = (enet_uint32) (size_t) host;
+ host -> randomSeed += enet_host_random_seed ();
+ host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
+ host -> channelLimit = channelLimit;
+ host -> incomingBandwidth = incomingBandwidth;
+ host -> outgoingBandwidth = outgoingBandwidth;
+ host -> bandwidthThrottleEpoch = 0;
+ host -> recalculateBandwidthLimits = 0;
+ host -> mtu = ENET_HOST_DEFAULT_MTU;
+ host -> peerCount = peerCount;
+ host -> commandCount = 0;
+ host -> bufferCount = 0;
+ host -> checksum = NULL;
+ host -> receivedAddress.host = ENET_HOST_ANY;
+ host -> receivedAddress.port = 0;
+ host -> receivedData = NULL;
+ host -> receivedDataLength = 0;
+
+ host -> totalSentData = 0;
+ host -> totalSentPackets = 0;
+ host -> totalReceivedData = 0;
+ host -> totalReceivedPackets = 0;
+
+ host -> connectedPeers = 0;
+ host -> bandwidthLimitedPeers = 0;
+ host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
+ host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
+ host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
+
+ host -> compressor.context = NULL;
+ host -> compressor.compress = NULL;
+ host -> compressor.decompress = NULL;
+ host -> compressor.destroy = NULL;
+
+ host -> intercept = NULL;
+
+ enet_list_clear (& host -> dispatchQueue);
+
+ for (currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ currentPeer -> host = host;
+ currentPeer -> incomingPeerID = currentPeer - host -> peers;
+ currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
+ currentPeer -> data = NULL;
+
+ enet_list_clear (& currentPeer -> acknowledgements);
+ enet_list_clear (& currentPeer -> sentReliableCommands);
+ enet_list_clear (& currentPeer -> sentUnreliableCommands);
+ enet_list_clear (& currentPeer -> outgoingReliableCommands);
+ enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
+ enet_list_clear (& currentPeer -> dispatchedCommands);
+
+ enet_peer_reset (currentPeer);
+ }
+
+ return host;
+}
+
+/** Destroys the host and all resources associated with it.
+ @param host pointer to the host to destroy
+*/
+void
+enet_host_destroy (ENetHost * host)
+{
+ ENetPeer * currentPeer;
+
+ if (host == NULL)
+ return;
+
+ enet_socket_destroy (host -> socket);
+
+ for (currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ enet_peer_reset (currentPeer);
+ }
+
+ if (host -> compressor.context != NULL && host -> compressor.destroy)
+ (* host -> compressor.destroy) (host -> compressor.context);
+
+ enet_free (host -> peers);
+ enet_free (host);
+}
+
+/** Initiates a connection to a foreign host.
+ @param host host seeking the connection
+ @param address destination for the connection
+ @param channelCount number of channels to allocate
+ @param data user data supplied to the receiving host
+ @returns a peer representing the foreign host on success, NULL on failure
+ @remarks The peer returned will have not completed the connection until enet_host_service()
+ notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
+*/
+ENetPeer *
+enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
+{
+ ENetPeer * currentPeer;
+ ENetChannel * channel;
+ ENetProtocol command;
+
+ if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+ channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+ else
+ if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+ channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+
+ for (currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+ break;
+ }
+
+ if (currentPeer >= & host -> peers [host -> peerCount])
+ return NULL;
+
+ currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+ if (currentPeer -> channels == NULL)
+ return NULL;
+ currentPeer -> channelCount = channelCount;
+ currentPeer -> state = ENET_PEER_STATE_CONNECTING;
+ currentPeer -> address = * address;
+ currentPeer -> connectID = ++ host -> randomSeed;
+
+ if (host -> outgoingBandwidth == 0)
+ currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+ else
+ currentPeer -> windowSize = (host -> outgoingBandwidth /
+ ENET_PEER_WINDOW_SIZE_SCALE) *
+ ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+ if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+ currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+ currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+ for (channel = currentPeer -> channels;
+ channel < & currentPeer -> channels [channelCount];
+ ++ channel)
+ {
+ channel -> outgoingReliableSequenceNumber = 0;
+ channel -> outgoingUnreliableSequenceNumber = 0;
+ channel -> incomingReliableSequenceNumber = 0;
+ channel -> incomingUnreliableSequenceNumber = 0;
+
+ enet_list_clear (& channel -> incomingReliableCommands);
+ enet_list_clear (& channel -> incomingUnreliableCommands);
+
+ channel -> usedReliableWindows = 0;
+ memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
+ }
+
+ command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ command.header.channelID = 0xFF;
+ command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+ command.connect.incomingSessionID = currentPeer -> incomingSessionID;
+ command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
+ command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
+ command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
+ command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+ command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+ command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+ command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+ command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+ command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+ command.connect.connectID = currentPeer -> connectID;
+ command.connect.data = ENET_HOST_TO_NET_32 (data);
+
+ enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
+
+ return currentPeer;
+}
+
+/** Queues a packet to be sent to all peers associated with the host.
+ @param host host on which to broadcast the packet
+ @param channelID channel on which to broadcast
+ @param packet packet to broadcast
+*/
+void
+enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
+{
+ ENetPeer * currentPeer;
+
+ for (currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+ continue;
+
+ enet_peer_send (currentPeer, channelID, packet);
+ }
+
+ if (packet -> referenceCount == 0)
+ enet_packet_destroy (packet);
+}
+
+/** Sets the packet compressor the host should use to compress and decompress packets.
+ @param host host to enable or disable compression for
+ @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
+*/
+void
+enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
+{
+ if (host -> compressor.context != NULL && host -> compressor.destroy)
+ (* host -> compressor.destroy) (host -> compressor.context);
+
+ if (compressor)
+ host -> compressor = * compressor;
+ else
+ host -> compressor.context = NULL;
+}
+
+/** Limits the maximum allowed channels of future incoming connections.
+ @param host host to limit
+ @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
+*/
+void
+enet_host_channel_limit (ENetHost * host, size_t channelLimit)
+{
+ if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+ channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+ else
+ if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+ channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+
+ host -> channelLimit = channelLimit;
+}
+
+
+/** Adjusts the bandwidth limits of a host.
+ @param host host to adjust
+ @param incomingBandwidth new incoming bandwidth
+ @param outgoingBandwidth new outgoing bandwidth
+ @remarks the incoming and outgoing bandwidth parameters are identical in function to those
+ specified in enet_host_create().
+*/
+void
+enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+ host -> incomingBandwidth = incomingBandwidth;
+ host -> outgoingBandwidth = outgoingBandwidth;
+ host -> recalculateBandwidthLimits = 1;
+}
+
+void
+enet_host_bandwidth_throttle (ENetHost * host)
+{
+ enet_uint32 timeCurrent = enet_time_get (),
+ elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
+ peersRemaining = (enet_uint32) host -> connectedPeers,
+ dataTotal = ~0,
+ bandwidth = ~0,
+ throttle = 0,
+ bandwidthLimit = 0;
+ int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
+ ENetPeer * peer;
+ ENetProtocol command;
+
+ if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+ return;
+
+ host -> bandwidthThrottleEpoch = timeCurrent;
+
+ if (peersRemaining == 0)
+ return;
+
+ if (host -> outgoingBandwidth != 0)
+ {
+ dataTotal = 0;
+ bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
+
+ for (peer = host -> peers;
+ peer < & host -> peers [host -> peerCount];
+ ++ peer)
+ {
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ continue;
+
+ dataTotal += peer -> outgoingDataTotal;
+ }
+ }
+
+ while (peersRemaining > 0 && needsAdjustment != 0)
+ {
+ needsAdjustment = 0;
+
+ if (dataTotal <= bandwidth)
+ throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+ else
+ throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+ for (peer = host -> peers;
+ peer < & host -> peers [host -> peerCount];
+ ++ peer)
+ {
+ enet_uint32 peerBandwidth;
+
+ if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+ peer -> incomingBandwidth == 0 ||
+ peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+ continue;
+
+ peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
+ if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
+ continue;
+
+ peer -> packetThrottleLimit = (peerBandwidth *
+ ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
+
+ if (peer -> packetThrottleLimit == 0)
+ peer -> packetThrottleLimit = 1;
+
+ if (peer -> packetThrottle > peer -> packetThrottleLimit)
+ peer -> packetThrottle = peer -> packetThrottleLimit;
+
+ peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
+
+ peer -> incomingDataTotal = 0;
+ peer -> outgoingDataTotal = 0;
+
+ needsAdjustment = 1;
+ -- peersRemaining;
+ bandwidth -= peerBandwidth;
+ dataTotal -= peerBandwidth;
+ }
+ }
+
+ if (peersRemaining > 0)
+ {
+ if (dataTotal <= bandwidth)
+ throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+ else
+ throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+ for (peer = host -> peers;
+ peer < & host -> peers [host -> peerCount];
+ ++ peer)
+ {
+ if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+ peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+ continue;
+
+ peer -> packetThrottleLimit = throttle;
+
+ if (peer -> packetThrottle > peer -> packetThrottleLimit)
+ peer -> packetThrottle = peer -> packetThrottleLimit;
+
+ peer -> incomingDataTotal = 0;
+ peer -> outgoingDataTotal = 0;
+ }
+ }
+
+ if (host -> recalculateBandwidthLimits)
+ {
+ host -> recalculateBandwidthLimits = 0;
+
+ peersRemaining = (enet_uint32) host -> connectedPeers;
+ bandwidth = host -> incomingBandwidth;
+ needsAdjustment = 1;
+
+ if (bandwidth == 0)
+ bandwidthLimit = 0;
+ else
+ while (peersRemaining > 0 && needsAdjustment != 0)
+ {
+ needsAdjustment = 0;
+ bandwidthLimit = bandwidth / peersRemaining;
+
+ for (peer = host -> peers;
+ peer < & host -> peers [host -> peerCount];
+ ++ peer)
+ {
+ if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+ peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+ continue;
+
+ if (peer -> outgoingBandwidth > 0 &&
+ peer -> outgoingBandwidth >= bandwidthLimit)
+ continue;
+
+ peer -> incomingBandwidthThrottleEpoch = timeCurrent;
+
+ needsAdjustment = 1;
+ -- peersRemaining;
+ bandwidth -= peer -> outgoingBandwidth;
+ }
+ }
+
+ for (peer = host -> peers;
+ peer < & host -> peers [host -> peerCount];
+ ++ peer)
+ {
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ continue;
+
+ command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ command.header.channelID = 0xFF;
+ command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+
+ if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+ command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
+ else
+ command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
+
+ enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+ }
+ }
+}
+
+/** @} */
diff --git a/modules/enet/list.c b/modules/enet/list.c
new file mode 100644
index 0000000000..1c1a8dfaaf
--- /dev/null
+++ b/modules/enet/list.c
@@ -0,0 +1,75 @@
+/**
+ @file list.c
+ @brief ENet linked list functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+/**
+ @defgroup list ENet linked list utility functions
+ @ingroup private
+ @{
+*/
+void
+enet_list_clear (ENetList * list)
+{
+ list -> sentinel.next = & list -> sentinel;
+ list -> sentinel.previous = & list -> sentinel;
+}
+
+ENetListIterator
+enet_list_insert (ENetListIterator position, void * data)
+{
+ ENetListIterator result = (ENetListIterator) data;
+
+ result -> previous = position -> previous;
+ result -> next = position;
+
+ result -> previous -> next = result;
+ position -> previous = result;
+
+ return result;
+}
+
+void *
+enet_list_remove (ENetListIterator position)
+{
+ position -> previous -> next = position -> next;
+ position -> next -> previous = position -> previous;
+
+ return position;
+}
+
+ENetListIterator
+enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast)
+{
+ ENetListIterator first = (ENetListIterator) dataFirst,
+ last = (ENetListIterator) dataLast;
+
+ first -> previous -> next = last -> next;
+ last -> next -> previous = first -> previous;
+
+ first -> previous = position -> previous;
+ last -> next = position;
+
+ first -> previous -> next = first;
+ position -> previous = last;
+
+ return first;
+}
+
+size_t
+enet_list_size (ENetList * list)
+{
+ size_t size = 0;
+ ENetListIterator position;
+
+ for (position = enet_list_begin (list);
+ position != enet_list_end (list);
+ position = enet_list_next (position))
+ ++ size;
+
+ return size;
+}
+
+/** @} */
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
new file mode 100644
index 0000000000..18a4347edf
--- /dev/null
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -0,0 +1,650 @@
+#include "os/os.h"
+#include "io/marshalls.h"
+#include "networked_multiplayer_enet.h"
+
+void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) {
+
+ transfer_mode=p_mode;
+}
+
+void NetworkedMultiplayerENet::set_target_peer(int p_peer){
+
+ target_peer=p_peer;
+}
+
+int NetworkedMultiplayerENet::get_packet_peer() const{
+
+ ERR_FAIL_COND_V(!active,1);
+ ERR_FAIL_COND_V(incoming_packets.size()==0,1);
+
+ return incoming_packets.front()->get().from;
+
+}
+
+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);
+
+ ENetAddress address;
+ address.host = bind_ip;
+
+ address.port = p_port;
+
+ 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 */,
+ 2 /* allow up to 2 channels to be used, 0 and 1 */,
+ p_in_bandwidth /* assume any amount of incoming bandwidth */,
+ p_out_bandwidth /* assume any amount of outgoing bandwidth */);
+
+ ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
+
+ _setup_compressor();
+ active=true;
+ server=true;
+ refuse_connections=false;
+ unique_id=1;
+ 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){
+
+ ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE);
+
+ host = enet_host_create (NULL /* create a client host */,
+ 1 /* only allow 1 outgoing connection */,
+ 2 /* allow up 2 channels to be used, 0 and 1 */,
+ p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */,
+ p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */);
+
+ ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
+
+
+ _setup_compressor();
+
+ ENetAddress address;
+ address.host=p_ip.host;
+ address.port=p_port;
+
+ //enet_address_set_host (& address, "localhost");
+ //address.port = p_port;
+
+ unique_id=_gen_unique_id();
+
+ /* Initiate the connection, allocating the two channels 0 and 1. */
+ ENetPeer *peer = enet_host_connect (host, & address, 2, 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.
+
+ connection_status=CONNECTION_CONNECTING;
+ active=true;
+ server=false;
+ refuse_connections=false;
+
+ return OK;
+}
+
+void NetworkedMultiplayerENet::poll(){
+
+ ERR_FAIL_COND(!active);
+
+ _pop_current_packet();
+
+ ENetEvent event;
+ /* Wait up to 1000 milliseconds for an event. */
+ while (true) {
+
+ 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?
+ break;
+ } else if (ret==0) {
+ break;
+ }
+
+ switch (event.type)
+ {
+ case ENET_EVENT_TYPE_CONNECT: {
+ /* Store any relevant client information here. */
+
+ if (server && refuse_connections) {
+ enet_peer_reset(event.peer);
+ break;
+ }
+
+ IP_Address ip;
+ ip.host=event.peer -> address.host;
+
+ 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
+ *new_id=1;
+ }
+
+ event.peer->data=new_id;
+
+ peer_map[*new_id]=event.peer;
+
+ connection_status=CONNECTION_CONNECTED; //if connecting, this means it connected t something!
+
+ emit_signal("peer_connected",*new_id);
+
+ if (server) {
+ //someone connected, let it know of 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
+ 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,1,packet);
+ //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]);
+ enet_peer_send(E->get(),1,packet);
+ }
+ } else {
+
+ emit_signal("connection_succeeded");
+ }
+
+ } break;
+ case ENET_EVENT_TYPE_DISCONNECT: {
+
+ /* Reset the peer's client information. */
+
+ int *id = (int*)event.peer -> data;
+
+
+
+ if (!id) {
+ if (!server) {
+ emit_signal("connection_failed");
+ }
+ } else {
+
+ if (server) {
+ //someone disconnected, let it know to 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]);
+ enet_peer_send(E->get(),1,packet);
+ }
+ } else if (!server) {
+ emit_signal("server_disconnected");
+ close_connection();
+ return;
+ }
+
+ emit_signal("peer_disconnected",*id);
+ peer_map.erase(*id);
+ memdelete( id );
+
+ }
+
+
+ } break;
+ case ENET_EVENT_TYPE_RECEIVE: {
+
+
+ if (event.channelID==1) {
+ //some config message
+ ERR_CONTINUE( event.packet->dataLength < 8);
+
+ int msg = decode_uint32(&event.packet->data[0]);
+ int id = decode_uint32(&event.packet->data[4]);
+
+ switch(msg) {
+ case SYSMSG_ADD_PEER: {
+
+ peer_map[id]=NULL;
+ emit_signal("peer_connected",id);
+
+ } break;
+ case SYSMSG_REMOVE_PEER: {
+
+ peer_map.erase(id);
+ emit_signal("peer_disconnected",id);
+ } break;
+ }
+
+ enet_packet_destroy(event.packet);
+ } else if (event.channelID==0){
+
+ Packet packet;
+ packet.packet = event.packet;
+
+ int *id = (int*)event.peer -> data;
+
+ ERR_CONTINUE(event.packet->dataLength<12)
+
+
+ uint32_t source = decode_uint32(&event.packet->data[0]);
+ int target = decode_uint32(&event.packet->data[4]);
+ uint32_t flags = decode_uint32(&event.packet->data[8]);
+
+ packet.from=source;
+
+ if (server) {
+
+ packet.from=*id;
+
+ if (target==0) {
+ //re-send the everyone but sender :|
+
+ incoming_packets.push_back(packet);
+ //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
+ continue;
+
+ ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags);
+
+ enet_peer_send(E->get(),0,packet2);
+ }
+
+ } else if (target<0) {
+ //to all but one
+
+ //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
+ continue;
+
+ ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags);
+
+ enet_peer_send(E->get(),0,packet2);
+ }
+
+ if (-target != 1) {
+ //server is not excluded
+ incoming_packets.push_back(packet);
+ } else {
+ //server is excluded, erase packet
+ enet_packet_destroy(packet.packet);
+ }
+
+ } else if (target==1) {
+ //to myself and only myself
+ incoming_packets.push_back(packet);
+ } else {
+ //to someone else, specifically
+ ERR_CONTINUE(!peer_map.has(target));
+ enet_peer_send(peer_map[target],0,packet.packet);
+ }
+ } else {
+
+ incoming_packets.push_back(packet);
+ }
+
+
+ //destroy packet later..
+ } else {
+ ERR_CONTINUE(true);
+ }
+
+
+ }break;
+ case ENET_EVENT_TYPE_NONE: {
+ //do nothing
+ } break;
+ }
+ }
+}
+
+bool NetworkedMultiplayerENet::is_server() const {
+ ERR_FAIL_COND_V(!active,false);
+
+ return server;
+}
+
+void NetworkedMultiplayerENet::close_connection() {
+
+ if (!active)
+ return;
+
+ _pop_current_packet();
+
+ bool peers_disconnected=false;
+ for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) {
+ if (E->get()) {
+ enet_peer_disconnect_now(E->get(),unique_id);
+ peers_disconnected=true;
+ }
+ }
+
+ if (peers_disconnected) {
+ enet_host_flush(host);
+ OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send
+
+ }
+
+ enet_host_destroy(host);
+ active=false;
+ incoming_packets.clear();
+ unique_id=1; //server is 1
+ connection_status=CONNECTION_DISCONNECTED;
+}
+
+int NetworkedMultiplayerENet::get_available_packet_count() const {
+
+ return incoming_packets.size();
+}
+Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{
+
+ ERR_FAIL_COND_V(incoming_packets.size()==0,ERR_UNAVAILABLE);
+
+ _pop_current_packet();
+
+ current_packet = incoming_packets.front()->get();
+ incoming_packets.pop_front();
+
+ *r_buffer=(const uint8_t*)(&current_packet.packet->data[12]);
+ r_buffer_size=current_packet.packet->dataLength;
+
+ return OK;
+}
+Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_size){
+
+ ERR_FAIL_COND_V(!active,ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(connection_status!=CONNECTION_CONNECTED,ERR_UNCONFIGURED);
+
+ int packet_flags=0;
+
+ switch(transfer_mode) {
+ case TRANSFER_MODE_UNRELIABLE: {
+ packet_flags=ENET_PACKET_FLAG_UNSEQUENCED;
+ } break;
+ case TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ packet_flags=0;
+ } break;
+ case TRANSFER_MODE_RELIABLE: {
+ packet_flags=ENET_PACKET_FLAG_RELIABLE;
+ } break;
+ }
+
+ Map<int,ENetPeer*>::Element *E=NULL;
+
+ if (target_peer!=0) {
+
+ E = peer_map.find(ABS(target_peer));
+ if (!E) {
+ ERR_EXPLAIN("Invalid Target Peer: "+itos(target_peer));
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
+ }
+ }
+
+ 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
+ copymem(&packet->data[12],p_buffer,p_buffer_size);
+
+ if (server) {
+
+ if (target_peer==0) {
+ enet_host_broadcast(host,0,packet);
+ } else if (target_peer<0) {
+ //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
+ continue;
+
+ ENetPacket* packet2 = enet_packet_create (packet->data,packet->dataLength,packet_flags);
+
+ enet_peer_send(F->get(),0,packet2);
+ }
+
+ enet_packet_destroy(packet); //original packet no longer needed
+ } else {
+ enet_peer_send (E->get(), 0, packet);
+
+ }
+ } else {
+
+ ERR_FAIL_COND_V(!peer_map.has(1),ERR_BUG);
+ enet_peer_send (peer_map[1], 0, packet); //send to server for broadcast..
+
+ }
+
+ enet_host_flush(host);
+
+ return OK;
+}
+
+int NetworkedMultiplayerENet::get_max_packet_size() const {
+
+ return 1<<24; //anything is good
+}
+
+void NetworkedMultiplayerENet::_pop_current_packet() const {
+
+ if (current_packet.packet) {
+ enet_packet_destroy(current_packet.packet);
+ current_packet.packet=NULL;
+ current_packet.from=0;
+ }
+
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus NetworkedMultiplayerENet::get_connection_status() const {
+
+ return connection_status;
+}
+
+uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
+
+ uint32_t hash = 0;
+
+ while (hash==0 || hash==1) {
+
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec() );
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash );
+ hash = hash_djb2_one_32(
+ (uint32_t)OS::get_singleton()->get_data_dir().hash64(), hash );
+ //hash = hash_djb2_one_32(
+ // (uint32_t)OS::get_singleton()->get_unique_ID().hash64(), hash );
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)this), hash ); //rely on aslr heap
+ hash = hash_djb2_one_32(
+ (uint32_t)((uint64_t)&hash), hash ); //rely on aslr stack
+
+ hash=hash&0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
+ }
+
+ return hash;
+}
+
+int NetworkedMultiplayerENet::get_unique_id() const {
+
+ ERR_FAIL_COND_V(!active,0);
+ return unique_id;
+}
+
+void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) {
+
+ refuse_connections=p_enable;
+}
+
+bool NetworkedMultiplayerENet::is_refusing_new_connections() const {
+
+ return refuse_connections;
+}
+
+void NetworkedMultiplayerENet::set_compression_mode(CompressionMode p_mode) {
+
+ compression_mode=p_mode;
+}
+
+NetworkedMultiplayerENet::CompressionMode NetworkedMultiplayerENet::get_compression_mode() const{
+
+ return compression_mode;
+}
+
+size_t NetworkedMultiplayerENet::enet_compress(void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) {
+
+ NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet*)(context);
+
+ if (size_t(enet->src_compressor_mem.size())<inLimit) {
+ enet->src_compressor_mem.resize( inLimit );
+ }
+
+ int total = inLimit;
+ int ofs=0;
+ while(total) {
+ for(size_t i=0;i<inBufferCount;i++) {
+ int to_copy = MIN(total,int(inBuffers[i].dataLength));
+ copymem(&enet->src_compressor_mem[ofs],inBuffers[i].data,to_copy);
+ ofs+=to_copy;
+ total-=to_copy;
+ }
+ }
+
+ Compression::Mode mode;
+
+ switch(enet->compression_mode) {
+ case COMPRESS_FASTLZ: {
+ mode=Compression::MODE_FASTLZ;
+ } break;
+ case COMPRESS_ZLIB: {
+ mode=Compression::MODE_DEFLATE;
+ } break;
+ default: { ERR_FAIL_V(0); }
+ }
+
+ int req_size = Compression::get_max_compressed_buffer_size(ofs,mode);
+ if (enet->dst_compressor_mem.size()<req_size) {
+ enet->dst_compressor_mem.resize(req_size);
+ }
+ int ret=Compression::compress(enet->dst_compressor_mem.ptr(),enet->src_compressor_mem.ptr(),ofs,mode);
+
+ if (ret<0)
+ return 0;
+
+
+ if (ret>int(outLimit))
+ return 0; //do not bother
+
+ copymem(outData,enet->dst_compressor_mem.ptr(),ret);
+
+ return ret;
+}
+
+size_t NetworkedMultiplayerENet::enet_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit){
+
+ NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet*)(context);
+ int ret = -1;
+ switch(enet->compression_mode) {
+ case COMPRESS_FASTLZ: {
+
+ ret=Compression::decompress(outData,outLimit,inData,inLimit,Compression::MODE_FASTLZ);
+ } break;
+ case COMPRESS_ZLIB: {
+
+ ret=Compression::decompress(outData,outLimit,inData,inLimit,Compression::MODE_DEFLATE);
+ } break;
+ default: {}
+ }
+ if (ret<0) {
+ return 0;
+ } else {
+ return ret;
+ }
+}
+
+void NetworkedMultiplayerENet::_setup_compressor() {
+
+ switch(compression_mode) {
+
+ case COMPRESS_NONE: {
+
+ enet_host_compress(host,NULL);
+ } break;
+ case COMPRESS_RANGE_CODER: {
+ enet_host_compress_with_range_coder(host);
+ } break;
+ case COMPRESS_FASTLZ:
+ case COMPRESS_ZLIB: {
+
+ enet_host_compress(host,&enet_compressor);
+ } break;
+ }
+}
+
+void NetworkedMultiplayerENet::enet_compressor_destroy(void * context){
+
+ //do none
+}
+
+
+void NetworkedMultiplayerENet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("create_server","port","max_clients","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_server,DEFVAL(32),DEFVAL(0),DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("create_client","ip","port","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_client,DEFVAL(0),DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("close_connection"),&NetworkedMultiplayerENet::close_connection);
+ ObjectTypeDB::bind_method(_MD("set_compression_mode","mode"),&NetworkedMultiplayerENet::set_compression_mode);
+ ObjectTypeDB::bind_method(_MD("get_compression_mode"),&NetworkedMultiplayerENet::get_compression_mode);
+ ObjectTypeDB::bind_method(_MD("set_bind_ip", "ip"),&NetworkedMultiplayerENet::set_bind_ip);
+
+ BIND_CONSTANT( COMPRESS_NONE );
+ BIND_CONSTANT( COMPRESS_RANGE_CODER );
+ BIND_CONSTANT( COMPRESS_FASTLZ );
+ BIND_CONSTANT( COMPRESS_ZLIB );
+}
+
+
+NetworkedMultiplayerENet::NetworkedMultiplayerENet(){
+
+ active=false;
+ server=false;
+ refuse_connections=false;
+ unique_id=0;
+ target_peer=0;
+ current_packet.packet=NULL;
+ transfer_mode=TRANSFER_MODE_RELIABLE;
+ connection_status=CONNECTION_DISCONNECTED;
+ compression_mode=COMPRESS_NONE;
+ enet_compressor.context=this;
+ enet_compressor.compress=enet_compress;
+ enet_compressor.decompress=enet_decompress;
+ enet_compressor.destroy=enet_compressor_destroy;
+
+ bind_ip=ENET_HOST_ANY;
+}
+
+NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){
+
+ close_connection();
+}
+
+// sets IP for ENet to bind when using create_server
+// if no IP is set, then ENet bind to ENET_HOST_ANY
+void NetworkedMultiplayerENet::set_bind_ip(const IP_Address& p_ip){
+ bind_ip=p_ip.host;
+}
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
new file mode 100644
index 0000000000..59863c1f78
--- /dev/null
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -0,0 +1,114 @@
+#ifndef NETWORKED_MULTIPLAYER_ENET_H
+#define NETWORKED_MULTIPLAYER_ENET_H
+
+#include "io/networked_multiplayer_peer.h"
+#include "enet/enet.h"
+#include "io/compression.h"
+
+class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
+
+ OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer)
+public:
+ enum CompressionMode {
+ COMPRESS_NONE,
+ COMPRESS_RANGE_CODER,
+ COMPRESS_FASTLZ,
+ COMPRESS_ZLIB
+ };
+private:
+
+
+ enum {
+ SYSMSG_ADD_PEER,
+ SYSMSG_REMOVE_PEER
+ };
+
+ bool active;
+ bool server;
+
+ uint32_t unique_id;
+
+ int target_peer;
+ TransferMode transfer_mode;
+
+ ENetEvent event;
+ ENetPeer *peer;
+ ENetHost *host;
+
+ bool refuse_connections;
+
+ ConnectionStatus connection_status;
+
+ Map<int,ENetPeer*> peer_map;
+
+ struct Packet {
+
+ ENetPacket *packet;
+ int from;
+ };
+
+ CompressionMode compression_mode;
+
+ mutable List<Packet> incoming_packets;
+
+ mutable Packet current_packet;
+
+ uint32_t _gen_unique_id() const;
+ void _pop_current_packet() const;
+
+ Vector<uint8_t> src_compressor_mem;
+ Vector<uint8_t> dst_compressor_mem;
+
+ ENetCompressor enet_compressor;
+ static size_t enet_compress(void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+ static size_t enet_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+ static void enet_compressor_destroy(void * context);
+ void _setup_compressor();
+
+ enet_uint32 bind_ip;
+protected:
+ static void _bind_methods();
+public:
+
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual void set_target_peer(int p_peer);
+
+
+ virtual int get_packet_peer() const;
+
+
+ Error create_server(int p_port, int p_max_peers=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);
+
+ void close_connection();
+
+ virtual void poll();
+
+ virtual bool is_server() const;
+
+ virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size);
+
+ virtual int get_max_packet_size() const;
+
+ virtual ConnectionStatus get_connection_status() const;
+
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual int get_unique_id() const;
+
+ void set_compression_mode(CompressionMode p_mode);
+ CompressionMode get_compression_mode() const;
+
+ NetworkedMultiplayerENet();
+ ~NetworkedMultiplayerENet();
+
+ void set_bind_ip(const IP_Address& p_ip);
+};
+
+VARIANT_ENUM_CAST(NetworkedMultiplayerENet::CompressionMode);
+
+
+#endif // NETWORKED_MULTIPLAYER_ENET_H
diff --git a/modules/enet/packet.c b/modules/enet/packet.c
new file mode 100644
index 0000000000..5fa78b28ae
--- /dev/null
+++ b/modules/enet/packet.c
@@ -0,0 +1,165 @@
+/**
+ @file packet.c
+ @brief ENet packet management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+/** @defgroup Packet ENet packet functions
+ @{
+*/
+
+/** Creates a packet that may be sent to a peer.
+ @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL.
+ @param dataLength size of the data allocated for this packet
+ @param flags flags for this packet as described for the ENetPacket structure.
+ @returns the packet on success, NULL on failure
+*/
+ENetPacket *
+enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
+{
+ ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
+ if (packet == NULL)
+ return NULL;
+
+ if (flags & ENET_PACKET_FLAG_NO_ALLOCATE)
+ packet -> data = (enet_uint8 *) data;
+ else
+ if (dataLength <= 0)
+ packet -> data = NULL;
+ else
+ {
+ packet -> data = (enet_uint8 *) enet_malloc (dataLength);
+ if (packet -> data == NULL)
+ {
+ enet_free (packet);
+ return NULL;
+ }
+
+ if (data != NULL)
+ memcpy (packet -> data, data, dataLength);
+ }
+
+ packet -> referenceCount = 0;
+ packet -> flags = flags;
+ packet -> dataLength = dataLength;
+ packet -> freeCallback = NULL;
+ packet -> userData = NULL;
+
+ return packet;
+}
+
+/** Destroys the packet and deallocates its data.
+ @param packet packet to be destroyed
+*/
+void
+enet_packet_destroy (ENetPacket * packet)
+{
+ if (packet == NULL)
+ return;
+
+ if (packet -> freeCallback != NULL)
+ (* packet -> freeCallback) (packet);
+ if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) &&
+ packet -> data != NULL)
+ enet_free (packet -> data);
+ enet_free (packet);
+}
+
+/** Attempts to resize the data in the packet to length specified in the
+ dataLength parameter
+ @param packet packet to resize
+ @param dataLength new size for the packet data
+ @returns 0 on success, < 0 on failure
+*/
+int
+enet_packet_resize (ENetPacket * packet, size_t dataLength)
+{
+ enet_uint8 * newData;
+
+ if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE))
+ {
+ packet -> dataLength = dataLength;
+
+ return 0;
+ }
+
+ newData = (enet_uint8 *) enet_malloc (dataLength);
+ if (newData == NULL)
+ return -1;
+
+ memcpy (newData, packet -> data, packet -> dataLength);
+ enet_free (packet -> data);
+
+ packet -> data = newData;
+ packet -> dataLength = dataLength;
+
+ return 0;
+}
+
+static int initializedCRC32 = 0;
+static enet_uint32 crcTable [256];
+
+static enet_uint32
+reflect_crc (int val, int bits)
+{
+ int result = 0, bit;
+
+ for (bit = 0; bit < bits; bit ++)
+ {
+ if(val & 1) result |= 1 << (bits - 1 - bit);
+ val >>= 1;
+ }
+
+ return result;
+}
+
+static void
+initialize_crc32 (void)
+{
+ int byte;
+
+ for (byte = 0; byte < 256; ++ byte)
+ {
+ enet_uint32 crc = reflect_crc (byte, 8) << 24;
+ int offset;
+
+ for(offset = 0; offset < 8; ++ offset)
+ {
+ if (crc & 0x80000000)
+ crc = (crc << 1) ^ 0x04c11db7;
+ else
+ crc <<= 1;
+ }
+
+ crcTable [byte] = reflect_crc (crc, 32);
+ }
+
+ initializedCRC32 = 1;
+}
+
+enet_uint32
+enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
+{
+ enet_uint32 crc = 0xFFFFFFFF;
+
+ if (! initializedCRC32) initialize_crc32 ();
+
+ while (bufferCount -- > 0)
+ {
+ const enet_uint8 * data = (const enet_uint8 *) buffers -> data,
+ * dataEnd = & data [buffers -> dataLength];
+
+ while (data < dataEnd)
+ {
+ crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
+ }
+
+ ++ buffers;
+ }
+
+ return ENET_HOST_TO_NET_32 (~ crc);
+}
+
+/** @} */
diff --git a/modules/enet/peer.c b/modules/enet/peer.c
new file mode 100644
index 0000000000..e2d0872bd3
--- /dev/null
+++ b/modules/enet/peer.c
@@ -0,0 +1,1004 @@
+/**
+ @file peer.c
+ @brief ENet peer management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+/** @defgroup peer ENet peer functions
+ @{
+*/
+
+/** Configures throttle parameter for a peer.
+
+ Unreliable packets are dropped by ENet in response to the varying conditions
+ of the Internet connection to the peer. The throttle represents a probability
+ that an unreliable packet should not be dropped and thus sent by ENet to the peer.
+ The lowest mean round trip time from the sending of a reliable packet to the
+ receipt of its acknowledgement is measured over an amount of time specified by
+ the interval parameter in milliseconds. If a measured round trip time happens to
+ be significantly less than the mean round trip time measured over the interval,
+ then the throttle probability is increased to allow more traffic by an amount
+ specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+ constant. If a measured round trip time happens to be significantly greater than
+ the mean round trip time measured over the interval, then the throttle probability
+ is decreased to limit traffic by an amount specified in the deceleration parameter, which
+ is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has
+ a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by
+ ENet, and so 100% of all unreliable packets will be sent. When the throttle has a
+ value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable
+ packets will be sent. Intermediate values for the throttle represent intermediate
+ probabilities between 0% and 100% of unreliable packets being sent. The bandwidth
+ limits of the local and foreign hosts are taken into account to determine a
+ sensible limit for the throttle probability above which it should not raise even in
+ the best of conditions.
+
+ @param peer peer to configure
+ @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL.
+ @param acceleration rate at which to increase the throttle probability as mean RTT declines
+ @param deceleration rate at which to decrease the throttle probability as mean RTT increases
+*/
+void
+enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration)
+{
+ ENetProtocol command;
+
+ peer -> packetThrottleInterval = interval;
+ peer -> packetThrottleAcceleration = acceleration;
+ peer -> packetThrottleDeceleration = deceleration;
+
+ command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ command.header.channelID = 0xFF;
+
+ command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval);
+ command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration);
+ command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration);
+
+ enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+int
+enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
+{
+ if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance)
+ {
+ peer -> packetThrottle = peer -> packetThrottleLimit;
+ }
+ else
+ if (rtt < peer -> lastRoundTripTime)
+ {
+ peer -> packetThrottle += peer -> packetThrottleAcceleration;
+
+ if (peer -> packetThrottle > peer -> packetThrottleLimit)
+ peer -> packetThrottle = peer -> packetThrottleLimit;
+
+ return 1;
+ }
+ else
+ if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
+ {
+ if (peer -> packetThrottle > peer -> packetThrottleDeceleration)
+ peer -> packetThrottle -= peer -> packetThrottleDeceleration;
+ else
+ peer -> packetThrottle = 0;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Queues a packet to be sent.
+ @param peer destination for the packet
+ @param channelID channel on which to send
+ @param packet packet to send
+ @retval 0 on success
+ @retval < 0 on failure
+*/
+int
+enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
+{
+ ENetChannel * channel = & peer -> channels [channelID];
+ ENetProtocol command;
+ size_t fragmentLength;
+
+ if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+ channelID >= peer -> channelCount ||
+ packet -> dataLength > peer -> host -> maximumPacketSize)
+ return -1;
+
+ fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
+ if (peer -> host -> checksum != NULL)
+ fragmentLength -= sizeof(enet_uint32);
+
+ if (packet -> dataLength > fragmentLength)
+ {
+ enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength,
+ fragmentNumber,
+ fragmentOffset;
+ enet_uint8 commandNumber;
+ enet_uint16 startSequenceNumber;
+ ENetList fragments;
+ ENetOutgoingCommand * fragment;
+
+ if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
+ return -1;
+
+ if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT &&
+ channel -> outgoingUnreliableSequenceNumber < 0xFFFF)
+ {
+ commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT;
+ startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1);
+ }
+ else
+ {
+ commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1);
+ }
+
+ enet_list_clear (& fragments);
+
+ for (fragmentNumber = 0,
+ fragmentOffset = 0;
+ fragmentOffset < packet -> dataLength;
+ ++ fragmentNumber,
+ fragmentOffset += fragmentLength)
+ {
+ if (packet -> dataLength - fragmentOffset < fragmentLength)
+ fragmentLength = packet -> dataLength - fragmentOffset;
+
+ fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+ if (fragment == NULL)
+ {
+ while (! enet_list_empty (& fragments))
+ {
+ fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
+
+ enet_free (fragment);
+ }
+
+ return -1;
+ }
+
+ fragment -> fragmentOffset = fragmentOffset;
+ fragment -> fragmentLength = fragmentLength;
+ fragment -> packet = packet;
+ fragment -> command.header.command = commandNumber;
+ fragment -> command.header.channelID = channelID;
+ fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber;
+ fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength);
+ fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount);
+ fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber);
+ fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength);
+ fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset);
+
+ enet_list_insert (enet_list_end (& fragments), fragment);
+ }
+
+ packet -> referenceCount += fragmentNumber;
+
+ while (! enet_list_empty (& fragments))
+ {
+ fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
+
+ enet_peer_setup_outgoing_command (peer, fragment);
+ }
+
+ return 0;
+ }
+
+ command.header.channelID = channelID;
+
+ if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED)
+ {
+ command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
+ command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+ }
+ else
+ if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF)
+ {
+ command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+ }
+ else
+ {
+ command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE;
+ command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+ }
+
+ if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL)
+ return -1;
+
+ return 0;
+}
+
+/** Attempts to dequeue any incoming queued packet.
+ @param peer peer to dequeue packets from
+ @param channelID holds the channel ID of the channel the packet was received on success
+ @returns a pointer to the packet, or NULL if there are no available incoming queued packets
+*/
+ENetPacket *
+enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID)
+{
+ ENetIncomingCommand * incomingCommand;
+ ENetPacket * packet;
+
+ if (enet_list_empty (& peer -> dispatchedCommands))
+ return NULL;
+
+ incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands));
+
+ if (channelID != NULL)
+ * channelID = incomingCommand -> command.header.channelID;
+
+ packet = incomingCommand -> packet;
+
+ -- packet -> referenceCount;
+
+ if (incomingCommand -> fragments != NULL)
+ enet_free (incomingCommand -> fragments);
+
+ enet_free (incomingCommand);
+
+ peer -> totalWaitingData -= packet -> dataLength;
+
+ return packet;
+}
+
+static void
+enet_peer_reset_outgoing_commands (ENetList * queue)
+{
+ ENetOutgoingCommand * outgoingCommand;
+
+ while (! enet_list_empty (queue))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue));
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ -- outgoingCommand -> packet -> referenceCount;
+
+ if (outgoingCommand -> packet -> referenceCount == 0)
+ enet_packet_destroy (outgoingCommand -> packet);
+ }
+
+ enet_free (outgoingCommand);
+ }
+}
+
+static void
+enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand)
+{
+ ENetListIterator currentCommand;
+
+ for (currentCommand = startCommand; currentCommand != endCommand; )
+ {
+ ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ currentCommand = enet_list_next (currentCommand);
+
+ enet_list_remove (& incomingCommand -> incomingCommandList);
+
+ if (incomingCommand -> packet != NULL)
+ {
+ -- incomingCommand -> packet -> referenceCount;
+
+ if (incomingCommand -> packet -> referenceCount == 0)
+ enet_packet_destroy (incomingCommand -> packet);
+ }
+
+ if (incomingCommand -> fragments != NULL)
+ enet_free (incomingCommand -> fragments);
+
+ enet_free (incomingCommand);
+ }
+}
+
+static void
+enet_peer_reset_incoming_commands (ENetList * queue)
+{
+ enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue));
+}
+
+void
+enet_peer_reset_queues (ENetPeer * peer)
+{
+ ENetChannel * channel;
+
+ if (peer -> needsDispatch)
+ {
+ enet_list_remove (& peer -> dispatchList);
+
+ peer -> needsDispatch = 0;
+ }
+
+ while (! enet_list_empty (& peer -> acknowledgements))
+ enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
+
+ enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
+ enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
+ enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands);
+ enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands);
+ enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
+
+ if (peer -> channels != NULL && peer -> channelCount > 0)
+ {
+ for (channel = peer -> channels;
+ channel < & peer -> channels [peer -> channelCount];
+ ++ channel)
+ {
+ enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);
+ enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);
+ }
+
+ enet_free (peer -> channels);
+ }
+
+ peer -> channels = NULL;
+ peer -> channelCount = 0;
+}
+
+void
+enet_peer_on_connect (ENetPeer * peer)
+{
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ {
+ if (peer -> incomingBandwidth != 0)
+ ++ peer -> host -> bandwidthLimitedPeers;
+
+ ++ peer -> host -> connectedPeers;
+ }
+}
+
+void
+enet_peer_on_disconnect (ENetPeer * peer)
+{
+ if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+ {
+ if (peer -> incomingBandwidth != 0)
+ -- peer -> host -> bandwidthLimitedPeers;
+
+ -- peer -> host -> connectedPeers;
+ }
+}
+
+/** Forcefully disconnects a peer.
+ @param peer peer to forcefully disconnect
+ @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout
+ on its connection to the local host.
+*/
+void
+enet_peer_reset (ENetPeer * peer)
+{
+ enet_peer_on_disconnect (peer);
+
+ peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID;
+ peer -> connectID = 0;
+
+ peer -> state = ENET_PEER_STATE_DISCONNECTED;
+
+ peer -> incomingBandwidth = 0;
+ peer -> outgoingBandwidth = 0;
+ peer -> incomingBandwidthThrottleEpoch = 0;
+ peer -> outgoingBandwidthThrottleEpoch = 0;
+ peer -> incomingDataTotal = 0;
+ peer -> outgoingDataTotal = 0;
+ peer -> lastSendTime = 0;
+ peer -> lastReceiveTime = 0;
+ peer -> nextTimeout = 0;
+ peer -> earliestTimeout = 0;
+ peer -> packetLossEpoch = 0;
+ peer -> packetsSent = 0;
+ peer -> packetsLost = 0;
+ peer -> packetLoss = 0;
+ peer -> packetLossVariance = 0;
+ peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;
+ peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;
+ peer -> packetThrottleCounter = 0;
+ peer -> packetThrottleEpoch = 0;
+ peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;
+ peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;
+ peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;
+ peer -> pingInterval = ENET_PEER_PING_INTERVAL;
+ peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT;
+ peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM;
+ peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM;
+ peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+ peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+ peer -> lastRoundTripTimeVariance = 0;
+ peer -> highestRoundTripTimeVariance = 0;
+ peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+ peer -> roundTripTimeVariance = 0;
+ peer -> mtu = peer -> host -> mtu;
+ peer -> reliableDataInTransit = 0;
+ peer -> outgoingReliableSequenceNumber = 0;
+ peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+ peer -> incomingUnsequencedGroup = 0;
+ peer -> outgoingUnsequencedGroup = 0;
+ peer -> eventData = 0;
+ peer -> totalWaitingData = 0;
+
+ memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+
+ enet_peer_reset_queues (peer);
+}
+
+/** Sends a ping request to a peer.
+ @param peer destination for the ping request
+ @remarks ping requests factor into the mean round trip time as designated by the
+ roundTripTime field in the ENetPeer structure. ENet automatically pings all connected
+ peers at regular intervals, however, this function may be called to ensure more
+ frequent ping requests.
+*/
+void
+enet_peer_ping (ENetPeer * peer)
+{
+ ENetProtocol command;
+
+ if (peer -> state != ENET_PEER_STATE_CONNECTED)
+ return;
+
+ command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ command.header.channelID = 0xFF;
+
+ enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+/** Sets the interval at which pings will be sent to a peer.
+
+ Pings are used both to monitor the liveness of the connection and also to dynamically
+ adjust the throttle during periods of low traffic so that the throttle has reasonable
+ responsiveness during traffic spikes.
+
+ @param peer the peer to adjust
+ @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0
+*/
+void
+enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval)
+{
+ peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL;
+}
+
+/** Sets the timeout parameters for a peer.
+
+ The timeout parameter control how and when a peer will timeout from a failure to acknowledge
+ reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable
+ packet is not acknowledge within some multiple of the average RTT plus a variance tolerance,
+ the timeout will be doubled until it reaches a set limit. If the timeout is thus at this
+ limit and reliable packets have been sent but not acknowledged within a certain minimum time
+ period, the peer will be disconnected. Alternatively, if reliable packets have been sent
+ but not acknowledged for a certain maximum time period, the peer will be disconnected regardless
+ of the current timeout limit value.
+
+ @param peer the peer to adjust
+ @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0
+ @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0
+ @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0
+*/
+
+void
+enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum)
+{
+ peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT;
+ peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM;
+ peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM;
+}
+
+/** Force an immediate disconnection from a peer.
+ @param peer peer to disconnect
+ @param data data describing the disconnection
+ @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not
+ guaranteed to receive the disconnect notification, and is reset immediately upon
+ return from this function.
+*/
+void
+enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data)
+{
+ ENetProtocol command;
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECTED)
+ return;
+
+ if (peer -> state != ENET_PEER_STATE_ZOMBIE &&
+ peer -> state != ENET_PEER_STATE_DISCONNECTING)
+ {
+ enet_peer_reset_queues (peer);
+
+ command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
+ command.header.channelID = 0xFF;
+ command.disconnect.data = ENET_HOST_TO_NET_32 (data);
+
+ enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+ enet_host_flush (peer -> host);
+ }
+
+ enet_peer_reset (peer);
+}
+
+/** Request a disconnection from a peer.
+ @param peer peer to request a disconnection
+ @param data data describing the disconnection
+ @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+ once the disconnection is complete.
+*/
+void
+enet_peer_disconnect (ENetPeer * peer, enet_uint32 data)
+{
+ ENetProtocol command;
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECTING ||
+ peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+ peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT ||
+ peer -> state == ENET_PEER_STATE_ZOMBIE)
+ return;
+
+ enet_peer_reset_queues (peer);
+
+ command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+ command.header.channelID = 0xFF;
+ command.disconnect.data = ENET_HOST_TO_NET_32 (data);
+
+ if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+ command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ else
+ command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
+
+ enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+ if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+ {
+ enet_peer_on_disconnect (peer);
+
+ peer -> state = ENET_PEER_STATE_DISCONNECTING;
+ }
+ else
+ {
+ enet_host_flush (peer -> host);
+ enet_peer_reset (peer);
+ }
+}
+
+/** Request a disconnection from a peer, but only after all queued outgoing packets are sent.
+ @param peer peer to request a disconnection
+ @param data data describing the disconnection
+ @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+ once the disconnection is complete.
+*/
+void
+enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
+{
+ if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) &&
+ ! (enet_list_empty (& peer -> outgoingReliableCommands) &&
+ enet_list_empty (& peer -> outgoingUnreliableCommands) &&
+ enet_list_empty (& peer -> sentReliableCommands)))
+ {
+ peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
+ peer -> eventData = data;
+ }
+ else
+ enet_peer_disconnect (peer, data);
+}
+
+ENetAcknowledgement *
+enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime)
+{
+ ENetAcknowledgement * acknowledgement;
+
+ if (command -> header.channelID < peer -> channelCount)
+ {
+ ENetChannel * channel = & peer -> channels [command -> header.channelID];
+ enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
+ currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+ if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+ if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS)
+ return NULL;
+ }
+
+ acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement));
+ if (acknowledgement == NULL)
+ return NULL;
+
+ peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge);
+
+ acknowledgement -> sentTime = sentTime;
+ acknowledgement -> command = * command;
+
+ enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement);
+
+ return acknowledgement;
+}
+
+void
+enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand)
+{
+ ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
+
+ peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength;
+
+ if (outgoingCommand -> command.header.channelID == 0xFF)
+ {
+ ++ peer -> outgoingReliableSequenceNumber;
+
+ outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber;
+ outgoingCommand -> unreliableSequenceNumber = 0;
+ }
+ else
+ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+ {
+ ++ channel -> outgoingReliableSequenceNumber;
+ channel -> outgoingUnreliableSequenceNumber = 0;
+
+ outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+ outgoingCommand -> unreliableSequenceNumber = 0;
+ }
+ else
+ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
+ {
+ ++ peer -> outgoingUnsequencedGroup;
+
+ outgoingCommand -> reliableSequenceNumber = 0;
+ outgoingCommand -> unreliableSequenceNumber = 0;
+ }
+ else
+ {
+ if (outgoingCommand -> fragmentOffset == 0)
+ ++ channel -> outgoingUnreliableSequenceNumber;
+
+ outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+ outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+ }
+
+ outgoingCommand -> sendAttempts = 0;
+ outgoingCommand -> sentTime = 0;
+ outgoingCommand -> roundTripTimeout = 0;
+ outgoingCommand -> roundTripTimeoutLimit = 0;
+ outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber);
+
+ switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK)
+ {
+ case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+ outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber);
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+ outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
+ break;
+
+ default:
+ break;
+ }
+
+ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+ enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand);
+ else
+ enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand);
+}
+
+ENetOutgoingCommand *
+enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length)
+{
+ ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+ if (outgoingCommand == NULL)
+ return NULL;
+
+ outgoingCommand -> command = * command;
+ outgoingCommand -> fragmentOffset = offset;
+ outgoingCommand -> fragmentLength = length;
+ outgoingCommand -> packet = packet;
+ if (packet != NULL)
+ ++ packet -> referenceCount;
+
+ enet_peer_setup_outgoing_command (peer, outgoingCommand);
+
+ return outgoingCommand;
+}
+
+void
+enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel)
+{
+ ENetListIterator droppedCommand, startCommand, currentCommand;
+
+ for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands);
+ currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+ currentCommand = enet_list_next (currentCommand))
+ {
+ ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+ continue;
+
+ if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
+ {
+ if (incomingCommand -> fragmentsRemaining <= 0)
+ {
+ channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber;
+ continue;
+ }
+
+ if (startCommand != currentCommand)
+ {
+ enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+ if (! peer -> needsDispatch)
+ {
+ enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+ peer -> needsDispatch = 1;
+ }
+
+ droppedCommand = currentCommand;
+ }
+ else
+ if (droppedCommand != currentCommand)
+ droppedCommand = enet_list_previous (currentCommand);
+ }
+ else
+ {
+ enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
+ currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+ if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+ break;
+
+ droppedCommand = enet_list_next (currentCommand);
+
+ if (startCommand != currentCommand)
+ {
+ enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+ if (! peer -> needsDispatch)
+ {
+ enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+ peer -> needsDispatch = 1;
+ }
+ }
+ }
+
+ startCommand = enet_list_next (currentCommand);
+ }
+
+ if (startCommand != currentCommand)
+ {
+ enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+ if (! peer -> needsDispatch)
+ {
+ enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+ peer -> needsDispatch = 1;
+ }
+
+ droppedCommand = currentCommand;
+ }
+
+ enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand);
+}
+
+void
+enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel)
+{
+ ENetListIterator currentCommand;
+
+ for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands);
+ currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+ currentCommand = enet_list_next (currentCommand))
+ {
+ ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if (incomingCommand -> fragmentsRemaining > 0 ||
+ incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1))
+ break;
+
+ channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber;
+
+ if (incomingCommand -> fragmentCount > 0)
+ channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1;
+ }
+
+ if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands))
+ return;
+
+ channel -> incomingUnreliableSequenceNumber = 0;
+
+ enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand));
+
+ if (! peer -> needsDispatch)
+ {
+ enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+ peer -> needsDispatch = 1;
+ }
+
+ if (! enet_list_empty (& channel -> incomingUnreliableCommands))
+ enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
+}
+
+ENetIncomingCommand *
+enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount)
+{
+ static ENetIncomingCommand dummyCommand;
+
+ ENetChannel * channel = & peer -> channels [command -> header.channelID];
+ enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0;
+ enet_uint16 reliableWindow, currentWindow;
+ ENetIncomingCommand * incomingCommand;
+ ENetListIterator currentCommand;
+ ENetPacket * packet = NULL;
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+ goto discardCommand;
+
+ if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+ {
+ reliableSequenceNumber = command -> header.reliableSequenceNumber;
+ reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+ if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+ if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+ goto discardCommand;
+ }
+
+ switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
+ {
+ case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+ case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+ if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
+ goto discardCommand;
+
+ for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+ currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+ currentCommand = enet_list_previous (currentCommand))
+ {
+ incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ continue;
+ }
+ else
+ if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+ break;
+
+ goto discardCommand;
+ }
+ }
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+ case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
+ unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber);
+
+ if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber &&
+ unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+ goto discardCommand;
+
+ for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+ currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+ currentCommand = enet_list_previous (currentCommand))
+ {
+ incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+ continue;
+
+ if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ continue;
+ }
+ else
+ if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
+ continue;
+
+ if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber)
+ {
+ if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber)
+ break;
+
+ goto discardCommand;
+ }
+ }
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+ currentCommand = enet_list_end (& channel -> incomingUnreliableCommands);
+ break;
+
+ default:
+ goto discardCommand;
+ }
+
+ if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData)
+ goto notifyError;
+
+ packet = enet_packet_create (data, dataLength, flags);
+ if (packet == NULL)
+ goto notifyError;
+
+ incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand));
+ if (incomingCommand == NULL)
+ goto notifyError;
+
+ incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber;
+ incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF;
+ incomingCommand -> command = * command;
+ incomingCommand -> fragmentCount = fragmentCount;
+ incomingCommand -> fragmentsRemaining = fragmentCount;
+ incomingCommand -> packet = packet;
+ incomingCommand -> fragments = NULL;
+
+ if (fragmentCount > 0)
+ {
+ if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
+ incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32));
+ if (incomingCommand -> fragments == NULL)
+ {
+ enet_free (incomingCommand);
+
+ goto notifyError;
+ }
+ memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32));
+ }
+
+ if (packet != NULL)
+ {
+ ++ packet -> referenceCount;
+
+ peer -> totalWaitingData += packet -> dataLength;
+ }
+
+ enet_list_insert (enet_list_next (currentCommand), incomingCommand);
+
+ switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
+ {
+ case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+ case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+ enet_peer_dispatch_incoming_reliable_commands (peer, channel);
+ break;
+
+ default:
+ enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
+ break;
+ }
+
+ return incomingCommand;
+
+discardCommand:
+ if (fragmentCount > 0)
+ goto notifyError;
+
+ if (packet != NULL && packet -> referenceCount == 0)
+ enet_packet_destroy (packet);
+
+ return & dummyCommand;
+
+notifyError:
+ if (packet != NULL && packet -> referenceCount == 0)
+ enet_packet_destroy (packet);
+
+ return NULL;
+}
+
+/** @} */
diff --git a/modules/enet/protocol.c b/modules/enet/protocol.c
new file mode 100644
index 0000000000..4a2a4ed185
--- /dev/null
+++ b/modules/enet/protocol.c
@@ -0,0 +1,1914 @@
+/**
+ @file protocol.c
+ @brief ENet protocol functions
+*/
+#include <stdio.h>
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet/utility.h"
+#include "enet/time.h"
+#include "enet/enet.h"
+
+
+static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
+{
+ 0,
+ sizeof (ENetProtocolAcknowledge),
+ sizeof (ENetProtocolConnect),
+ sizeof (ENetProtocolVerifyConnect),
+ sizeof (ENetProtocolDisconnect),
+ sizeof (ENetProtocolPing),
+ sizeof (ENetProtocolSendReliable),
+ sizeof (ENetProtocolSendUnreliable),
+ sizeof (ENetProtocolSendFragment),
+ sizeof (ENetProtocolSendUnsequenced),
+ sizeof (ENetProtocolBandwidthLimit),
+ sizeof (ENetProtocolThrottleConfigure),
+ sizeof (ENetProtocolSendFragment)
+};
+
+size_t
+enet_protocol_command_size (enet_uint8 commandNumber)
+{
+ return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK];
+}
+
+static void
+enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
+{
+ if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER)
+ enet_peer_on_connect (peer);
+ else
+ enet_peer_on_disconnect (peer);
+
+ peer -> state = state;
+}
+
+static void
+enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
+{
+ enet_protocol_change_state (host, peer, state);
+
+ if (! peer -> needsDispatch)
+ {
+ enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
+
+ peer -> needsDispatch = 1;
+ }
+}
+
+static int
+enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+ while (! enet_list_empty (& host -> dispatchQueue))
+ {
+ ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue));
+
+ peer -> needsDispatch = 0;
+
+ switch (peer -> state)
+ {
+ case ENET_PEER_STATE_CONNECTION_PENDING:
+ case ENET_PEER_STATE_CONNECTION_SUCCEEDED:
+ enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
+
+ event -> type = ENET_EVENT_TYPE_CONNECT;
+ event -> peer = peer;
+ event -> data = peer -> eventData;
+
+ return 1;
+
+ case ENET_PEER_STATE_ZOMBIE:
+ host -> recalculateBandwidthLimits = 1;
+
+ event -> type = ENET_EVENT_TYPE_DISCONNECT;
+ event -> peer = peer;
+ event -> data = peer -> eventData;
+
+ enet_peer_reset (peer);
+
+ return 1;
+
+ case ENET_PEER_STATE_CONNECTED:
+ if (enet_list_empty (& peer -> dispatchedCommands))
+ continue;
+
+ event -> packet = enet_peer_receive (peer, & event -> channelID);
+ if (event -> packet == NULL)
+ continue;
+
+ event -> type = ENET_EVENT_TYPE_RECEIVE;
+ event -> peer = peer;
+
+ if (! enet_list_empty (& peer -> dispatchedCommands))
+ {
+ peer -> needsDispatch = 1;
+
+ enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
+ }
+
+ return 1;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void
+enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+ host -> recalculateBandwidthLimits = 1;
+
+ if (event != NULL)
+ {
+ enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
+
+ event -> type = ENET_EVENT_TYPE_CONNECT;
+ event -> peer = peer;
+ event -> data = peer -> eventData;
+ }
+ else
+ enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING);
+}
+
+static void
+enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+ if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING)
+ host -> recalculateBandwidthLimits = 1;
+
+ if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED)
+ enet_peer_reset (peer);
+ else
+ if (event != NULL)
+ {
+ event -> type = ENET_EVENT_TYPE_DISCONNECT;
+ event -> peer = peer;
+ event -> data = 0;
+
+ enet_peer_reset (peer);
+ }
+ else
+ {
+ peer -> eventData = 0;
+
+ enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+ }
+}
+
+static void
+enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
+{
+ ENetOutgoingCommand * outgoingCommand;
+
+ while (! enet_list_empty (& peer -> sentUnreliableCommands))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
+
+ enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ -- outgoingCommand -> packet -> referenceCount;
+
+ if (outgoingCommand -> packet -> referenceCount == 0)
+ {
+ outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
+
+ enet_packet_destroy (outgoingCommand -> packet);
+ }
+ }
+
+ enet_free (outgoingCommand);
+ }
+}
+
+static ENetProtocolCommand
+enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
+{
+ ENetOutgoingCommand * outgoingCommand = NULL;
+ ENetListIterator currentCommand;
+ ENetProtocolCommand commandNumber;
+ int wasSent = 1;
+
+ for (currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+ currentCommand != enet_list_end (& peer -> sentReliableCommands);
+ currentCommand = enet_list_next (currentCommand))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+ outgoingCommand -> command.header.channelID == channelID)
+ break;
+ }
+
+ if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
+ {
+ for (currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
+ currentCommand != enet_list_end (& peer -> outgoingReliableCommands);
+ currentCommand = enet_list_next (currentCommand))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE;
+
+ if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+ outgoingCommand -> command.header.channelID == channelID)
+ break;
+ }
+
+ if (currentCommand == enet_list_end (& peer -> outgoingReliableCommands))
+ return ENET_PROTOCOL_COMMAND_NONE;
+
+ wasSent = 0;
+ }
+
+ if (outgoingCommand == NULL)
+ return ENET_PROTOCOL_COMMAND_NONE;
+
+ if (channelID < peer -> channelCount)
+ {
+ ENetChannel * channel = & peer -> channels [channelID];
+ enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ if (channel -> reliableWindows [reliableWindow] > 0)
+ {
+ -- channel -> reliableWindows [reliableWindow];
+ if (! channel -> reliableWindows [reliableWindow])
+ channel -> usedReliableWindows &= ~ (1 << reliableWindow);
+ }
+ }
+
+ commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK);
+
+ enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ if (wasSent)
+ peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+ -- outgoingCommand -> packet -> referenceCount;
+
+ if (outgoingCommand -> packet -> referenceCount == 0)
+ {
+ outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
+
+ enet_packet_destroy (outgoingCommand -> packet);
+ }
+ }
+
+ enet_free (outgoingCommand);
+
+ if (enet_list_empty (& peer -> sentReliableCommands))
+ return commandNumber;
+
+ outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands);
+
+ peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+
+ return commandNumber;
+}
+
+static ENetPeer *
+enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command)
+{
+ enet_uint8 incomingSessionID, outgoingSessionID;
+ enet_uint32 mtu, windowSize;
+ ENetChannel * channel;
+ size_t channelCount, duplicatePeers = 0;
+ ENetPeer * currentPeer, * peer = NULL;
+ ENetProtocol verifyCommand;
+
+ channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
+
+ if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT ||
+ channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+ return NULL;
+
+ for (currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+ {
+ if (peer == NULL)
+ peer = currentPeer;
+ }
+ else
+ if (currentPeer -> state != ENET_PEER_STATE_CONNECTING &&
+ currentPeer -> address.host == host -> receivedAddress.host)
+ {
+ if (currentPeer -> address.port == host -> receivedAddress.port &&
+ currentPeer -> connectID == command -> connect.connectID)
+ return NULL;
+
+ ++ duplicatePeers;
+ }
+ }
+
+ if (peer == NULL || duplicatePeers >= host -> duplicatePeers)
+ return NULL;
+
+ if (channelCount > host -> channelLimit)
+ channelCount = host -> channelLimit;
+ peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+ if (peer -> channels == NULL)
+ return NULL;
+ peer -> channelCount = channelCount;
+ peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
+ peer -> connectID = command -> connect.connectID;
+ peer -> address = host -> receivedAddress;
+ peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
+ peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
+ peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
+ peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval);
+ peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration);
+ peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration);
+ peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data);
+
+ incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID;
+ incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+ if (incomingSessionID == peer -> outgoingSessionID)
+ incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+ peer -> outgoingSessionID = incomingSessionID;
+
+ outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID;
+ outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+ if (outgoingSessionID == peer -> incomingSessionID)
+ outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+ peer -> incomingSessionID = outgoingSessionID;
+
+ for (channel = peer -> channels;
+ channel < & peer -> channels [channelCount];
+ ++ channel)
+ {
+ channel -> outgoingReliableSequenceNumber = 0;
+ channel -> outgoingUnreliableSequenceNumber = 0;
+ channel -> incomingReliableSequenceNumber = 0;
+ channel -> incomingUnreliableSequenceNumber = 0;
+
+ enet_list_clear (& channel -> incomingReliableCommands);
+ enet_list_clear (& channel -> incomingUnreliableCommands);
+
+ channel -> usedReliableWindows = 0;
+ memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
+ }
+
+ mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu);
+
+ if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+ mtu = ENET_PROTOCOL_MINIMUM_MTU;
+ else
+ if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+ mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+ peer -> mtu = mtu;
+
+ if (host -> outgoingBandwidth == 0 &&
+ peer -> incomingBandwidth == 0)
+ peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+ else
+ if (host -> outgoingBandwidth == 0 ||
+ peer -> incomingBandwidth == 0)
+ peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) /
+ ENET_PEER_WINDOW_SIZE_SCALE) *
+ ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) /
+ ENET_PEER_WINDOW_SIZE_SCALE) *
+ ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+ if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+ peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+ peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+ if (host -> incomingBandwidth == 0)
+ windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+ else
+ windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) *
+ ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+ if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize))
+ windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize);
+
+ if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+ windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+ windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+ verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+ verifyCommand.header.channelID = 0xFF;
+ verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID);
+ verifyCommand.verifyConnect.incomingSessionID = incomingSessionID;
+ verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID;
+ verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu);
+ verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize);
+ verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+ verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+ verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+ verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval);
+ verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration);
+ verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration);
+ verifyCommand.verifyConnect.connectID = peer -> connectID;
+
+ enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0);
+
+ return peer;
+}
+
+static int
+enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+ size_t dataLength;
+
+ if (command -> header.channelID >= peer -> channelCount ||
+ (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+ return -1;
+
+ dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength);
+ * currentData += dataLength;
+ if (dataLength > host -> maximumPacketSize ||
+ * currentData < host -> receivedData ||
+ * currentData > & host -> receivedData [host -> receivedDataLength])
+ return -1;
+
+ if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+ enet_uint32 unsequencedGroup, index;
+ size_t dataLength;
+
+ if (command -> header.channelID >= peer -> channelCount ||
+ (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+ return -1;
+
+ dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength);
+ * currentData += dataLength;
+ if (dataLength > host -> maximumPacketSize ||
+ * currentData < host -> receivedData ||
+ * currentData > & host -> receivedData [host -> receivedDataLength])
+ return -1;
+
+ unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup);
+ index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE;
+
+ if (unsequencedGroup < peer -> incomingUnsequencedGroup)
+ unsequencedGroup += 0x10000;
+
+ if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE)
+ return 0;
+
+ unsequencedGroup &= 0xFFFF;
+
+ if (unsequencedGroup - index != peer -> incomingUnsequencedGroup)
+ {
+ peer -> incomingUnsequencedGroup = unsequencedGroup - index;
+
+ memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+ }
+ else
+ if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32)))
+ return 0;
+
+ if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL)
+ return -1;
+
+ peer -> unsequencedWindow [index / 32] |= 1 << (index % 32);
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+ size_t dataLength;
+
+ if (command -> header.channelID >= peer -> channelCount ||
+ (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+ return -1;
+
+ dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength);
+ * currentData += dataLength;
+ if (dataLength > host -> maximumPacketSize ||
+ * currentData < host -> receivedData ||
+ * currentData > & host -> receivedData [host -> receivedDataLength])
+ return -1;
+
+ if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+ enet_uint32 fragmentNumber,
+ fragmentCount,
+ fragmentOffset,
+ fragmentLength,
+ startSequenceNumber,
+ totalLength;
+ ENetChannel * channel;
+ enet_uint16 startWindow, currentWindow;
+ ENetListIterator currentCommand;
+ ENetIncomingCommand * startCommand = NULL;
+
+ if (command -> header.channelID >= peer -> channelCount ||
+ (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+ return -1;
+
+ fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
+ * currentData += fragmentLength;
+ if (fragmentLength > host -> maximumPacketSize ||
+ * currentData < host -> receivedData ||
+ * currentData > & host -> receivedData [host -> receivedDataLength])
+ return -1;
+
+ channel = & peer -> channels [command -> header.channelID];
+ startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
+ startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+ if (startSequenceNumber < channel -> incomingReliableSequenceNumber)
+ startWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+ if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+ return 0;
+
+ fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+ fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+ fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+ totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+
+ if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
+ fragmentNumber >= fragmentCount ||
+ totalLength > host -> maximumPacketSize ||
+ fragmentOffset >= totalLength ||
+ fragmentLength > totalLength - fragmentOffset)
+ return -1;
+
+ for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+ currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+ currentCommand = enet_list_previous (currentCommand))
+ {
+ ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if (startSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ continue;
+ }
+ else
+ if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < startSequenceNumber)
+ break;
+
+ if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT ||
+ totalLength != incomingCommand -> packet -> dataLength ||
+ fragmentCount != incomingCommand -> fragmentCount)
+ return -1;
+
+ startCommand = incomingCommand;
+ break;
+ }
+ }
+
+ if (startCommand == NULL)
+ {
+ ENetProtocol hostCommand = * command;
+
+ hostCommand.header.reliableSequenceNumber = startSequenceNumber;
+
+ startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount);
+ if (startCommand == NULL)
+ return -1;
+ }
+
+ if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
+ {
+ -- startCommand -> fragmentsRemaining;
+
+ startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
+
+ if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+ fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+ memcpy (startCommand -> packet -> data + fragmentOffset,
+ (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
+ fragmentLength);
+
+ if (startCommand -> fragmentsRemaining <= 0)
+ enet_peer_dispatch_incoming_reliable_commands (peer, channel);
+ }
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+ enet_uint32 fragmentNumber,
+ fragmentCount,
+ fragmentOffset,
+ fragmentLength,
+ reliableSequenceNumber,
+ startSequenceNumber,
+ totalLength;
+ enet_uint16 reliableWindow, currentWindow;
+ ENetChannel * channel;
+ ENetListIterator currentCommand;
+ ENetIncomingCommand * startCommand = NULL;
+
+ if (command -> header.channelID >= peer -> channelCount ||
+ (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+ return -1;
+
+ fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
+ * currentData += fragmentLength;
+ if (fragmentLength > host -> maximumPacketSize ||
+ * currentData < host -> receivedData ||
+ * currentData > & host -> receivedData [host -> receivedDataLength])
+ return -1;
+
+ channel = & peer -> channels [command -> header.channelID];
+ reliableSequenceNumber = command -> header.reliableSequenceNumber;
+ startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
+
+ reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+ if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+ if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+ return 0;
+
+ if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber &&
+ startSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+ return 0;
+
+ fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+ fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+ fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+ totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+
+ if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
+ fragmentNumber >= fragmentCount ||
+ totalLength > host -> maximumPacketSize ||
+ fragmentOffset >= totalLength ||
+ fragmentLength > totalLength - fragmentOffset)
+ return -1;
+
+ for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+ currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+ currentCommand = enet_list_previous (currentCommand))
+ {
+ ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+ if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ {
+ if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+ continue;
+ }
+ else
+ if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+ break;
+
+ if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
+ continue;
+
+ if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber)
+ {
+ if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber)
+ break;
+
+ if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT ||
+ totalLength != incomingCommand -> packet -> dataLength ||
+ fragmentCount != incomingCommand -> fragmentCount)
+ return -1;
+
+ startCommand = incomingCommand;
+ break;
+ }
+ }
+
+ if (startCommand == NULL)
+ {
+ startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount);
+ if (startCommand == NULL)
+ return -1;
+ }
+
+ if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
+ {
+ -- startCommand -> fragmentsRemaining;
+
+ startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
+
+ if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+ fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+ memcpy (startCommand -> packet -> data + fragmentOffset,
+ (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
+ fragmentLength);
+
+ if (startCommand -> fragmentsRemaining <= 0)
+ enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
+ }
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ return -1;
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ return -1;
+
+ if (peer -> incomingBandwidth != 0)
+ -- host -> bandwidthLimitedPeers;
+
+ peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth);
+ peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth);
+
+ if (peer -> incomingBandwidth != 0)
+ ++ host -> bandwidthLimitedPeers;
+
+ if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0)
+ peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+ else
+ if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0)
+ peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+ ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+ ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+ if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+ peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+ else
+ if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+ peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ return -1;
+
+ peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval);
+ peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration);
+ peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration);
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+ if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT)
+ return 0;
+
+ enet_peer_reset_queues (peer);
+
+ if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING)
+ enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+ else
+ if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+ {
+ if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1;
+
+ enet_peer_reset (peer);
+ }
+ else
+ if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+ enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT);
+ else
+ enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+ if (peer -> state != ENET_PEER_STATE_DISCONNECTED)
+ peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data);
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+ enet_uint32 roundTripTime,
+ receivedSentTime,
+ receivedReliableSequenceNumber;
+ ENetProtocolCommand commandNumber;
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE)
+ return 0;
+
+ receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime);
+ receivedSentTime |= host -> serviceTime & 0xFFFF0000;
+ if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000))
+ receivedSentTime -= 0x10000;
+
+ if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime))
+ return 0;
+
+ peer -> lastReceiveTime = host -> serviceTime;
+ peer -> earliestTimeout = 0;
+
+ roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime);
+
+ enet_peer_throttle (peer, roundTripTime);
+
+ peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4;
+
+ if (roundTripTime >= peer -> roundTripTime)
+ {
+ peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8;
+ peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4;
+ }
+ else
+ {
+ peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8;
+ peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4;
+ }
+
+ if (peer -> roundTripTime < peer -> lowestRoundTripTime)
+ peer -> lowestRoundTripTime = peer -> roundTripTime;
+
+ if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance)
+ peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+
+ if (peer -> packetThrottleEpoch == 0 ||
+ ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval)
+ {
+ peer -> lastRoundTripTime = peer -> lowestRoundTripTime;
+ peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance;
+ peer -> lowestRoundTripTime = peer -> roundTripTime;
+ peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+ peer -> packetThrottleEpoch = host -> serviceTime;
+ }
+
+ receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber);
+
+ commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID);
+
+ switch (peer -> state)
+ {
+ case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+ if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT)
+ return -1;
+
+ enet_protocol_notify_connect (host, peer, event);
+ break;
+
+ case ENET_PEER_STATE_DISCONNECTING:
+ if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT)
+ return -1;
+
+ enet_protocol_notify_disconnect (host, peer, event);
+ break;
+
+ case ENET_PEER_STATE_DISCONNECT_LATER:
+ if (enet_list_empty (& peer -> outgoingReliableCommands) &&
+ enet_list_empty (& peer -> outgoingUnreliableCommands) &&
+ enet_list_empty (& peer -> sentReliableCommands))
+ enet_peer_disconnect (peer, peer -> eventData);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+ enet_uint32 mtu, windowSize;
+ size_t channelCount;
+
+ if (peer -> state != ENET_PEER_STATE_CONNECTING)
+ return 0;
+
+ channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount);
+
+ if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT ||
+ ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval ||
+ ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration ||
+ ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration ||
+ command -> verifyConnect.connectID != peer -> connectID)
+ {
+ peer -> eventData = 0;
+
+ enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+ return -1;
+ }
+
+ enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF);
+
+ if (channelCount < peer -> channelCount)
+ peer -> channelCount = channelCount;
+
+ peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID);
+ peer -> incomingSessionID = command -> verifyConnect.incomingSessionID;
+ peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID;
+
+ mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu);
+
+ if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+ mtu = ENET_PROTOCOL_MINIMUM_MTU;
+ else
+ if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+ mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+ if (mtu < peer -> mtu)
+ peer -> mtu = mtu;
+
+ windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize);
+
+ if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+ windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+ if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+ windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+ if (windowSize < peer -> windowSize)
+ peer -> windowSize = windowSize;
+
+ peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth);
+ peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth);
+
+ enet_protocol_notify_connect (host, peer, event);
+ return 0;
+}
+
+static int
+enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+ ENetProtocolHeader * header;
+ ENetProtocol * command;
+ ENetPeer * peer;
+ enet_uint8 * currentData;
+ size_t headerSize;
+ enet_uint16 peerID, flags;
+ enet_uint8 sessionID;
+
+ if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime)
+ return 0;
+
+ header = (ENetProtocolHeader *) host -> receivedData;
+
+ peerID = ENET_NET_TO_HOST_16 (header -> peerID);
+ sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT;
+ flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK;
+ peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK);
+
+ headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime);
+ if (host -> checksum != NULL)
+ headerSize += sizeof (enet_uint32);
+
+ if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID)
+ peer = NULL;
+ else
+ if (peerID >= host -> peerCount)
+ return 0;
+ else
+ {
+ peer = & host -> peers [peerID];
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+ peer -> state == ENET_PEER_STATE_ZOMBIE ||
+ ((host -> receivedAddress.host != peer -> address.host ||
+ host -> receivedAddress.port != peer -> address.port) &&
+ peer -> address.host != ENET_HOST_BROADCAST) ||
+ (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID &&
+ sessionID != peer -> incomingSessionID))
+ return 0;
+ }
+
+ if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED)
+ {
+ size_t originalSize;
+ if (host -> compressor.context == NULL || host -> compressor.decompress == NULL)
+ return 0;
+
+ originalSize = host -> compressor.decompress (host -> compressor.context,
+ host -> receivedData + headerSize,
+ host -> receivedDataLength - headerSize,
+ host -> packetData [1] + headerSize,
+ sizeof (host -> packetData [1]) - headerSize);
+ if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize)
+ return 0;
+
+ memcpy (host -> packetData [1], header, headerSize);
+ host -> receivedData = host -> packetData [1];
+ host -> receivedDataLength = headerSize + originalSize;
+ }
+
+ if (host -> checksum != NULL)
+ {
+ enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)],
+ desiredChecksum = * checksum;
+ ENetBuffer buffer;
+
+ * checksum = peer != NULL ? peer -> connectID : 0;
+
+ buffer.data = host -> receivedData;
+ buffer.dataLength = host -> receivedDataLength;
+
+ if (host -> checksum (& buffer, 1) != desiredChecksum)
+ return 0;
+ }
+
+ if (peer != NULL)
+ {
+ peer -> address.host = host -> receivedAddress.host;
+ peer -> address.port = host -> receivedAddress.port;
+ peer -> incomingDataTotal += host -> receivedDataLength;
+ }
+
+ currentData = host -> receivedData + headerSize;
+
+ while (currentData < & host -> receivedData [host -> receivedDataLength])
+ {
+ enet_uint8 commandNumber;
+ size_t commandSize;
+
+ command = (ENetProtocol *) currentData;
+
+ if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength])
+ break;
+
+ commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK;
+ if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT)
+ break;
+
+ commandSize = commandSizes [commandNumber];
+ if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength])
+ break;
+
+ currentData += commandSize;
+
+ if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT)
+ break;
+
+ command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber);
+
+ switch (commandNumber)
+ {
+ case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE:
+ if (enet_protocol_handle_acknowledge (host, event, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_CONNECT:
+ if (peer != NULL)
+ goto commandError;
+ peer = enet_protocol_handle_connect (host, header, command);
+ if (peer == NULL)
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT:
+ if (enet_protocol_handle_verify_connect (host, event, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_DISCONNECT:
+ if (enet_protocol_handle_disconnect (host, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_PING:
+ if (enet_protocol_handle_ping (host, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+ if (enet_protocol_handle_send_reliable (host, peer, command, & currentData))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+ if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+ if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+ if (enet_protocol_handle_send_fragment (host, peer, command, & currentData))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT:
+ if (enet_protocol_handle_bandwidth_limit (host, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE:
+ if (enet_protocol_handle_throttle_configure (host, peer, command))
+ goto commandError;
+ break;
+
+ case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
+ if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData))
+ goto commandError;
+ break;
+
+ default:
+ goto commandError;
+ }
+
+ if (peer != NULL &&
+ (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0)
+ {
+ enet_uint16 sentTime;
+
+ if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME))
+ break;
+
+ sentTime = ENET_NET_TO_HOST_16 (header -> sentTime);
+
+ switch (peer -> state)
+ {
+ case ENET_PEER_STATE_DISCONNECTING:
+ case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+ case ENET_PEER_STATE_DISCONNECTED:
+ case ENET_PEER_STATE_ZOMBIE:
+ break;
+
+ case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT:
+ if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
+ enet_peer_queue_acknowledgement (peer, command, sentTime);
+ break;
+
+ default:
+ enet_peer_queue_acknowledgement (peer, command, sentTime);
+ break;
+ }
+ }
+ }
+
+commandError:
+ if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+ return 1;
+
+ return 0;
+}
+
+static int
+enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+ int packets;
+
+ for (packets = 0; packets < 256; ++ packets)
+ {
+ int receivedLength;
+ ENetBuffer buffer;
+
+ buffer.data = host -> packetData [0];
+ buffer.dataLength = sizeof (host -> packetData [0]);
+
+ receivedLength = enet_socket_receive (host -> socket,
+ & host -> receivedAddress,
+ & buffer,
+ 1);
+
+ if (receivedLength < 0)
+ return -1;
+
+ if (receivedLength == 0)
+ return 0;
+
+ host -> receivedData = host -> packetData [0];
+ host -> receivedDataLength = receivedLength;
+
+ host -> totalReceivedData += receivedLength;
+ host -> totalReceivedPackets ++;
+
+ if (host -> intercept != NULL)
+ {
+ switch (host -> intercept (host, event))
+ {
+ case 1:
+ if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+ return 1;
+
+ continue;
+
+ case -1:
+ return -1;
+
+ default:
+ break;
+ }
+ }
+
+ switch (enet_protocol_handle_incoming_commands (host, event))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+ return -1;
+
+ default:
+ break;
+ }
+ }
+
+ return -1;
+}
+
+static void
+enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
+{
+ ENetProtocol * command = & host -> commands [host -> commandCount];
+ ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+ ENetAcknowledgement * acknowledgement;
+ ENetListIterator currentAcknowledgement;
+ enet_uint16 reliableSequenceNumber;
+
+ currentAcknowledgement = enet_list_begin (& peer -> acknowledgements);
+
+ while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements))
+ {
+ if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+ buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+ peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
+ {
+ host -> continueSending = 1;
+
+ break;
+ }
+
+ acknowledgement = (ENetAcknowledgement *) currentAcknowledgement;
+
+ currentAcknowledgement = enet_list_next (currentAcknowledgement);
+
+ buffer -> data = command;
+ buffer -> dataLength = sizeof (ENetProtocolAcknowledge);
+
+ host -> packetSize += buffer -> dataLength;
+
+ reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber);
+
+ command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE;
+ command -> header.channelID = acknowledgement -> command.header.channelID;
+ command -> header.reliableSequenceNumber = reliableSequenceNumber;
+ command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber;
+ command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime);
+
+ if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
+ enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+ enet_list_remove (& acknowledgement -> acknowledgementList);
+ enet_free (acknowledgement);
+
+ ++ command;
+ ++ buffer;
+ }
+
+ host -> commandCount = command - host -> commands;
+ host -> bufferCount = buffer - host -> buffers;
+}
+
+static void
+enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+ ENetProtocol * command = & host -> commands [host -> commandCount];
+ ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+ ENetOutgoingCommand * outgoingCommand;
+ ENetListIterator currentCommand;
+
+ currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands);
+
+ while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands))
+ {
+ size_t commandSize;
+
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+ commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK];
+
+ if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+ buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+ peer -> mtu - host -> packetSize < commandSize ||
+ (outgoingCommand -> packet != NULL &&
+ peer -> mtu - host -> packetSize < commandSize + outgoingCommand -> fragmentLength))
+ {
+ host -> continueSending = 1;
+
+ break;
+ }
+
+ currentCommand = enet_list_next (currentCommand);
+
+ if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0)
+ {
+ peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER;
+ peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE;
+
+ if (peer -> packetThrottleCounter > peer -> packetThrottle)
+ {
+ enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber,
+ unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber;
+ for (;;)
+ {
+ -- outgoingCommand -> packet -> referenceCount;
+
+ if (outgoingCommand -> packet -> referenceCount == 0)
+ enet_packet_destroy (outgoingCommand -> packet);
+
+ enet_list_remove (& outgoingCommand -> outgoingCommandList);
+ enet_free (outgoingCommand);
+
+ if (currentCommand == enet_list_end (& peer -> outgoingUnreliableCommands))
+ break;
+
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+ if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber ||
+ outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber)
+ break;
+
+ currentCommand = enet_list_next (currentCommand);
+ }
+
+ continue;
+ }
+ }
+
+ buffer -> data = command;
+ buffer -> dataLength = commandSize;
+
+ host -> packetSize += buffer -> dataLength;
+
+ * command = outgoingCommand -> command;
+
+ enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ ++ buffer;
+
+ buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
+ buffer -> dataLength = outgoingCommand -> fragmentLength;
+
+ host -> packetSize += buffer -> dataLength;
+
+ enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
+ }
+ else
+ enet_free (outgoingCommand);
+
+ ++ command;
+ ++ buffer;
+ }
+
+ host -> commandCount = command - host -> commands;
+ host -> bufferCount = buffer - host -> buffers;
+
+ if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
+ enet_list_empty (& peer -> outgoingReliableCommands) &&
+ enet_list_empty (& peer -> outgoingUnreliableCommands) &&
+ enet_list_empty (& peer -> sentReliableCommands))
+ enet_peer_disconnect (peer, peer -> eventData);
+}
+
+static int
+enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+ ENetOutgoingCommand * outgoingCommand;
+ ENetListIterator currentCommand, insertPosition;
+
+ currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+ insertPosition = enet_list_begin (& peer -> outgoingReliableCommands);
+
+ while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ currentCommand = enet_list_next (currentCommand);
+
+ if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)
+ continue;
+
+ if (peer -> earliestTimeout == 0 ||
+ ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout))
+ peer -> earliestTimeout = outgoingCommand -> sentTime;
+
+ if (peer -> earliestTimeout != 0 &&
+ (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum ||
+ (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit &&
+ ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum)))
+ {
+ enet_protocol_notify_disconnect (host, peer, event);
+
+ return 1;
+ }
+
+ if (outgoingCommand -> packet != NULL)
+ peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+ ++ peer -> packetsLost;
+
+ outgoingCommand -> roundTripTimeout *= 2;
+
+ enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+ if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
+ ! enet_list_empty (& peer -> sentReliableCommands))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+ }
+ }
+
+ return 0;
+}
+
+static int
+enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+ ENetProtocol * command = & host -> commands [host -> commandCount];
+ ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+ ENetOutgoingCommand * outgoingCommand;
+ ENetListIterator currentCommand;
+ ENetChannel *channel;
+ enet_uint16 reliableWindow;
+ size_t commandSize;
+ int windowExceeded = 0, windowWrap = 0, canPing = 1;
+
+ currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
+
+ while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL;
+ reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+ if (channel != NULL)
+ {
+ if (! windowWrap &&
+ outgoingCommand -> sendAttempts < 1 &&
+ ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) &&
+ (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE ||
+ channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) |
+ (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))))
+ windowWrap = 1;
+ if (windowWrap)
+ {
+ currentCommand = enet_list_next (currentCommand);
+
+ continue;
+ }
+ }
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ if (! windowExceeded)
+ {
+ enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
+
+ if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
+ windowExceeded = 1;
+ }
+ if (windowExceeded)
+ {
+ currentCommand = enet_list_next (currentCommand);
+
+ continue;
+ }
+ }
+
+ canPing = 0;
+
+ commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK];
+ if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+ buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+ peer -> mtu - host -> packetSize < commandSize ||
+ (outgoingCommand -> packet != NULL &&
+ (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
+ {
+ host -> continueSending = 1;
+
+ break;
+ }
+
+ currentCommand = enet_list_next (currentCommand);
+
+ if (channel != NULL && outgoingCommand -> sendAttempts < 1)
+ {
+ channel -> usedReliableWindows |= 1 << reliableWindow;
+ ++ channel -> reliableWindows [reliableWindow];
+ }
+
+ ++ outgoingCommand -> sendAttempts;
+
+ if (outgoingCommand -> roundTripTimeout == 0)
+ {
+ outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
+ outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
+ }
+
+ if (enet_list_empty (& peer -> sentReliableCommands))
+ peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
+
+ enet_list_insert (enet_list_end (& peer -> sentReliableCommands),
+ enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+ outgoingCommand -> sentTime = host -> serviceTime;
+
+ buffer -> data = command;
+ buffer -> dataLength = commandSize;
+
+ host -> packetSize += buffer -> dataLength;
+ host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME;
+
+ * command = outgoingCommand -> command;
+
+ if (outgoingCommand -> packet != NULL)
+ {
+ ++ buffer;
+
+ buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
+ buffer -> dataLength = outgoingCommand -> fragmentLength;
+
+ host -> packetSize += outgoingCommand -> fragmentLength;
+
+ peer -> reliableDataInTransit += outgoingCommand -> fragmentLength;
+ }
+
+ ++ peer -> packetsSent;
+
+ ++ command;
+ ++ buffer;
+ }
+
+ host -> commandCount = command - host -> commands;
+ host -> bufferCount = buffer - host -> buffers;
+
+ return canPing;
+}
+
+static int
+enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
+{
+ enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
+ ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
+ ENetPeer * currentPeer;
+ int sentLength;
+ size_t shouldCompress = 0;
+
+ host -> continueSending = 1;
+
+ while (host -> continueSending)
+ for (host -> continueSending = 0,
+ currentPeer = host -> peers;
+ currentPeer < & host -> peers [host -> peerCount];
+ ++ currentPeer)
+ {
+ if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
+ currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+ continue;
+
+ host -> headerFlags = 0;
+ host -> commandCount = 0;
+ host -> bufferCount = 1;
+ host -> packetSize = sizeof (ENetProtocolHeader);
+
+ if (! enet_list_empty (& currentPeer -> acknowledgements))
+ enet_protocol_send_acknowledgements (host, currentPeer);
+
+ if (checkForTimeouts != 0 &&
+ ! enet_list_empty (& currentPeer -> sentReliableCommands) &&
+ ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) &&
+ enet_protocol_check_timeouts (host, currentPeer, event) == 1)
+ {
+ if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+ return 1;
+ else
+ continue;
+ }
+
+ if ((enet_list_empty (& currentPeer -> outgoingReliableCommands) ||
+ enet_protocol_send_reliable_outgoing_commands (host, currentPeer)) &&
+ enet_list_empty (& currentPeer -> sentReliableCommands) &&
+ ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
+ currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
+ {
+ enet_peer_ping (currentPeer);
+ enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
+ }
+
+ if (! enet_list_empty (& currentPeer -> outgoingUnreliableCommands))
+ enet_protocol_send_unreliable_outgoing_commands (host, currentPeer);
+
+ if (host -> commandCount == 0)
+ continue;
+
+ if (currentPeer -> packetLossEpoch == 0)
+ currentPeer -> packetLossEpoch = host -> serviceTime;
+ else
+ if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL &&
+ currentPeer -> packetsSent > 0)
+ {
+ enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
+
+#ifdef ENET_DEBUG
+ printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
+#endif
+
+ currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4;
+
+ if (packetLoss >= currentPeer -> packetLoss)
+ {
+ currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8;
+ currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4;
+ }
+ else
+ {
+ currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8;
+ currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4;
+ }
+
+ currentPeer -> packetLossEpoch = host -> serviceTime;
+ currentPeer -> packetsSent = 0;
+ currentPeer -> packetsLost = 0;
+ }
+
+ host -> buffers -> data = headerData;
+ if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)
+ {
+ header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF);
+
+ host -> buffers -> dataLength = sizeof (ENetProtocolHeader);
+ }
+ else
+ host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime;
+
+ shouldCompress = 0;
+ if (host -> compressor.context != NULL && host -> compressor.compress != NULL)
+ {
+ size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader),
+ compressedSize = host -> compressor.compress (host -> compressor.context,
+ & host -> buffers [1], host -> bufferCount - 1,
+ originalSize,
+ host -> packetData [1],
+ originalSize);
+ if (compressedSize > 0 && compressedSize < originalSize)
+ {
+ host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED;
+ shouldCompress = compressedSize;
+#ifdef ENET_DEBUG_COMPRESS
+ printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize);
+#endif
+ }
+ }
+
+ if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID)
+ host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT;
+ header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags);
+ if (host -> checksum != NULL)
+ {
+ enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength];
+ * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0;
+ host -> buffers -> dataLength += sizeof (enet_uint32);
+ * checksum = host -> checksum (host -> buffers, host -> bufferCount);
+ }
+
+ if (shouldCompress > 0)
+ {
+ host -> buffers [1].data = host -> packetData [1];
+ host -> buffers [1].dataLength = shouldCompress;
+ host -> bufferCount = 2;
+ }
+
+ currentPeer -> lastSendTime = host -> serviceTime;
+
+ sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
+
+ enet_protocol_remove_sent_unreliable_commands (currentPeer);
+
+ if (sentLength < 0)
+ return -1;
+
+ host -> totalSentData += sentLength;
+ host -> totalSentPackets ++;
+ }
+
+ return 0;
+}
+
+/** Sends any queued packets on the host specified to its designated peers.
+
+ @param host host to flush
+ @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service().
+ @ingroup host
+*/
+void
+enet_host_flush (ENetHost * host)
+{
+ host -> serviceTime = enet_time_get ();
+
+ enet_protocol_send_outgoing_commands (host, NULL, 0);
+}
+
+/** Checks for any queued events on the host and dispatches one if available.
+
+ @param host host to check for events
+ @param event an event structure where event details will be placed if available
+ @retval > 0 if an event was dispatched
+ @retval 0 if no events are available
+ @retval < 0 on failure
+ @ingroup host
+*/
+int
+enet_host_check_events (ENetHost * host, ENetEvent * event)
+{
+ if (event == NULL) return -1;
+
+ event -> type = ENET_EVENT_TYPE_NONE;
+ event -> peer = NULL;
+ event -> packet = NULL;
+
+ return enet_protocol_dispatch_incoming_commands (host, event);
+}
+
+/** Waits for events on the host specified and shuttles packets between
+ the host and its peers.
+
+ @param host host to service
+ @param event an event structure where event details will be placed if one occurs
+ if event == NULL then no events will be delivered
+ @param timeout number of milliseconds that ENet should wait for events
+ @retval > 0 if an event occurred within the specified time limit
+ @retval 0 if no event occurred
+ @retval < 0 on failure
+ @remarks enet_host_service should be called fairly regularly for adequate performance
+ @ingroup host
+*/
+int
+enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
+{
+ enet_uint32 waitCondition;
+
+ if (event != NULL)
+ {
+ event -> type = ENET_EVENT_TYPE_NONE;
+ event -> peer = NULL;
+ event -> packet = NULL;
+
+ switch (enet_protocol_dispatch_incoming_commands (host, event))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+#ifdef ENET_DEBUG
+ perror ("Error dispatching incoming packets");
+#endif
+
+ return -1;
+
+ default:
+ break;
+ }
+ }
+
+ host -> serviceTime = enet_time_get ();
+
+ timeout += host -> serviceTime;
+
+ do
+ {
+ if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+ enet_host_bandwidth_throttle (host);
+
+ switch (enet_protocol_send_outgoing_commands (host, event, 1))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+#ifdef ENET_DEBUG
+ perror ("Error sending outgoing packets");
+#endif
+
+ return -1;
+
+ default:
+ break;
+ }
+
+ switch (enet_protocol_receive_incoming_commands (host, event))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+#ifdef ENET_DEBUG
+ perror ("Error receiving incoming packets");
+#endif
+
+ return -1;
+
+ default:
+ break;
+ }
+
+ switch (enet_protocol_send_outgoing_commands (host, event, 1))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+#ifdef ENET_DEBUG
+ perror ("Error sending outgoing packets");
+#endif
+
+ return -1;
+
+ default:
+ break;
+ }
+
+ if (event != NULL)
+ {
+ switch (enet_protocol_dispatch_incoming_commands (host, event))
+ {
+ case 1:
+ return 1;
+
+ case -1:
+#ifdef ENET_DEBUG
+ perror ("Error dispatching incoming packets");
+#endif
+
+ return -1;
+
+ default:
+ break;
+ }
+ }
+
+ if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
+ return 0;
+
+ do
+ {
+ host -> serviceTime = enet_time_get ();
+
+ if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
+ return 0;
+
+ waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT;
+
+ if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0)
+ return -1;
+ }
+ while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT);
+
+ host -> serviceTime = enet_time_get ();
+ } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE);
+
+ return 0;
+}
+
diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp
new file mode 100644
index 0000000000..630b76ced8
--- /dev/null
+++ b/modules/enet/register_types.cpp
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "networked_multiplayer_enet.h"
+
+static bool enet_ok=false;
+
+void register_enet_types() {
+
+ if (enet_initialize() !=0 ) {
+ ERR_PRINT("ENet initialization failure");
+ } else {
+ enet_ok=true;
+ }
+
+ ObjectTypeDB::register_type<NetworkedMultiplayerENet>();
+}
+
+void unregister_enet_types() {
+
+ if (enet_ok)
+ enet_deinitialize();
+
+}
diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h
new file mode 100644
index 0000000000..50f34dc67f
--- /dev/null
+++ b/modules/enet/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_enet_types();
+void unregister_enet_types();
diff --git a/modules/enet/unix.c b/modules/enet/unix.c
new file mode 100644
index 0000000000..3138cc04b6
--- /dev/null
+++ b/modules/enet/unix.c
@@ -0,0 +1,616 @@
+/**
+ @file unix.c
+ @brief ENet Unix system specific functions
+*/
+#ifndef _WIN32
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+//@godot: added this since enet takes them fromt he build system
+#define HAS_POLL
+#define HAS_FCNTL
+#define HAS_SOCKLEN_T
+
+
+#ifdef __APPLE__
+#ifdef HAS_POLL
+#undef HAS_POLL
+#endif
+#ifndef HAS_FCNTL
+#define HAS_FCNTL 1
+#endif
+#ifndef HAS_INET_PTON
+#define HAS_INET_PTON 1
+#endif
+#ifndef HAS_INET_NTOP
+#define HAS_INET_NTOP 1
+#endif
+#ifndef HAS_MSGHDR_FLAGS
+#define HAS_MSGHDR_FLAGS 1
+#endif
+#ifndef HAS_SOCKLEN_T
+#define HAS_SOCKLEN_T 1
+#endif
+#ifndef HAS_GETADDRINFO
+#define HAS_GETADDRINFO 1
+#endif
+#ifndef HAS_GETNAMEINFO
+#define HAS_GETNAMEINFO 1
+#endif
+#endif
+
+#ifdef HAS_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef HAS_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef HAS_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+ return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+}
+
+enet_uint32
+enet_host_random_seed (void)
+{
+ return (enet_uint32) time (NULL);
+}
+
+enet_uint32
+enet_time_get (void)
+{
+ struct timeval timeVal;
+
+ gettimeofday (& timeVal, NULL);
+
+ return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+ struct timeval timeVal;
+
+ gettimeofday (& timeVal, NULL);
+
+ timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+#ifdef HAS_GETADDRINFO
+ struct addrinfo hints, * resultList = NULL, * result = NULL;
+
+ memset (& hints, 0, sizeof (hints));
+ hints.ai_family = AF_INET;
+
+ if (getaddrinfo (name, NULL, NULL, & resultList) != 0)
+ return -1;
+
+ for (result = resultList; result != NULL; result = result -> ai_next)
+ {
+ if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in))
+ {
+ struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr;
+
+ address -> host = sin -> sin_addr.s_addr;
+
+ freeaddrinfo (resultList);
+
+ return 0;
+ }
+ }
+
+ if (resultList != NULL)
+ freeaddrinfo (resultList);
+#else
+ struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYNAME_R
+ struct hostent hostData;
+ char buffer [2048];
+ int errnum;
+
+#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+ hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+ hostEntry = gethostbyname (name);
+#endif
+
+ if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET)
+ {
+ address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+ return 0;
+ }
+#endif
+
+#ifdef HAS_INET_PTON
+ if (! inet_pton (AF_INET, name, & address -> host))
+#else
+ if (! inet_aton (name, (struct in_addr *) & address -> host))
+#endif
+ return -1;
+
+ return 0;
+}
+
+int
+enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
+{
+#ifdef HAS_INET_NTOP
+ if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL)
+#else
+ char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
+ if (addr != NULL)
+ {
+ size_t addrLen = strlen(addr);
+ if (addrLen >= nameLength)
+ return -1;
+ memcpy (name, addr, addrLen + 1);
+ }
+ else
+#endif
+ return -1;
+ return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+#ifdef HAS_GETNAMEINFO
+ struct sockaddr_in sin;
+ int err;
+
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+
+ err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD);
+ if (! err)
+ {
+ if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength))
+ return -1;
+ return 0;
+ }
+ if (err != EAI_NONAME)
+ return -1;
+#else
+ struct in_addr in;
+ struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYADDR_R
+ struct hostent hostData;
+ char buffer [2048];
+ int errnum;
+
+ in.s_addr = address -> host;
+
+#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+ hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+ in.s_addr = address -> host;
+
+ hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+#endif
+
+ if (hostEntry != NULL)
+ {
+ size_t hostLen = strlen (hostEntry -> h_name);
+ if (hostLen >= nameLength)
+ return -1;
+ memcpy (name, hostEntry -> h_name, hostLen + 1);
+ return 0;
+ }
+#endif
+
+ return enet_address_get_host_ip (address, name, nameLength);
+}
+
+int
+enet_socket_bind (ENetSocket socket, const ENetAddress * address)
+{
+ struct sockaddr_in sin;
+
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+
+ if (address != NULL)
+ {
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+ }
+ else
+ {
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ return bind (socket,
+ (struct sockaddr *) & sin,
+ sizeof (struct sockaddr_in));
+}
+
+int
+enet_socket_get_address (ENetSocket socket, ENetAddress * address)
+{
+ struct sockaddr_in sin;
+ socklen_t sinLength = sizeof (struct sockaddr_in);
+
+ if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
+ return -1;
+
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+
+ return 0;
+}
+
+int
+enet_socket_listen (ENetSocket socket, int backlog)
+{
+ return listen (socket, backlog < 0 ? SOMAXCONN : backlog);
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type)
+{
+ return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+}
+
+int
+enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
+{
+ int result = -1;
+ switch (option)
+ {
+ case ENET_SOCKOPT_NONBLOCK:
+#ifdef HAS_FCNTL
+ result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK));
+#else
+ result = ioctl (socket, FIONBIO, & value);
+#endif
+ break;
+
+ case ENET_SOCKOPT_BROADCAST:
+ result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_REUSEADDR:
+ result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_RCVBUF:
+ result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_SNDBUF:
+ result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_RCVTIMEO:
+ {
+ struct timeval timeVal;
+ timeVal.tv_sec = value / 1000;
+ timeVal.tv_usec = (value % 1000) * 1000;
+ result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval));
+ break;
+ }
+
+ case ENET_SOCKOPT_SNDTIMEO:
+ {
+ struct timeval timeVal;
+ timeVal.tv_sec = value / 1000;
+ timeVal.tv_usec = (value % 1000) * 1000;
+ result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval));
+ break;
+ }
+
+ case ENET_SOCKOPT_NODELAY:
+ result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
+ break;
+
+ default:
+ break;
+ }
+ return result == -1 ? -1 : 0;
+}
+
+int
+enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
+{
+ int result = -1;
+ socklen_t len;
+ switch (option)
+ {
+ case ENET_SOCKOPT_ERROR:
+ len = sizeof (int);
+ result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len);
+ break;
+
+ default:
+ break;
+ }
+ return result == -1 ? -1 : 0;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+ struct sockaddr_in sin;
+ int result;
+
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+
+ result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+ if (result == -1 && errno == EINPROGRESS)
+ return 0;
+
+ return result;
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+ int result;
+ struct sockaddr_in sin;
+ socklen_t sinLength = sizeof (struct sockaddr_in);
+
+ result = accept (socket,
+ address != NULL ? (struct sockaddr *) & sin : NULL,
+ address != NULL ? & sinLength : NULL);
+
+ if (result == -1)
+ return ENET_SOCKET_NULL;
+
+ if (address != NULL)
+ {
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+ }
+
+ return result;
+}
+
+int
+enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
+{
+ return shutdown (socket, (int) how);
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+ if (socket != -1)
+ close (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+ const ENetAddress * address,
+ const ENetBuffer * buffers,
+ size_t bufferCount)
+{
+ struct msghdr msgHdr;
+ struct sockaddr_in sin;
+ int sentLength;
+
+ memset (& msgHdr, 0, sizeof (struct msghdr));
+
+ if (address != NULL)
+ {
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+
+ msgHdr.msg_name = & sin;
+ msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+ }
+
+ msgHdr.msg_iov = (struct iovec *) buffers;
+ msgHdr.msg_iovlen = bufferCount;
+
+ sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
+
+ if (sentLength == -1)
+ {
+ if (errno == EWOULDBLOCK)
+ return 0;
+
+ return -1;
+ }
+
+ return sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+ ENetAddress * address,
+ ENetBuffer * buffers,
+ size_t bufferCount)
+{
+ struct msghdr msgHdr;
+ struct sockaddr_in sin;
+ int recvLength;
+
+ memset (& msgHdr, 0, sizeof (struct msghdr));
+
+ if (address != NULL)
+ {
+ msgHdr.msg_name = & sin;
+ msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+ }
+
+ msgHdr.msg_iov = (struct iovec *) buffers;
+ msgHdr.msg_iovlen = bufferCount;
+
+ recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
+
+ if (recvLength == -1)
+ {
+ if (errno == EWOULDBLOCK)
+ return 0;
+
+ return -1;
+ }
+
+#ifdef HAS_MSGHDR_FLAGS
+ if (msgHdr.msg_flags & MSG_TRUNC)
+ return -1;
+#endif
+
+ if (address != NULL)
+ {
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+ }
+
+ return recvLength;
+}
+
+int
+enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
+{
+ struct timeval timeVal;
+
+ timeVal.tv_sec = timeout / 1000;
+ timeVal.tv_usec = (timeout % 1000) * 1000;
+
+ return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+#ifdef HAS_POLL
+ struct pollfd pollSocket;
+ int pollCount;
+
+ pollSocket.fd = socket;
+ pollSocket.events = 0;
+
+ if (* condition & ENET_SOCKET_WAIT_SEND)
+ pollSocket.events |= POLLOUT;
+
+ if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+ pollSocket.events |= POLLIN;
+
+ pollCount = poll (& pollSocket, 1, timeout);
+
+ if (pollCount < 0)
+ {
+ if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
+ {
+ * condition = ENET_SOCKET_WAIT_INTERRUPT;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ * condition = ENET_SOCKET_WAIT_NONE;
+
+ if (pollCount == 0)
+ return 0;
+
+ if (pollSocket.revents & POLLOUT)
+ * condition |= ENET_SOCKET_WAIT_SEND;
+
+ if (pollSocket.revents & POLLIN)
+ * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+ return 0;
+#else
+ fd_set readSet, writeSet;
+ struct timeval timeVal;
+ int selectCount;
+
+ timeVal.tv_sec = timeout / 1000;
+ timeVal.tv_usec = (timeout % 1000) * 1000;
+
+ FD_ZERO (& readSet);
+ FD_ZERO (& writeSet);
+
+ if (* condition & ENET_SOCKET_WAIT_SEND)
+ FD_SET (socket, & writeSet);
+
+ if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+ FD_SET (socket, & readSet);
+
+ selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+ if (selectCount < 0)
+ {
+ if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
+ {
+ * condition = ENET_SOCKET_WAIT_INTERRUPT;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ * condition = ENET_SOCKET_WAIT_NONE;
+
+ if (selectCount == 0)
+ return 0;
+
+ if (FD_ISSET (socket, & writeSet))
+ * condition |= ENET_SOCKET_WAIT_SEND;
+
+ if (FD_ISSET (socket, & readSet))
+ * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+ return 0;
+#endif
+}
+
+#endif
+
diff --git a/modules/enet/win32.c b/modules/enet/win32.c
new file mode 100644
index 0000000000..d77fa9a49a
--- /dev/null
+++ b/modules/enet/win32.c
@@ -0,0 +1,422 @@
+/**
+ @file win32.c
+ @brief ENet Win32 system specific functions
+*/
+#ifdef _WIN32
+
+#define ENET_BUILDING_LIB 0
+#include "enet/enet.h"
+#include <windows.h>
+#include <mmsystem.h>
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+ WORD versionRequested = MAKEWORD (1, 1);
+ WSADATA wsaData;
+
+ if (WSAStartup (versionRequested, & wsaData))
+ return -1;
+
+ if (LOBYTE (wsaData.wVersion) != 1||
+ HIBYTE (wsaData.wVersion) != 1)
+ {
+ WSACleanup ();
+
+ return -1;
+ }
+
+ timeBeginPeriod (1);
+
+ return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+ timeEndPeriod (1);
+
+ WSACleanup ();
+}
+
+enet_uint32
+enet_host_random_seed (void)
+{
+ return (enet_uint32) timeGetTime ();
+}
+
+enet_uint32
+enet_time_get (void)
+{
+ return (enet_uint32) timeGetTime () - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+ timeBase = (enet_uint32) timeGetTime () - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+ struct hostent * hostEntry;
+
+ hostEntry = gethostbyname (name);
+ if (hostEntry == NULL ||
+ hostEntry -> h_addrtype != AF_INET)
+ {
+ unsigned long host = inet_addr (name);
+ if (host == INADDR_NONE)
+ return -1;
+ address -> host = host;
+ return 0;
+ }
+
+ address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+ return 0;
+}
+
+int
+enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
+{
+ char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
+ if (addr == NULL)
+ return -1;
+ else
+ {
+ size_t addrLen = strlen(addr);
+ if (addrLen >= nameLength)
+ return -1;
+ memcpy (name, addr, addrLen + 1);
+ }
+ return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+ struct in_addr in;
+ struct hostent * hostEntry;
+
+ in.s_addr = address -> host;
+
+ hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+ if (hostEntry == NULL)
+ return enet_address_get_host_ip (address, name, nameLength);
+ else
+ {
+ size_t hostLen = strlen (hostEntry -> h_name);
+ if (hostLen >= nameLength)
+ return -1;
+ memcpy (name, hostEntry -> h_name, hostLen + 1);
+ }
+
+ return 0;
+}
+
+int
+enet_socket_bind (ENetSocket socket, const ENetAddress * address)
+{
+ struct sockaddr_in sin;
+
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+
+ if (address != NULL)
+ {
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+ }
+ else
+ {
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ return bind (socket,
+ (struct sockaddr *) & sin,
+ sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_get_address (ENetSocket socket, ENetAddress * address)
+{
+ struct sockaddr_in sin;
+ int sinLength = sizeof (struct sockaddr_in);
+
+ if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
+ return -1;
+
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+
+ return 0;
+}
+
+int
+enet_socket_listen (ENetSocket socket, int backlog)
+{
+ return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type)
+{
+ return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+}
+
+int
+enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
+{
+ int result = SOCKET_ERROR;
+ switch (option)
+ {
+ case ENET_SOCKOPT_NONBLOCK:
+ {
+ u_long nonBlocking = (u_long) value;
+ result = ioctlsocket (socket, FIONBIO, & nonBlocking);
+ break;
+ }
+
+ case ENET_SOCKOPT_BROADCAST:
+ result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_REUSEADDR:
+ result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_RCVBUF:
+ result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_SNDBUF:
+ result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_RCVTIMEO:
+ result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_SNDTIMEO:
+ result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int));
+ break;
+
+ case ENET_SOCKOPT_NODELAY:
+ result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
+ break;
+
+ default:
+ break;
+ }
+ return result == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
+{
+ int result = SOCKET_ERROR, len;
+ switch (option)
+ {
+ case ENET_SOCKOPT_ERROR:
+ len = sizeof(int);
+ result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len);
+ break;
+
+ default:
+ break;
+ }
+ return result == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+ struct sockaddr_in sin;
+ int result;
+
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+
+ result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+ if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK)
+ return -1;
+
+ return 0;
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+ SOCKET result;
+ struct sockaddr_in sin;
+ int sinLength = sizeof (struct sockaddr_in);
+
+ result = accept (socket,
+ address != NULL ? (struct sockaddr *) & sin : NULL,
+ address != NULL ? & sinLength : NULL);
+
+ if (result == INVALID_SOCKET)
+ return ENET_SOCKET_NULL;
+
+ if (address != NULL)
+ {
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+ }
+
+ return result;
+}
+
+int
+enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
+{
+ return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0;
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+ if (socket != INVALID_SOCKET)
+ closesocket (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+ const ENetAddress * address,
+ const ENetBuffer * buffers,
+ size_t bufferCount)
+{
+ struct sockaddr_in sin;
+ DWORD sentLength;
+
+ if (address != NULL)
+ {
+ memset (& sin, 0, sizeof (struct sockaddr_in));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+ sin.sin_addr.s_addr = address -> host;
+ }
+
+ if (WSASendTo (socket,
+ (LPWSABUF) buffers,
+ (DWORD) bufferCount,
+ & sentLength,
+ 0,
+ address != NULL ? (struct sockaddr *) & sin : NULL,
+ address != NULL ? sizeof (struct sockaddr_in) : 0,
+ NULL,
+ NULL) == SOCKET_ERROR)
+ {
+ if (WSAGetLastError () == WSAEWOULDBLOCK)
+ return 0;
+
+ return -1;
+ }
+
+ return (int) sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+ ENetAddress * address,
+ ENetBuffer * buffers,
+ size_t bufferCount)
+{
+ INT sinLength = sizeof (struct sockaddr_in);
+ DWORD flags = 0,
+ recvLength;
+ struct sockaddr_in sin;
+
+ if (WSARecvFrom (socket,
+ (LPWSABUF) buffers,
+ (DWORD) bufferCount,
+ & recvLength,
+ & flags,
+ address != NULL ? (struct sockaddr *) & sin : NULL,
+ address != NULL ? & sinLength : NULL,
+ NULL,
+ NULL) == SOCKET_ERROR)
+ {
+ switch (WSAGetLastError ())
+ {
+ case WSAEWOULDBLOCK:
+ case WSAECONNRESET:
+ return 0;
+ }
+
+ return -1;
+ }
+
+ if (flags & MSG_PARTIAL)
+ return -1;
+
+ if (address != NULL)
+ {
+ address -> host = (enet_uint32) sin.sin_addr.s_addr;
+ address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+ }
+
+ return (int) recvLength;
+}
+
+int
+enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
+{
+ struct timeval timeVal;
+
+ timeVal.tv_sec = timeout / 1000;
+ timeVal.tv_usec = (timeout % 1000) * 1000;
+
+ return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+ fd_set readSet, writeSet;
+ struct timeval timeVal;
+ int selectCount;
+
+ timeVal.tv_sec = timeout / 1000;
+ timeVal.tv_usec = (timeout % 1000) * 1000;
+
+ FD_ZERO (& readSet);
+ FD_ZERO (& writeSet);
+
+ if (* condition & ENET_SOCKET_WAIT_SEND)
+ FD_SET (socket, & writeSet);
+
+ if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+ FD_SET (socket, & readSet);
+
+ selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+ if (selectCount < 0)
+ return -1;
+
+ * condition = ENET_SOCKET_WAIT_NONE;
+
+ if (selectCount == 0)
+ return 0;
+
+ if (FD_ISSET (socket, & writeSet))
+ * condition |= ENET_SOCKET_WAIT_SEND;
+
+ if (FD_ISSET (socket, & readSet))
+ * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+ return 0;
+}
+
+#endif
+
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 7a2e9c9638..ce8b6a6ea4 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -460,7 +460,6 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
const GDParser::Node *instance = on->arguments[0];
- bool in_static=false;
if (instance->type==GDParser::Node::TYPE_SELF) {
//room for optimization
@@ -550,17 +549,25 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
int index;
if (named) {
-#ifdef DEBUG_ENABLED
if (on->arguments[0]->type==GDParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- const Map<StringName,GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name);
+ GDParser::IdentifierNode* identifier = static_cast<GDParser::IdentifierNode*>(on->arguments[1]);
+ const Map<StringName,GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+
+#ifdef DEBUG_ENABLED
if (MI && MI->get().getter==codegen.function_node->name) {
String n = static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name;
_set_error("Must use '"+n+"' instead of 'self."+n+"' in getter.",on);
return -1;
}
- }
#endif
+
+ if (MI && MI->get().getter=="") {
+ // Faster than indexing self (as if no self. had been used)
+ return (MI->get().index)|(GDFunction::ADDR_TYPE_MEMBER<<GDFunction::ADDR_BITS);
+ }
+ }
+
index=codegen.get_name_map_pos(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name);
} else {
@@ -763,8 +770,6 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
Vector<int> setchain;
- int prev_key_idx=-1;
-
for(List<GDParser::OperatorNode*>::Element *E=chain.back();E;E=E->prev()) {
@@ -814,7 +819,6 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
setchain.push_back(named ? GDFunction::OPCODE_SET_NAMED : GDFunction::OPCODE_SET);
prev_pos=dst_pos;
- prev_key_idx=key_idx;
}
@@ -1203,7 +1207,6 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
if (p_func) {
for(int i=0;i<p_func->arguments.size();i++) {
- int idx = i;
codegen.add_stack_identifier(p_func->arguments[i],i);
#ifdef TOOLS_ENABLED
argnames.push_back(p_func->arguments[i]);
@@ -1294,8 +1297,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
gdfunc = p_script->member_functions[func_name];
//}
- if (p_func)
+ if (p_func) {
gdfunc->_static=p_func->_static;
+ gdfunc->rpc_mode=p_func->rpc_mode;
+ }
#ifdef TOOLS_ENABLED
gdfunc->arg_names=argnames;
@@ -1433,6 +1438,7 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
+ p_script->_signals.clear();
p_script->initializer=NULL;
p_script->subclasses.clear();
@@ -1441,7 +1447,6 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
p_script->name=p_class->name;
- int index_from=0;
Ref<GDNativeClass> native;
if (p_class->extends_used) {
@@ -1497,7 +1502,8 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
String sub = p_class->extends_class[i];
if (script->subclasses.has(sub)) {
- script=script->subclasses[sub];
+ Ref<Script> subclass = script->subclasses[sub]; //avoid reference from dissapearing
+ script=subclass;
} else {
_set_error("Could not find subclass: "+sub,p_class);
@@ -1611,6 +1617,9 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
p_script->member_default_values[name]=p_class->variables[i].default_value;
}
#endif
+ } else {
+
+ p_script->member_info[name]=PropertyInfo(Variant::NIL,name,PROPERTY_HINT_NONE,"",PROPERTY_USAGE_SCRIPT_VARIABLE);
}
//int new_idx = p_script->member_indices.size();
@@ -1618,6 +1627,8 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
minfo.index = p_script->member_indices.size();
minfo.setter = p_class->variables[i].setter;
minfo.getter = p_class->variables[i].getter;
+ minfo.rpc_mode=p_class->variables[i].rpc_mode;
+
p_script->member_indices[name]=minfo;
p_script->members.insert(name);
@@ -1680,6 +1691,7 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
if (err)
return err;
+
p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants
p_script->subclasses.insert(name,subclass);
@@ -1779,6 +1791,7 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
ScriptInstance *si = E->get()->get_script_instance();
if (si->is_placeholder()) {
+#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *psi = static_cast<PlaceHolderScriptInstance*>(si);
if (p_script->is_tool()) {
@@ -1807,8 +1820,9 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
//well, tough luck, not goinna do anything here
}
}
+#endif
} else {
- print_line("RELOAD MEMBERS");
+
GDInstance *gi = static_cast<GDInstance*>(si);
gi->reload_members();
}
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 7e5ff620c9..2a80531ec5 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -44,21 +44,26 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
}
-String GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
+Ref<Script> GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
String _template = String()+
- "\nextends %BASE%\n\n"+
- "# member variables here, example:\n"+
- "# var a=2\n"+
- "# var b=\"textvar\"\n\n"+
+ "extends %BASE%\n\n"+
+ "# class member variables go here, for example:\n"+
+ "# var a = 2\n"+
+ "# var b = \"textvar\"\n\n"+
"func _ready():\n"+
"\t# Called every time the node is added to the scene.\n"+
"\t# Initialization here\n"+
- "\tpass\n"+
- "\n"+
- "\n";
+ "\tpass\n";
+
+ _template = _template.replace("%BASE%",p_base_class_name);
+
+ Ref<GDScript> script;
+ script.instance();
+ script->set_source_code(_template);
+
+ return script;
- return _template.replace("%BASE%",p_base_class_name);
}
@@ -212,7 +217,7 @@ String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
int l = _debug_call_stack_pos - p_level -1;
- return _call_stack[l].function->get_script()->get_path();
+ return _call_stack[l].function->get_source();
}
void GDScriptLanguage::debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
@@ -297,7 +302,7 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
}
{
MethodInfo mi;
- mi.name="yield";
+ mi.name="yield:GDFunctionState";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT,"object"));
mi.arguments.push_back(PropertyInfo(Variant::STRING,"signal"));
mi.default_arguments.push_back(Variant::NIL);
@@ -328,7 +333,7 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam
for(int i=0;i<p_args.size();i++) {
if (i>0)
s+=", ";
- s+=p_args[i];
+ s+=p_args[i].get_slice(":",0);
}
s+=" ";
}
@@ -449,62 +454,21 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) {
}
String base=context._class->extends_class[0];
- const GDParser::ClassNode *p = context._class->owner;
- Ref<GDScript> base_class;
-#if 0
- while(p) {
-
- if (p->subclasses.has(base)) {
-
- base_class=p->subclasses[base];
- break;
- }
- p=p->_owner;
- }
-#endif
- if (base_class.is_valid()) {
-#if 0
- for(int i=1;i<context._class->extends_class.size();i++) {
-
- String subclass=context._class->extends_class[i];
-
- if (base_class->subclasses.has(subclass)) {
-
- base_class=base_class->subclasses[subclass];
- } else {
-
- //print_line("Could not find subclass: "+subclass);
- return _get_type_from_class(context); //fail please
- }
- }
-
- script=base_class;
-#endif
-
- } else {
-
- if (context._class->extends_class.size()>1) {
-
- return REF();
-
-
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
- return REF();
- }
+ if (context._class->extends_class.size()>1) {
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- if (!native.is_valid()) {
+ return REF();
- print_line("Global not a class: '"+base+"'");
+ }
+ //if not found, try engine classes
+ if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
- }
- return native;
+ return REF();
}
+ int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
+ return native;
}
@@ -1024,7 +988,7 @@ static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_l
}
-static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context,const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) {
+static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context, int p_src_line, const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) {
const GDParser::FunctionNode* func=NULL;
for(int i=0;i<context._class->functions.size();i++) {
@@ -1039,7 +1003,9 @@ static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& c
for(int i=0;i<func->body->statements.size();i++) {
-
+ if (func->body->statements[i]->line == p_src_line) {
+ break;
+ }
if (func->body->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) {
const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(func->body->statements[i]);
@@ -1160,11 +1126,11 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const
}
//try to guess from assignment in construtor or _ready
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_ready",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_ready",r_type))
return true;
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_enter_tree",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_enter_tree",r_type))
return true;
- if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_init",r_type))
+ if (_guess_identifier_from_assignment_in_function(context,p_line+1,p_identifier,"_init",r_type))
return true;
return false;
@@ -1495,7 +1461,7 @@ static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argid
}
-static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) {
+static void _find_type_arguments(GDCompletionContext& context,const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) {
//print_line("find type arguments?");
@@ -1732,9 +1698,31 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St
if (p_argidx==0) {
List<MethodInfo> sigs;
ObjectTypeDB::get_signal_list(id.obj_type,&sigs);
+
+ if (id.script.is_valid()) {
+ id.script->get_script_signal_list(&sigs);
+ } else if (id.value.get_type()==Variant::OBJECT) {
+ Object *obj = id.value;
+ if (obj && !obj->get_script().is_null()) {
+ Ref<Script> scr=obj->get_script();
+ if (scr.is_valid()) {
+ scr->get_script_signal_list(&sigs);
+ }
+ }
+ }
+
for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) {
result.insert("\""+E->get().name+"\"");
}
+
+ } else if (p_argidx==2){
+
+
+ if (context._class) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ result.insert("\""+context._class->functions[i]->name+"\"");
+ }
+ }
}
/*if (p_argidx==2) {
@@ -1976,7 +1964,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
if (!context._class->owner)
ci.value=context.base;
- _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint);
+ _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint);
//guess type..
/*
List<MethodInfo> methods;
@@ -1999,7 +1987,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
GDCompletionIdentifier ci;
if (_guess_expression_type(context,op->arguments[0],p_line,ci)) {
- _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint);
+ _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint);
return;
}
@@ -2103,10 +2091,8 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No
}
Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base_path, Object*p_owner, List<String>* r_options, String &r_call_hint) {
- //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>"));
GDParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
Error err = p.parse(p_code,p_base_path,false,"",true);
bool isfunction=false;
@@ -2122,10 +2108,8 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
switch(p.get_completion_type()) {
case GDParser::COMPLETION_NONE: {
- print_line("No completion");
} break;
case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
- print_line("Built in type constant");
List<StringName> constants;
Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(),&constants);
for(List<StringName>::Element *E=constants.front();E;E=E->next()) {
@@ -2141,7 +2125,6 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
_find_identifiers(context,p.get_completion_line(),isfunction,options);
} break;
case GDParser::COMPLETION_PARENT_FUNCTION: {
- print_line("parent function");
} break;
case GDParser::COMPLETION_METHOD:
@@ -2210,7 +2193,6 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
if (code!="") {
//if there is code, parse it. This way is slower but updates in real-time
GDParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false);
@@ -2427,7 +2409,24 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
}
}
} break;
+ case GDParser::COMPLETION_YIELD: {
+
+ const GDParser::Node *node = p.get_completion_node();
+
+ GDCompletionIdentifier t;
+ if (!_guess_expression_type(context,node,p.get_completion_line(),t))
+ break;
+ if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+
+ List<MethodInfo> sigs;
+ ObjectTypeDB::get_signal_list(t.obj_type,&sigs);
+ for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) {
+ options.insert("\""+E->get().name+"\"");
+ }
+ }
+
+ } break;
}
diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp
index 04522aadc2..b2cc6341c1 100644
--- a/modules/gdscript/gd_function.cpp
+++ b/modules/gdscript/gd_function.cpp
@@ -372,8 +372,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language()==GDScriptLanguage::get_singleton()) {
- GDInstance *ins = static_cast<GDInstance*>(obj_A->get_script_instance());
- GDScript *cmp = ins->script.ptr();
+ GDScript *cmp = static_cast<GDScript*>(obj_A->get_script_instance()->get_script().ptr());
//bool found=false;
while(cmp) {
@@ -654,10 +653,10 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
if (call_ret) {
GET_VARIANT_PTR(ret,argc);
- *ret = base->call(*methodname,(const Variant**)argptrs,argc,err);
+ base->call_ptr(*methodname,(const Variant**)argptrs,argc,ret,err);
} else {
- base->call(*methodname,(const Variant**)argptrs,argc,err);
+ base->call_ptr(*methodname,(const Variant**)argptrs,argc,NULL,err);
}
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -727,7 +726,12 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
String methodstr = GDFunctions::get_func_name(func);
- err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs);
+ if (dst->get_type()==Variant::STRING) {
+ //call provided error string
+ err_text="Error calling built-in function '"+methodstr+"': "+String(*dst);
+ } else {
+ err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs);
+ }
break;
}
ip+=argc+1;
@@ -841,6 +845,8 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a
gdfs->state._class=_class;
gdfs->state.ip=ip+ipofs;
gdfs->state.line=line;
+ gdfs->state.instance_id=(p_instance && p_instance->get_owner())?p_instance->get_owner()->get_instance_ID():0;
+ gdfs->state.script_id=_class->get_instance_ID();
//gdfs->state.result_pos=ip+ipofs-1;
gdfs->state.defarg=defarg;
gdfs->state.instance=p_instance;
@@ -1303,6 +1309,7 @@ GDFunction::GDFunction() : function_list(this) {
_stack_size=0;
_call_size=0;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
name="<anonymous>";
#ifdef DEBUG_ENABLED
_func_cname=NULL;
@@ -1347,6 +1354,18 @@ GDFunction::~GDFunction() {
Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+#ifdef DEBUG_ENABLED
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
Variant arg;
r_error.error=Variant::CallError::CALL_OK;
@@ -1393,6 +1412,17 @@ bool GDFunctionState::is_valid() const {
Variant GDFunctionState::resume(const Variant& p_arg) {
ERR_FAIL_COND_V(!function,Variant());
+#ifdef DEBUG_ENABLED
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
state.result=p_arg;
Variant::CallError err;
diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h
index 1f790eaadc..f1c5b13ca1 100644
--- a/modules/gdscript/gd_function.h
+++ b/modules/gdscript/gd_function.h
@@ -7,6 +7,7 @@
#include "variant.h"
#include "string_db.h"
#include "reference.h"
+#include "script_language.h"
class GDInstance;
class GDScript;
@@ -64,6 +65,14 @@ public:
ADDR_TYPE_NIL=8
};
+ enum RPCMode {
+ RPC_DISABLED,
+ RPC_ENABLED,
+ RPC_SYNC,
+ RPC_SYNC_MASTER,
+ RPC_SYNC_SLAVE
+ };
+
struct StackDebug {
int line;
@@ -91,6 +100,8 @@ friend class GDCompiler;
int _call_size;
int _initial_line;
bool _static;
+ ScriptInstance::RPCMode rpc_mode;
+
GDScript *_script;
StringName name;
@@ -136,6 +147,9 @@ public:
struct CallState {
+ ObjectID instance_id; //by debug only
+ ObjectID script_id;
+
GDInstance *instance;
Vector<uint8_t> stack;
int stack_size;
@@ -160,6 +174,7 @@ public:
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
GDScript *get_script() const { return _script; }
+ StringName get_source() const { return source; }
void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const;
@@ -181,6 +196,7 @@ public:
Variant call(GDInstance *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; }
GDFunction();
~GDFunction();
};
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
index 1c05a71d01..a565e866d0 100644
--- a/modules/gdscript/gd_functions.cpp
+++ b/modules/gdscript/gd_functions.cpp
@@ -87,6 +87,7 @@ const char *GDFunctions::get_func_name(Function p_func) {
"funcref",
"convert",
"typeof",
+ "type_exists",
"str",
"print",
"printt",
@@ -120,11 +121,13 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_arg_count<m_count) {\
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;\
r_error.argument=m_count;\
+ r_ret=Variant();\
return;\
}\
if (p_arg_count>m_count) {\
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;\
r_error.argument=m_count;\
+ r_ret=Variant();\
return;\
}
@@ -133,6 +136,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
r_error.argument=m_arg;\
r_error.expected=Variant::REAL;\
+ r_ret=Variant();\
return;\
}
@@ -244,6 +248,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::REAL;
+ r_ret=Variant();
}
} break;
case MATH_SIGN: {
@@ -261,6 +266,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::REAL;
+ r_ret=Variant();
}
} break;
case MATH_POW: {
@@ -298,7 +304,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
case MATH_DECIMALS: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
- r_ret=Math::decimals(*p_args[0]);
+ r_ret=Math::step_decimals(*p_args[0]);
} break;
case MATH_STEPIFY: {
VALIDATE_ARG_COUNT(2);
@@ -442,6 +448,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
return;
}
@@ -479,7 +486,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
- r_ret=Variant();
+ r_ret=Variant();
return;
}
@@ -495,7 +502,6 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
Ref<FuncRef> fr = memnew( FuncRef);
- Object *obj = *p_args[0];
fr->set_instance(*p_args[0]);
fr->set_function(*p_args[1]);
@@ -508,8 +514,11 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
int type=*p_args[1];
if (type<0 || type>=Variant::VARIANT_MAX) {
- ERR_PRINT("Invalid type argument to convert()");
- r_ret=Variant::NIL;
+ r_ret=RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return;
} else {
@@ -523,6 +532,12 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_ret = p_args[0]->get_type();
} break;
+ case TYPE_EXISTS: {
+
+ VALIDATE_ARG_COUNT(1);
+ r_ret = ObjectTypeDB::type_exists(*p_args[0]);
+
+ } break;
case TEXT_STR: {
String str;
@@ -638,7 +653,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::STRING;
- r_ret=Variant();
+ r_ret="Parse error at line "+itos(line)+": "+errs;
+ return;
}
} break;
@@ -652,7 +668,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::NIL;
- r_ret=Variant();
+ r_ret="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
return;
}
@@ -680,11 +696,10 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
ByteArray::Read r=varr.read();
Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
if (err!=OK) {
- ERR_PRINT("Not enough bytes for decoding..");
+ r_ret=RTR("Not enough bytes for decoding bytes, or invalid format.");
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::RAW_ARRAY;
- r_ret=Variant();
return;
}
@@ -701,6 +716,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument=1;
+ r_ret=Variant();
} break;
case 1: {
@@ -759,9 +775,9 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
int incr=*p_args[2];
if (incr==0) {
- ERR_EXPLAIN("step argument is zero!");
+ r_ret=RTR("step argument is zero!");
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
- ERR_FAIL();
+ return;
}
Array arr(true);
@@ -812,6 +828,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument=3;
+ r_ret=Variant();
+
} break;
}
@@ -847,8 +865,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- ERR_PRINT("Not a script with an instance");
-
+ r_ret=RTR("Not a script with an instance");
+ return;
} else {
GDInstance *ins = static_cast<GDInstance*>(obj->get_script_instance());
@@ -858,7 +876,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- ERR_PRINT("Not based on a script");
+ r_ret=RTR("Not based on a script");
return;
}
@@ -879,8 +897,10 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
- print_line("PATH: "+p->path);
- ERR_PRINT("Not based on a resource file");
+ r_ret=Variant();
+
+
+ r_ret=RTR("Not based on a resource file");
return;
}
@@ -926,6 +946,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::DICTIONARY;
+ r_ret=Variant();
+
return;
}
@@ -936,6 +958,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=RTR("Invalid instance dictionary format (missing @path)");
+
return;
}
@@ -945,6 +969,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=RTR("Invalid instance dictionary format (can't load script at @path)");
return;
}
@@ -955,6 +980,8 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
+ r_ret=RTR("Invalid instance dictionary format (invalid script at @path)");
return;
}
@@ -971,20 +998,22 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
+ r_ret=Variant();
+ r_ret=RTR("Invalid instance dictionary (invalid subclasses)");
return;
}
}
r_ret = gdscr->_new(NULL,0,r_error);
- GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance());
- Ref<GDScript> gd_ref = ins->get_script();
+ GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = ins->get_script();
- for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if(d.has(E->key())) {
- ins->members[E->get().index] = d[E->key()];
- }
- }
+ for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
+ if(d.has(E->key())) {
+ ins->members[E->get().index] = d[E->key()];
+ }
+ }
} break;
case HASH: {
@@ -998,11 +1027,15 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_arg_count<3) {
r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument=3;
+ r_ret=Variant();
+
return;
}
if (p_arg_count>4) {
r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument=4;
+ r_ret=Variant();
+
return;
}
@@ -1010,11 +1043,11 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
VALIDATE_ARG_NUM(1);
VALIDATE_ARG_NUM(2);
- Color color(*p_args[0],*p_args[1],*p_args[2]);
+ Color color((float)*p_args[0]/255.0f,(float)*p_args[1]/255.0f,(float)*p_args[2]/255.0f);
if (p_arg_count==4) {
VALIDATE_ARG_NUM(3);
- color.a=*p_args[3];
+ color.a=(float)*p_args[3]/255.0f;
}
r_ret=color;
@@ -1036,6 +1069,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
if (p_args[0]->get_type()!=Variant::INT && p_args[0]->get_type()!=Variant::REAL) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
+ r_error.expected=Variant::INT;
r_ret=Variant();
break;
}
@@ -1098,6 +1132,7 @@ bool GDFunctions::is_deterministic(Function p_func) {
case LOGIC_NEAREST_PO2:
case TYPE_CONVERT:
case TYPE_OF:
+ case TYPE_EXISTS:
case TEXT_STR:
case COLOR8:
// enable for debug only, otherwise not desirable - case GEN_RANGE:
@@ -1281,12 +1316,12 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_SEED: {
- MethodInfo mi("seed",PropertyInfo(Variant::REAL,"seed"));
+ MethodInfo mi("seed",PropertyInfo(Variant::INT,"seed"));
mi.return_val.type=Variant::NIL;
return mi;
} break;
case MATH_RANDSEED: {
- MethodInfo mi("rand_seed",PropertyInfo(Variant::REAL,"seed"));
+ MethodInfo mi("rand_seed",PropertyInfo(Variant::INT,"seed"));
mi.return_val.type=Variant::ARRAY;
return mi;
} break;
@@ -1361,6 +1396,13 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TYPE_EXISTS: {
+
+ MethodInfo mi("type_exists",PropertyInfo(Variant::STRING,"type"));
+ mi.return_val.type=Variant::BOOL;
+ return mi;
+
+ } break;
case TEXT_STR: {
MethodInfo mi("str",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h
index 8c88472567..c78956fe20 100644
--- a/modules/gdscript/gd_functions.h
+++ b/modules/gdscript/gd_functions.h
@@ -81,6 +81,7 @@ public:
FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
+ TYPE_EXISTS,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINT_TABBED,
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 9e90027a70..e5a8dc0152 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -31,6 +31,7 @@
#include "io/resource_loader.h"
#include "os/file_access.h"
#include "script_language.h"
+#include "gd_script.h"
template<class T>
T* GDParser::alloc_node() {
@@ -216,7 +217,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
}
-GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) {
+GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign,bool p_parsing_constant) {
// Vector<Node*> expressions;
// Vector<OperatorNode::Operator> operators;
@@ -225,6 +226,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
Node *expr=NULL;
+ int op_line = tokenizer->get_token_line(); // when operators are created at the bottom, the line might have been changed (\n found)
+
while(true) {
@@ -243,7 +246,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//subexpression ()
tokenizer->advance();
parenthesis++;
- Node* subexpr = _parse_expression(p_parent,p_static);
+ Node* subexpr = _parse_expression(p_parent,p_static,p_allow_assign,p_parsing_constant);
parenthesis--;
if (!subexpr)
return NULL;
@@ -375,6 +378,21 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+
+
+ completion_cursor=StringName();
+ completion_node=object;
+ completion_type=COMPLETION_YIELD;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_argument=0;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+ }
+
Node *signal = _parse_and_reduce_expression(p_parent,p_static);
if (!signal)
return NULL;
@@ -477,20 +495,30 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
//identifier (reference)
- const ClassNode* cln = static_cast<const ClassNode*>(get_parse_tree());
+ const ClassNode* cln = current_class;
bool bfn = false;
StringName identifier;
if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) {
}
- for( int i=0; i<cln->constant_expressions.size(); ++i ) {
+ if (p_parsing_constant) {
+ for( int i=0; i<cln->constant_expressions.size(); ++i ) {
- if( cln->constant_expressions[i].identifier == identifier ) {
+ if( cln->constant_expressions[i].identifier == identifier ) {
- expr = cln->constant_expressions[i].expression;
- bfn = true;
- break;
+ expr = cln->constant_expressions[i].expression;
+ bfn = true;
+ break;
+ }
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ //check from constants
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value = GDScriptLanguage::get_singleton()->get_global_array()[ GDScriptLanguage::get_singleton()->get_global_map()[identifier] ];
+ expr=constant;
+ bfn = true;
}
}
@@ -503,8 +531,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
} else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
//single prefix operators like !expr -expr ++expr --expr
- OperatorNode *op = alloc_node<OperatorNode>();
-
+ alloc_node<OperatorNode>();
Expression e;
e.is_op=true;
@@ -567,7 +594,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
_set_error("',' or ']' expected");
return NULL;
}
- Node *n = _parse_expression(arr,p_static);
+ Node *n = _parse_expression(arr,p_static,p_allow_assign,p_parsing_constant);
if (!n)
return NULL;
arr->elements.push_back(n);
@@ -674,7 +701,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expecting=DICT_EXPECT_VALUE;
} else {
//python/js style more flexible
- key = _parse_expression(dict,p_static);
+ key = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!key)
return NULL;
expecting=DICT_EXPECT_COLON;
@@ -682,7 +709,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (expecting==DICT_EXPECT_VALUE) {
- Node *value = _parse_expression(dict,p_static);
+ Node *value = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!value)
return NULL;
expecting=DICT_EXPECT_COMMA;
@@ -730,7 +757,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//find list [ or find dictionary {
- print_line("found bug?");
+ //print_line("found bug?");
_set_error("Error parsing expression, misplaced: "+String(tokenizer->get_token_name(tokenizer->get_token())));
return NULL; //nothing
@@ -833,7 +860,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance(1);
- Node *subexpr = _parse_expression(op,p_static);
+ Node *subexpr = _parse_expression(op,p_static,p_allow_assign,p_parsing_constant);
if (!subexpr) {
return NULL;
}
@@ -1041,6 +1068,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[i].op;
op->arguments.push_back(expression[i+1].node);
+ op->line=op_line; //line might have been changed from a \n
expression[i].is_op=false;
expression[i].node=op;
expression.remove(i+1);
@@ -1056,6 +1084,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
if (expression[next_op-1].is_op) {
@@ -1069,8 +1098,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
// can be followed by an unary op in a valid combination,
// due to how precedence works, unaries will always dissapear first
- _set_error("Parser bug..");
-
+ _set_error("Unexpected two consecutive operators.");
+ return NULL;
}
@@ -1258,6 +1287,8 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
} break;
}
+ error_line=op->line;
+
return p_node;
}
@@ -1293,6 +1324,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get(cb->value,&valid);
if (!valid) {
_set_error("invalid index in constant expression");
+ error_line=op->line;
return op;
}
@@ -1330,6 +1362,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get_named(ib->name,&valid);
if (!valid) {
_set_error("invalid index '"+String(ib->name)+"' in constant expression");
+ error_line=op->line;
return op;
}
@@ -1359,6 +1392,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
if (op->arguments[0]->type==Node::TYPE_CONSTANT) {
_set_error("Can't assign to constant",tokenizer->get_token_line()-1);
+ error_line=op->line;
return op;
}
@@ -1374,6 +1408,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,Variant(),res,valid);\
if (!valid) {\
_set_error("Invalid operand for unary operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1386,6 +1421,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,static_cast<ConstantNode*>(op->arguments[1])->value,res,valid);\
if (!valid) {\
_set_error("Invalid operands for operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1432,7 +1468,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const,bool p_allow_assign) {
- Node* expr=_parse_expression(p_parent,p_static,p_allow_assign);
+ Node* expr=_parse_expression(p_parent,p_static,p_allow_assign,p_reduce_const);
if (!expr || error_set)
return NULL;
expr = _reduce_expression(expr,p_reduce_const);
@@ -1526,6 +1562,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
tokenizer->advance();
+ if(tokenizer->get_token()==GDTokenizer::TK_SEMICOLON) {
+ // Ignore semicolon after 'pass'
+ tokenizer->advance();
+ }
} break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -2035,6 +2075,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (error_set)
return;
+
if (indent_level>tab_level.back()->get()) {
p_class->end_line=tokenizer->get_token_line();
return; //go back a level
@@ -2331,6 +2372,9 @@ void GDParser::_parse_class(ClassNode *p_class) {
function->_static=_static;
function->line=fnline;
+ function->rpc_mode=rpc_mode;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (_static)
p_class->static_functions.push_back(function);
@@ -2408,6 +2452,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
current_export.type=type;
+ current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
// hint expected next!
@@ -2782,6 +2827,8 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export.type=Variant::OBJECT;
current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+
current_export.hint_string=identifier;
tokenizer->advance();
@@ -2799,25 +2846,101 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
- if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_ONREADY && tokenizer->get_token()!=GDTokenizer::TK_PR_REMOTE && tokenizer->get_token()!=GDTokenizer::TK_PR_MASTER && tokenizer->get_token()!=GDTokenizer::TK_PR_SLAVE && tokenizer->get_token()!=GDTokenizer::TK_PR_SYNC) {
current_export=PropertyInfo();
- _set_error("Expected 'var'.");
+ _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'.");
return;
}
- }; //fallthrough to var
+ continue;
+ } break;
case GDTokenizer::TK_PR_ONREADY: {
- if (token==GDTokenizer::TK_PR_ONREADY) {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_REMOTE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
_set_error("Expected 'var'.");
return;
}
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
}
- }; //fallthrough to var
+ rpc_mode=ScriptInstance::RPC_MODE_REMOTE;
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_MASTER: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_MASTER;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SLAVE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SLAVE;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SYNC;
+ continue;
+ } break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -2841,8 +2964,12 @@ void GDParser::_parse_class(ClassNode *p_class) {
member.expression=NULL;
member._export.name=member.identifier;
member.line=tokenizer->get_token_line();
+ member.rpc_mode=rpc_mode;
+
tokenizer->advance();
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
@@ -2901,6 +3028,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
member._export.type=cn->value.get_type();
+ member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
}
}
#ifdef TOOLS_ENABLED
@@ -3184,6 +3312,7 @@ void GDParser::clear() {
current_class=NULL;
completion_found=false;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
current_function=NULL;
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 6c49c1df52..9e6f6e6765 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -33,6 +33,7 @@
#include "gd_functions.h"
#include "map.h"
#include "object.h"
+#include "script_language.h"
class GDParser {
public:
@@ -88,6 +89,7 @@ public:
StringName getter;
int line;
Node *expression;
+ ScriptInstance::RPCMode rpc_mode;
};
struct Constant {
StringName identifier;
@@ -119,12 +121,13 @@ public:
struct FunctionNode : public Node {
bool _static;
+ ScriptInstance::RPCMode rpc_mode;
StringName name;
Vector<StringName> arguments;
Vector<Node*> default_values;
BlockNode *body;
- FunctionNode() { type=TYPE_FUNCTION; _static=false; }
+ FunctionNode() { type=TYPE_FUNCTION; _static=false; rpc_mode=ScriptInstance::RPC_MODE_DISABLED; }
};
@@ -375,7 +378,8 @@ public:
COMPLETION_METHOD,
COMPLETION_CALL_ARGUMENTS,
COMPLETION_INDEX,
- COMPLETION_VIRTUAL_FUNC
+ COMPLETION_VIRTUAL_FUNC,
+ COMPLETION_YIELD,
};
@@ -428,6 +432,9 @@ private:
PropertyInfo current_export;
+ ScriptInstance::RPCMode rpc_mode;
+
+
void _set_error(const String& p_error, int p_line=-1, int p_column=-1);
bool _recover_from_completion();
@@ -435,7 +442,7 @@ private:
bool _parse_arguments(Node* p_parent, Vector<Node*>& p_args, bool p_static, bool p_can_codecomplete=false);
bool _enter_indent_block(BlockNode *p_block=NULL);
bool _parse_newline();
- Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false);
+ Node* _parse_expression(Node *p_parent, bool p_static, bool p_allow_assign=false, bool p_parsing_constant=false);
Node* _reduce_expression(Node *p_node,bool p_to_const=false);
Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index dcd0641f76..b97a0fcbb6 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -179,6 +179,15 @@ bool GDScript::can_instance() const {
}
+Ref<Script> GDScript::get_base_script() const {
+
+ if (_base) {
+ return Ref<GDScript>( _base );
+ } else {
+ return Ref<Script>();
+ }
+}
+
StringName GDScript::get_instance_base_type() const {
if (native.is_valid())
@@ -249,6 +258,85 @@ void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
}*/
#endif
+
+void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
+
+ for (const Map<StringName,GDFunction*>::Element *E=member_functions.front();E;E=E->next()) {
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get()->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.type=Variant::NIL; //variant
+ arg.name=E->get()->get_argument_name(i);
+ mi.arguments.push_back(arg);
+ }
+
+ mi.return_val.name="Variant";
+ p_list->push_back(mi);
+ }
+}
+
+void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+
+ const GDScript *sptr=this;
+ List<PropertyInfo> props;
+
+ while(sptr) {
+
+ Vector<_GDScriptMemberSort> msort;
+ for(Map<StringName,PropertyInfo>::Element *E=sptr->member_info.front();E;E=E->next()) {
+
+ _GDScriptMemberSort ms;
+ ERR_CONTINUE(!sptr->member_indices.has(E->key()));
+ ms.index=sptr->member_indices[E->key()].index;
+ ms.name=E->key();
+ msort.push_back(ms);
+
+ }
+
+ msort.sort();
+ msort.invert();
+ for(int i=0;i<msort.size();i++) {
+
+ props.push_front(sptr->member_info[msort[i].name]);
+
+ }
+
+ sptr = sptr->_base;
+ }
+
+ for (List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
+ p_list->push_back(E->get());
+ }
+
+}
+
+bool GDScript::has_method(const StringName& p_method) const {
+
+ return member_functions.has(p_method);
+}
+
+MethodInfo GDScript::get_method_info(const StringName& p_method) const {
+
+ const Map<StringName,GDFunction*>::Element *E=member_functions.find(p_method);
+ if (!E)
+ return MethodInfo();
+
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get()->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.type=Variant::NIL; //variant
+ arg.name=E->get()->get_argument_name(i);
+ mi.arguments.push_back(arg);
+ }
+
+ mi.return_val.name="Variant";
+ return mi;
+
+}
+
+
bool GDScript::get_property_default_value(const StringName& p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
@@ -663,7 +751,7 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
void GDScript::_bind_methods() {
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo("new"));
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new"));
ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code);
@@ -874,6 +962,10 @@ GDScript::~GDScript() {
memdelete( E->get() );
}
+ for (Map<StringName,Ref<GDScript> >::Element *E=subclasses.front();E;E=E->next()) {
+ E->get()->_owner=NULL; //bye, you are no longer owned cause I died
+ }
+
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->lock) {
GDScriptLanguage::get_singleton()->lock->lock();
@@ -960,11 +1052,16 @@ bool GDInstance::get(const StringName& p_name, Variant &r_ret) const {
}
{
- const Map<StringName,Variant>::Element *E = script->constants.find(p_name);
- if (E) {
- r_ret=E->get();
- return true; //index found
+ const GDScript *sl = sptr;
+ while(sl) {
+ const Map<StringName,Variant>::Element *E = sl->constants.find(p_name);
+ if (E) {
+ r_ret=E->get();
+ return true; //index found
+
+ }
+ sl=sl->_base;
}
}
@@ -1213,6 +1310,8 @@ void GDInstance::call_multilevel_reversed(const StringName& p_method,const Varia
}
}
+
+
void GDInstance::notification(int p_notification) {
//notification is not virutal, it gets called at ALL levels just like in C.
@@ -1245,6 +1344,46 @@ ScriptLanguage *GDInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
+GDInstance::RPCMode GDInstance::get_rpc_mode(const StringName& p_method) const {
+
+ const GDScript *cscript = script.ptr();
+
+ while(cscript) {
+ const Map<StringName,GDFunction*>::Element *E=cscript->member_functions.find(p_method);
+ if (E) {
+
+ if (E->get()->get_rpc_mode()!=RPC_MODE_DISABLED) {
+ return E->get()->get_rpc_mode();
+ }
+
+ }
+ cscript=cscript->_base;
+ }
+
+ return RPC_MODE_DISABLED;
+}
+
+GDInstance::RPCMode GDInstance::get_rset_mode(const StringName& p_variable) const {
+
+ const GDScript *cscript = script.ptr();
+
+ while(cscript) {
+ const Map<StringName,GDScript::MemberInfo>::Element *E=cscript->member_indices.find(p_variable);
+ if (E) {
+
+ if (E->get().rpc_mode) {
+ return E->get().rpc_mode;
+ }
+
+ }
+ cscript=cscript->_base;
+ }
+
+ return RPC_MODE_DISABLED;
+}
+
+
+
void GDInstance::reload_members() {
#ifdef DEBUG_ENABLED
@@ -1558,6 +1697,127 @@ void GDScriptLanguage::reload_all_scripts() {
#endif
}
+
+void GDScriptLanguage::reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload) {
+
+
+#ifdef DEBUG_ENABLED
+
+ if (lock) {
+ lock->lock();
+ }
+
+ List<Ref<GDScript> > scripts;
+
+ SelfList<GDScript> *elem=script_list.first();
+ while(elem) {
+ if (elem->self()->get_path().is_resource_file()) {
+
+ scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
+ }
+ elem=elem->next();
+ }
+
+ if (lock) {
+ lock->unlock();
+ }
+
+ //when someone asks you why dynamically typed languages are easier to write....
+
+ Map< Ref<GDScript>, Map<ObjectID,List<Pair<StringName,Variant> > > > to_reload;
+
+ //as scripts are going to be reloaded, must proceed without locking here
+
+ scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
+
+ for(List<Ref<GDScript> >::Element *E=scripts.front();E;E=E->next()) {
+
+ bool reload = E->get()==p_script || to_reload.has(E->get()->get_base());
+
+ if (!reload)
+ continue;
+
+ to_reload.insert(E->get(),Map<ObjectID,List<Pair<StringName,Variant> > >());
+
+ if (!p_soft_reload) {
+
+ //save state and remove script from instances
+ Map<ObjectID,List<Pair<StringName,Variant> > >& map = to_reload[E->get()];
+
+ while(E->get()->instances.front()) {
+ Object *obj = E->get()->instances.front()->get();
+ //save instance info
+ List<Pair<StringName,Variant> > state;
+ if (obj->get_script_instance()) {
+
+ obj->get_script_instance()->get_property_state(state);
+ map[obj->get_instance_ID()]=state;
+ obj->set_script(RefPtr());
+ }
+ }
+
+ //same thing for placeholders
+#ifdef TOOLS_ENABLED
+
+ while(E->get()->placeholders.size()) {
+
+ Object *obj = E->get()->placeholders.front()->get()->get_owner();
+ //save instance info
+ List<Pair<StringName,Variant> > state;
+ if (obj->get_script_instance()) {
+
+ obj->get_script_instance()->get_property_state(state);
+ map[obj->get_instance_ID()]=state;
+ obj->set_script(RefPtr());
+ }
+ }
+#endif
+
+ for(Map<ObjectID,List<Pair<StringName,Variant> > >::Element *F=E->get()->pending_reload_state.front();F;F=F->next()) {
+ map[F->key()]=F->get(); //pending to reload, use this one instead
+ }
+ }
+ }
+
+ for(Map< Ref<GDScript>, Map<ObjectID,List<Pair<StringName,Variant> > > >::Element *E=to_reload.front();E;E=E->next()) {
+
+ Ref<GDScript> scr = E->key();
+ scr->reload(p_soft_reload);
+
+ //restore state if saved
+ for (Map<ObjectID,List<Pair<StringName,Variant> > >::Element *F=E->get().front();F;F=F->next()) {
+
+ Object *obj = ObjectDB::get_instance(F->key());
+ if (!obj)
+ continue;
+
+ if (!p_soft_reload) {
+ //clear it just in case (may be a pending reload state)
+ obj->set_script(RefPtr());
+ }
+ obj->set_script(scr.get_ref_ptr());
+ if (!obj->get_script_instance()) {
+ //failed, save reload state for next time if not saved
+ if (!scr->pending_reload_state.has(obj->get_instance_ID())) {
+ scr->pending_reload_state[obj->get_instance_ID()]=F->get();
+ }
+ continue;
+ }
+
+ for (List<Pair<StringName,Variant> >::Element *G=F->get().front();G;G=G->next()) {
+ obj->get_script_instance()->set(G->get().first,G->get().second);
+ }
+
+ scr->pending_reload_state.erase(obj->get_instance_ID()); //as it reloaded, remove pending state
+ }
+
+ //if instance states were saved, set them!
+ }
+
+
+#endif
+}
+
void GDScriptLanguage::frame() {
// print_line("calls: "+itos(calls));
@@ -1602,6 +1862,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"false",
"float",
"int",
+ "bool",
"null",
"PI",
"self",
@@ -1634,6 +1895,10 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"pass",
"return",
"while",
+ "remote",
+ "sync",
+ "master",
+ "slave",
0};
@@ -1791,6 +2056,11 @@ Error ResourceFormatSaverGDScript::save(const String &p_path,const RES& p_resour
}
file->close();
memdelete(file);
+
+ if (ScriptServer::is_reload_scripts_on_save_enabled()) {
+ GDScriptLanguage::get_singleton()->reload_tool_script(p_resource,false);
+ }
+
return OK;
}
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index f052d13685..0c3e1eb614 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -64,6 +64,7 @@ class GDScript : public Script {
int index;
StringName setter;
StringName getter;
+ ScriptInstance::RPCMode rpc_mode;
};
friend class GDInstance;
@@ -120,7 +121,11 @@ friend class GDScriptLanguage;
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
+#ifdef DEBUG_ENABLED
+
+ Map<ObjectID,List<Pair<StringName,Variant> > > pending_reload_state;
+#endif
bool _update_exports();
@@ -157,6 +162,8 @@ public:
Variant _new(const Variant** p_args,int p_argcount,Variant::CallError& r_error);
virtual bool can_instance() const;
+ virtual Ref<Script> get_base_script() const;
+
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance* instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
@@ -177,6 +184,13 @@ public:
bool get_property_default_value(const StringName& p_property,Variant& r_value) const;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName& p_method) const;
+ virtual MethodInfo get_method_info(const StringName& p_method) const;
+
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+
+
virtual ScriptLanguage *get_language() const;
GDScript();
@@ -202,6 +216,8 @@ friend class GDCompiler;
public:
+ _FORCE_INLINE_ Object* get_owner() { return owner; }
+
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
@@ -226,6 +242,10 @@ public:
void reload_members();
+ virtual RPCMode get_rpc_mode(const StringName& p_method) const;
+ virtual RPCMode get_rset_mode(const StringName& p_variable) const;
+
+
GDInstance();
~GDInstance();
@@ -240,29 +260,31 @@ class GDScriptLanguage : public ScriptLanguage {
Map<StringName,int> globals;
- struct CallLevel {
+ struct CallLevel {
- Variant *stack;
- GDFunction *function;
- GDInstance *instance;
- int *ip;
- int *line;
+ Variant *stack;
+ GDFunction *function;
+ GDInstance *instance;
+ int *ip;
+ int *line;
- };
+ };
- int _debug_parse_err_line;
- String _debug_parse_err_file;
- String _debug_error;
- int _debug_call_stack_pos;
- int _debug_max_call_stack;
- CallLevel *_call_stack;
+ int _debug_parse_err_line;
+ String _debug_parse_err_file;
+ String _debug_error;
+ int _debug_call_stack_pos;
+ int _debug_max_call_stack;
+ CallLevel *_call_stack;
void _add_global(const StringName& p_name,const Variant& p_value);
Mutex *lock;
+
+
friend class GDScript;
SelfList<GDScript>::List script_list;
@@ -276,54 +298,54 @@ public:
int calls;
- bool debug_break(const String& p_error,bool p_allow_continue=true);
- bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
+ bool debug_break(const String& p_error,bool p_allow_continue=true);
+ bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
- _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
+ _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return; //no support for other threads than main for now
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
- if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
- ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
- if (_debug_call_stack_pos >= _debug_max_call_stack) {
- //stack overflow
- _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
- ScriptDebugger::get_singleton()->debug(this);
- return;
- }
+ if (_debug_call_stack_pos >= _debug_max_call_stack) {
+ //stack overflow
+ _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
- _call_stack[_debug_call_stack_pos].stack=p_stack;
- _call_stack[_debug_call_stack_pos].instance=p_instance;
- _call_stack[_debug_call_stack_pos].function=p_function;
- _call_stack[_debug_call_stack_pos].ip=p_ip;
- _call_stack[_debug_call_stack_pos].line=p_line;
- _debug_call_stack_pos++;
- }
+ _call_stack[_debug_call_stack_pos].stack=p_stack;
+ _call_stack[_debug_call_stack_pos].instance=p_instance;
+ _call_stack[_debug_call_stack_pos].function=p_function;
+ _call_stack[_debug_call_stack_pos].ip=p_ip;
+ _call_stack[_debug_call_stack_pos].line=p_line;
+ _debug_call_stack_pos++;
+ }
- _FORCE_INLINE_ void exit_function() {
+ _FORCE_INLINE_ void exit_function() {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return; //no support for other threads than main for now
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
- if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
- ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
- if (_debug_call_stack_pos==0) {
+ if (_debug_call_stack_pos==0) {
- _debug_error="Stack Underflow (Engine Bug)";
- ScriptDebugger::get_singleton()->debug(this);
- return;
- }
+ _debug_error="Stack Underflow (Engine Bug)";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
- _debug_call_stack_pos--;
- }
+ _debug_call_stack_pos--;
+ }
virtual Vector<StackInfo> debug_get_current_stack_info() {
- if (Thread::get_main_ID()!=Thread::get_caller_ID())
- return Vector<StackInfo>();
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return Vector<StackInfo>();
Vector<StackInfo> csi;
csi.resize(_debug_call_stack_pos);
@@ -365,7 +387,7 @@ public:
virtual void get_reserved_words(List<String> *p_words) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
- virtual String get_template(const String& p_class_name, const String& p_base_class_name) const;
+ virtual Ref<Script> get_template(const String& p_class_name, const String& p_base_class_name) const;
virtual bool 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=NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
@@ -389,6 +411,7 @@ public:
virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1);
virtual void reload_all_scripts();
+ virtual void reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload);
virtual void frame();
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 56eacfd20e..47e740b227 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -79,8 +79,8 @@ const char* GDTokenizer::token_names[TK_MAX]={
"for",
"do",
"while",
-"switch",
-"case",
+"switch (reserved)",
+"case (reserved)",
"break",
"continue",
"pass",
@@ -100,6 +100,10 @@ const char* GDTokenizer::token_names[TK_MAX]={
"yield",
"signal",
"breakpoint",
+"rpc",
+"sync",
+"master",
+"slave",
"'['",
"']'",
"'{'",
@@ -725,7 +729,7 @@ void GDTokenizerText::_advance() {
if (hexa_found) {
int val = str.hex_to_int();
_make_constant(val);
- } else if (period_found) {
+ } else if (period_found || exponent_found) {
real_t val = str.to_double();
//print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
@@ -865,6 +869,10 @@ void GDTokenizerText::_advance() {
{TK_PR_YIELD,"yield"},
{TK_PR_SIGNAL,"signal"},
{TK_PR_BREAKPOINT,"breakpoint"},
+ {TK_PR_REMOTE,"remote"},
+ {TK_PR_MASTER,"master"},
+ {TK_PR_SLAVE,"slave"},
+ {TK_PR_SYNC,"sync"},
{TK_PR_CONST,"const"},
//controlflow
{TK_CF_IF,"if"},
@@ -874,6 +882,7 @@ void GDTokenizerText::_advance() {
{TK_CF_WHILE,"while"},
{TK_CF_DO,"do"},
{TK_CF_SWITCH,"switch"},
+ {TK_CF_CASE,"case"},
{TK_CF_BREAK,"break"},
{TK_CF_CONTINUE,"continue"},
{TK_CF_RETURN,"return"},
@@ -1046,7 +1055,7 @@ void GDTokenizerText::advance(int p_amount) {
//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 10
+#define BYTECODE_VERSION 11
Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) {
@@ -1155,7 +1164,6 @@ Vector<uint8_t> GDTokenizerBuffer::parse_code_string(const String& p_code) {
GDTokenizerText tt;
tt.set_code(p_code);
int line=-1;
- int col=0;
while(true) {
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
index aaff573090..1815f82894 100644
--- a/modules/gdscript/gd_tokenizer.h
+++ b/modules/gdscript/gd_tokenizer.h
@@ -107,6 +107,10 @@ public:
TK_PR_YIELD,
TK_PR_SIGNAL,
TK_PR_BREAKPOINT,
+ TK_PR_REMOTE,
+ TK_PR_SYNC,
+ TK_PR_MASTER,
+ TK_PR_SLAVE,
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 2aea494f39..95b18cae4d 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -1,14 +1,31 @@
-/*************************************************/
-/* register_script_types.cpp */
-/*************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/*************************************************/
-/* Source code within this file is: */
-/* (c) 2007-2016 Juan Linietsky, Ariel Manzur */
-/* All Rights Reserved. */
-/*************************************************/
-
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gd_script.h"
@@ -141,7 +158,7 @@ void register_gdscript_types() {
void unregister_gdscript_types() {
-
+ ScriptServer::unregister_language(script_language_gd);
if (script_language_gd)
memdelete( script_language_gd );
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 503e723de2..6e73244b57 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -912,7 +912,6 @@ void GridMap::_octant_bake(const OctantKey &p_key, const Ref<TriangleMesh>& p_tm
int lc = p_lights.size();
const BakeLight* bl = p_lights.ptr();
float ofs = cell_size*0.02;
- float att = 0.2;
for(;V;V=V->next()) {
@@ -1809,6 +1808,7 @@ bool GridMap::is_using_baked_light() const{
}
+
GridMap::GridMap() {
cell_size=2;
@@ -1830,7 +1830,7 @@ GridMap::GridMap() {
baked_light_instance=NULL;
use_baked_light=false;
-
+ navigation = NULL;
}
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 87afe3d5a4..9bdad6713d 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -212,6 +212,18 @@ void GridMapEditor::_menu_option(int p_option) {
_update_areas_display();
update_areas();
} break;
+ case MENU_OPTION_SELECTION_DUPLICATE:
+ if (!(selection.active && input_action==INPUT_NONE))
+ return;
+ if (last_mouseover==Vector3(-1,-1,-1)) //nono mouseovering anythin
+ break;
+
+ input_action=INPUT_DUPLICATE;
+ selection.click=last_mouseover;
+ selection.current=last_mouseover;
+ selection.duplicate_rot=0;
+ _update_duplicate_indicator();
+ break;
case MENU_OPTION_SELECTION_CLEAR: {
if (!selection.active)
return;
@@ -377,7 +389,9 @@ bool GridMapEditor::do_input_action(Camera* p_camera,const Point2& p_point,bool
int item=node->get_cell_item(cell[0],cell[1],cell[2]);
if (item>=0) {
selected_pallete=item;
+ theme_pallete->set_current(item);
update_pallete();
+ _update_cursor_instance();
}
return true;
} if (input_action==INPUT_PAINT) {
@@ -530,29 +544,6 @@ bool GridMapEditor::forward_spatial_input_event(Camera* p_camera,const InputEven
if (edit_mode->get_selected()==0) { // regular click
switch (p_event.type) {
- case InputEvent::KEY: {
-
- if (p_event.key.pressed && p_event.key.scancode==KEY_D && p_event.key.mod.shift && selection.active && input_action==INPUT_NONE) {
-
- if (last_mouseover==Vector3(-1,-1,-1)) //nono mouseovering anythin
- return false;
-
- input_action=INPUT_DUPLICATE;
- selection.click=last_mouseover;
- selection.current=last_mouseover;
- selection.duplicate_rot=0;
- _update_duplicate_indicator();
-
-
- }
-
- if (p_event.key.pressed && p_event.key.scancode==KEY_DELETE && selection.active) {
-
- _delete_selection();
- return true;
- }
-
- } break;
case InputEvent::MOUSE_BUTTON: {
if (p_event.mouse_button.button_index==BUTTON_WHEEL_UP && (p_event.mouse_button.mod.command || p_event.mouse_button.mod.shift)) {
@@ -741,7 +732,7 @@ void GridMapEditor::update_pallete() {
}
float min_size = EDITOR_DEF("grid_map/preview_size",64);
- theme_pallete->set_min_icon_size(Size2(min_size, min_size));
+ theme_pallete->set_fixed_icon_size(Size2(min_size, min_size));
theme_pallete->set_fixed_column_width(min_size*3/2);
theme_pallete->set_max_text_lines(2);
@@ -851,6 +842,11 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
VS *vs = VS::get_singleton();
last_mouseover=Vector3(-1,-1,-1);
+ input_action=INPUT_NONE;
+ selection.active=false;
+ _update_selection_transform();
+ _update_duplicate_indicator();
+
spatial_editor = editor->get_editor_plugin_screen()->cast_to<SpatialEditorPlugin>();
if (!node) {
@@ -1247,7 +1243,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_item("Create Exterior Connector",MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR);
options->get_popup()->add_item("Erase Area",MENU_OPTION_REMOVE_AREA);
options->get_popup()->add_separator();
- options->get_popup()->add_item("Selection -> Clear",MENU_OPTION_SELECTION_CLEAR);
+ options->get_popup()->add_item("Selection -> Duplicate",MENU_OPTION_SELECTION_DUPLICATE,KEY_MASK_SHIFT+KEY_INSERT);
+ options->get_popup()->add_item("Selection -> Clear",MENU_OPTION_SELECTION_CLEAR,KEY_MASK_SHIFT+KEY_DELETE);
//options->get_popup()->add_separator();
//options->get_popup()->add_item("Configure",MENU_OPTION_CONFIGURE);
@@ -1266,7 +1263,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
settings_pick_distance->set_max(10000.0f);
settings_pick_distance->set_min(500.0f);
settings_pick_distance->set_step(1.0f);
- settings_pick_distance->set_val(EDITOR_DEF("gridmap_editor/pick_distance", 5000.0));
+ settings_pick_distance->set_val(EDITOR_DEF("grid_map/pick_distance", 5000.0));
settings_vbc->add_margin_child("Pick Distance:", settings_pick_distance);
clip_mode=CLIP_DISABLED;
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index fc43866ef3..535c51bcbf 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -167,6 +167,7 @@ class GridMapEditor : public VBoxContainer {
MENU_OPTION_DUPLICATE_SELECTS,
MENU_OPTION_SELECTION_MAKE_AREA,
MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
+ MENU_OPTION_SELECTION_DUPLICATE,
MENU_OPTION_SELECTION_CLEAR,
MENU_OPTION_REMOVE_AREA,
MENU_OPTION_GRIDMAP_SETTINGS
diff --git a/modules/ik/ik.cpp b/modules/ik/ik.cpp
index 6c383fdb55..35b3ba7e83 100644
--- a/modules/ik/ik.cpp
+++ b/modules/ik/ik.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
/* ik.cpp */
+/* Copyright (c) 2016 Sergey Lapin <slapinid@gmail.com> */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
-/* This file is Copyright (c) 2016 Sergey Lapin <slapinid@gmail.com> */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -241,7 +241,7 @@ void InverseKinematics::_notification(int p_what)
}
} break;
case NOTIFICATION_PROCESS: {
- float delta = get_process_delta_time();
+
Spatial *sksp = skel->cast_to<Spatial>();
if (!bound)
break;
diff --git a/modules/ik/ik.h b/modules/ik/ik.h
index 9daddb229d..b57d69b026 100644
--- a/modules/ik/ik.h
+++ b/modules/ik/ik.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* ik.h */
+/* ik.h */
+/* Copyright (c) 2016 Sergey Lapin <slapinid@gmail.com> */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
-/* This file is (c) 2016 Sergey Lapin <slapinid@gmail.com> */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/modules/register_module_types.h b/modules/register_module_types.h
index 3cc0422d80..683ce7c6b8 100644
--- a/modules/register_module_types.h
+++ b/modules/register_module_types.h
@@ -1,3 +1,31 @@
+/*************************************************************************/
+/* register_module_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 REGISTER_MODULE_TYPES_H
#define REGISTER_MODULE_TYPES_H
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
new file mode 100644
index 0000000000..403fe68f66
--- /dev/null
+++ b/modules/visual_script/SCsub
@@ -0,0 +1,5 @@
+Import('env')
+
+env.add_source_files(env.modules_sources,"*.cpp")
+
+Export('env')
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
new file mode 100644
index 0000000000..ea7e83378a
--- /dev/null
+++ b/modules/visual_script/config.py
@@ -0,0 +1,11 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
new file mode 100644
index 0000000000..ad54149b51
--- /dev/null
+++ b/modules/visual_script/register_types.cpp
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "visual_script.h"
+#include "visual_script_editor.h"
+#include "io/resource_loader.h"
+#include "visual_script_nodes.h"
+#include "visual_script_func_nodes.h"
+#include "visual_script_builtin_funcs.h"
+#include "visual_script_flow_control.h"
+#include "visual_script_yield_nodes.h"
+#include "visual_script_expression.h"
+
+
+VisualScriptLanguage *visual_script_language=NULL;
+
+
+void register_visual_script_types() {
+
+ visual_script_language=memnew( VisualScriptLanguage );
+ //script_language_gd->init();
+ ScriptServer::register_language(visual_script_language);
+
+ ObjectTypeDB::register_type<VisualScript>();
+ ObjectTypeDB::register_virtual_type<VisualScriptNode>();
+ ObjectTypeDB::register_virtual_type<VisualScriptFunctionState>();
+ ObjectTypeDB::register_type<VisualScriptFunction>();
+ ObjectTypeDB::register_type<VisualScriptOperator>();
+ ObjectTypeDB::register_type<VisualScriptVariableSet>();
+ ObjectTypeDB::register_type<VisualScriptVariableGet>();
+ ObjectTypeDB::register_type<VisualScriptConstant>();
+ ObjectTypeDB::register_type<VisualScriptIndexGet>();
+ ObjectTypeDB::register_type<VisualScriptIndexSet>();
+ ObjectTypeDB::register_type<VisualScriptGlobalConstant>();
+ ObjectTypeDB::register_type<VisualScriptClassConstant>();
+ ObjectTypeDB::register_type<VisualScriptMathConstant>();
+ ObjectTypeDB::register_type<VisualScriptBasicTypeConstant>();
+ ObjectTypeDB::register_type<VisualScriptEngineSingleton>();
+ ObjectTypeDB::register_type<VisualScriptSceneNode>();
+ ObjectTypeDB::register_type<VisualScriptSceneTree>();
+ ObjectTypeDB::register_type<VisualScriptResourcePath>();
+ ObjectTypeDB::register_type<VisualScriptSelf>();
+ ObjectTypeDB::register_type<VisualScriptCustomNode>();
+ ObjectTypeDB::register_type<VisualScriptSubCall>();
+ ObjectTypeDB::register_type<VisualScriptComment>();
+ ObjectTypeDB::register_type<VisualScriptConstructor>();
+ ObjectTypeDB::register_type<VisualScriptLocalVar>();
+ ObjectTypeDB::register_type<VisualScriptLocalVarSet>();
+ ObjectTypeDB::register_type<VisualScriptInputAction>();
+ ObjectTypeDB::register_type<VisualScriptDeconstruct>();
+ ObjectTypeDB::register_type<VisualScriptPreload>();
+ ObjectTypeDB::register_type<VisualScriptTypeCast>();
+
+
+ ObjectTypeDB::register_type<VisualScriptFunctionCall>();
+ ObjectTypeDB::register_type<VisualScriptPropertySet>();
+ ObjectTypeDB::register_type<VisualScriptPropertyGet>();
+// ObjectTypeDB::register_type<VisualScriptScriptCall>();
+ ObjectTypeDB::register_type<VisualScriptEmitSignal>();
+
+ ObjectTypeDB::register_type<VisualScriptReturn>();
+ ObjectTypeDB::register_type<VisualScriptCondition>();
+ ObjectTypeDB::register_type<VisualScriptWhile>();
+ ObjectTypeDB::register_type<VisualScriptIterator>();
+ ObjectTypeDB::register_type<VisualScriptSequence>();
+ ObjectTypeDB::register_type<VisualScriptInputFilter>();
+ ObjectTypeDB::register_type<VisualScriptSwitch >();
+
+ ObjectTypeDB::register_type<VisualScriptYield>();
+ ObjectTypeDB::register_type<VisualScriptYieldSignal>();
+
+ ObjectTypeDB::register_type<VisualScriptBuiltinFunc>();
+
+
+ ObjectTypeDB::register_type<VisualScriptExpression>();
+
+ register_visual_script_nodes();
+ register_visual_script_func_nodes();
+ register_visual_script_builtin_func_node();
+ register_visual_script_flow_control_nodes();
+ register_visual_script_yield_nodes();
+ register_visual_script_expression_node();
+
+#ifdef TOOLS_ENABLED
+ VisualScriptEditor::register_editor();
+#endif
+
+
+}
+
+void unregister_visual_script_types() {
+
+
+ unregister_visual_script_nodes();
+
+ ScriptServer::unregister_language(visual_script_language);
+
+#ifdef TOOLS_ENABLED
+ VisualScriptEditor::free_clipboard();
+#endif
+ if (visual_script_language)
+ memdelete( visual_script_language );
+
+}
diff --git a/modules/visual_script/register_types.h b/modules/visual_script/register_types.h
new file mode 100644
index 0000000000..0a5805eb0b
--- /dev/null
+++ b/modules/visual_script/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_visual_script_types();
+void unregister_visual_script_types();
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
new file mode 100644
index 0000000000..d0a933a11c
--- /dev/null
+++ b/modules/visual_script/visual_script.cpp
@@ -0,0 +1,2841 @@
+#include "visual_script.h"
+#include "visual_script_nodes.h"
+#include "scene/main/node.h"
+#include "os/os.h"
+#include "globals.h"
+
+
+
+//used by editor, this is not really saved
+void VisualScriptNode::set_breakpoint(bool p_breakpoint) {
+ breakpoint=p_breakpoint;
+}
+
+bool VisualScriptNode::is_breakpoint() const {
+
+ return breakpoint;
+}
+
+void VisualScriptNode::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_POSTINITIALIZE) {
+
+ int dvc = get_input_value_port_count();
+ for(int i=0;i<dvc;i++) {
+ Variant::Type expected = get_input_value_port_info(i).type;
+ Variant::CallError ce;
+ default_input_values.push_back(Variant::construct(expected,NULL,0,ce,false));
+ }
+ }
+}
+
+void VisualScriptNode::ports_changed_notify(){
+
+
+ default_input_values.resize( MAX(default_input_values.size(),get_input_value_port_count()) ); //let it grow as big as possible, we don't want to lose values on resize
+
+ emit_signal("ports_changed");
+
+}
+
+void VisualScriptNode::set_default_input_value(int p_port,const Variant& p_value) {
+
+ ERR_FAIL_INDEX(p_port,default_input_values.size());
+
+ default_input_values[p_port]=p_value;
+
+#ifdef TOOLS_ENABLED
+ for (Set<VisualScript*>::Element *E=scripts_used.front();E;E=E->next()) {
+ E->get()->set_edited(true);
+ }
+#endif
+
+}
+
+Variant VisualScriptNode::get_default_input_value(int p_port) const {
+
+ ERR_FAIL_INDEX_V(p_port,default_input_values.size(),Variant());
+ return default_input_values[p_port];
+}
+
+void VisualScriptNode::_set_default_input_values(Array p_values) {
+
+
+ default_input_values=p_values;
+}
+
+
+void VisualScriptNode::validate_input_default_values() {
+
+
+
+ default_input_values.resize(get_input_value_port_count());
+
+ //actually validate on save
+ for(int i=0;i<get_input_value_port_count();i++) {
+
+ Variant::Type expected = get_input_value_port_info(i).type;
+
+
+ if (expected==Variant::NIL || expected==default_input_values[i].get_type()) {
+ continue;
+ } else {
+ //not the same, reconvert
+ Variant::CallError ce;
+ Variant existing = default_input_values[i];
+ const Variant *existingp=&existing;
+ default_input_values[i] = Variant::construct(expected,&existingp,1,ce,false);
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ //could not convert? force..
+ default_input_values[i] = Variant::construct(expected,NULL,0,ce,false);
+ }
+ }
+ }
+}
+
+Array VisualScriptNode::_get_default_input_values() const {
+
+ //validate on save, since on load there is little info about this
+
+ return default_input_values;
+}
+
+
+
+void VisualScriptNode::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_visual_script:VisualScript"),&VisualScriptNode::get_visual_script);
+ ObjectTypeDB::bind_method(_MD("set_default_input_value","port_idx","value:Variant"),&VisualScriptNode::set_default_input_value);
+ ObjectTypeDB::bind_method(_MD("get_default_input_value:Variant","port_idx"),&VisualScriptNode::get_default_input_value);
+ ObjectTypeDB::bind_method(_MD("_set_default_input_values","values"),&VisualScriptNode::_set_default_input_values);
+ ObjectTypeDB::bind_method(_MD("_get_default_input_values"),&VisualScriptNode::_get_default_input_values);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY,"_default_input_values",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_default_input_values"),_SCS("_get_default_input_values"));
+ ADD_SIGNAL(MethodInfo("ports_changed"));
+}
+
+VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess* p_inputs,int p_output) const {
+
+ PropertyInfo pinfo = get_output_value_port_info(p_output);
+
+ TypeGuess tg;
+
+ tg.type=pinfo.type;
+ if (pinfo.hint==PROPERTY_HINT_RESOURCE_TYPE) {
+ tg.obj_type=pinfo.hint_string;
+ }
+
+ return tg;
+}
+
+Ref<VisualScript> VisualScriptNode::get_visual_script() const {
+
+ if (scripts_used.size())
+ return Ref<VisualScript>(scripts_used.front()->get());
+
+ return Ref<VisualScript>();
+}
+
+VisualScriptNode::VisualScriptNode() {
+ breakpoint=false;
+}
+
+////////////////
+
+/////////////////////
+
+VisualScriptNodeInstance::VisualScriptNodeInstance() {
+
+ sequence_outputs=NULL;
+ input_ports=NULL;
+}
+
+VisualScriptNodeInstance::~VisualScriptNodeInstance() {
+
+ if (sequence_outputs) {
+ memdelete_arr(sequence_outputs);
+ }
+
+ if (input_ports) {
+ memdelete_arr(input_ports);
+ }
+
+ if (output_ports) {
+ memdelete_arr(output_ports);
+ }
+
+}
+
+void VisualScript::add_function(const StringName& p_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!String(p_name).is_valid_identifier());
+ ERR_FAIL_COND(functions.has(p_name));
+
+ functions[p_name]=Function();
+ functions[p_name].scroll=Vector2(-50,-100);
+}
+
+bool VisualScript::has_function(const StringName& p_name) const {
+
+ return functions.has(p_name);
+
+}
+void VisualScript::remove_function(const StringName& p_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_name));
+
+ for (Map<int,Function::NodeData>::Element *E=functions[p_name].nodes.front();E;E=E->next()) {
+
+ E->get().node->disconnect("ports_changed",this,"_node_ports_changed");
+ E->get().node->scripts_used.erase(this);
+ }
+
+ functions.erase(p_name);
+
+}
+
+void VisualScript::rename_function(const StringName& p_name,const StringName& p_new_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_name));
+ if (p_new_name==p_name)
+ return;
+
+ ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
+
+ ERR_FAIL_COND(functions.has(p_new_name));
+ ERR_FAIL_COND(variables.has(p_new_name));
+ ERR_FAIL_COND(custom_signals.has(p_new_name));
+
+ functions[p_new_name]=functions[p_name];
+ functions.erase(p_name);
+
+}
+
+void VisualScript::set_function_scroll(const StringName& p_name, const Vector2& p_scroll) {
+
+ ERR_FAIL_COND(!functions.has(p_name));
+ functions[p_name].scroll=p_scroll;
+
+}
+
+Vector2 VisualScript::get_function_scroll(const StringName& p_name) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_name),Vector2());
+ return functions[p_name].scroll;
+
+}
+
+
+void VisualScript::get_function_list(List<StringName> *r_functions) const {
+
+ for (const Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+ r_functions->push_back(E->key());
+ }
+
+ r_functions->sort_custom<StringName::AlphCompare>();
+
+}
+
+int VisualScript::get_function_node_id(const StringName& p_name) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_name),-1);
+
+ return functions[p_name].function_id;
+
+}
+
+
+void VisualScript::_node_ports_changed(int p_id) {
+
+
+
+ StringName function;
+
+ for (Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+
+ if (E->get().nodes.has(p_id)) {
+ function=E->key();
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(function==StringName());
+
+ Function &func = functions[function];
+ Ref<VisualScriptNode> vsn = func.nodes[p_id].node;
+
+ if (OS::get_singleton()->get_main_loop() && OS::get_singleton()->get_main_loop()->cast_to<SceneTree>() && OS::get_singleton()->get_main_loop()->cast_to<SceneTree>()->is_editor_hint()) {
+ vsn->validate_input_default_values(); //force validate default values when editing on editor
+ }
+
+ //must revalidate all the functions
+
+ {
+ List<SequenceConnection> to_remove;
+
+ for (Set<SequenceConnection>::Element *E=func.sequence_connections.front();E;E=E->next()) {
+ if (E->get().from_node==p_id && E->get().from_output>=vsn->get_output_sequence_port_count()) {
+
+ to_remove.push_back(E->get());
+ }
+ if (E->get().to_node==p_id && !vsn->has_input_sequence_port()) {
+
+ to_remove.push_back(E->get());
+ }
+ }
+
+ while(to_remove.size()) {
+ func.sequence_connections.erase(to_remove.front()->get());
+ to_remove.pop_front();
+ }
+ }
+
+ {
+
+ List<DataConnection> to_remove;
+
+
+ for (Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) {
+ if (E->get().from_node==p_id && E->get().from_port>=vsn->get_output_value_port_count()) {
+ to_remove.push_back(E->get());
+ }
+ if (E->get().to_node==p_id && E->get().to_port>=vsn->get_input_value_port_count()) {
+ to_remove.push_back(E->get());
+ }
+ }
+
+ while(to_remove.size()) {
+ func.data_connections.erase(to_remove.front()->get());
+ to_remove.pop_front();
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ set_edited(true); //something changed, let's set as edited
+ emit_signal("node_ports_changed",function,p_id);
+#endif
+}
+
+void VisualScript::add_node(const StringName& p_func,int p_id, const Ref<VisualScriptNode>& p_node, const Point2 &p_pos) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_func));
+
+
+ for (Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+
+ ERR_FAIL_COND(E->get().nodes.has(p_id)); //id can exist only one in script, even for different functions
+ }
+
+ Function &func = functions[p_func];
+
+
+ if (p_node->cast_to<VisualScriptFunction>()) {
+ //the function indeed
+ ERR_EXPLAIN("A function node already has been set here.");
+ ERR_FAIL_COND(func.function_id>=0);
+
+ func.function_id=p_id;
+ }
+
+ Function::NodeData nd;
+ nd.node=p_node;
+ nd.pos=p_pos;
+
+ Ref<VisualScriptNode> vsn = p_node;
+ vsn->connect("ports_changed",this,"_node_ports_changed",varray(p_id));
+ vsn->scripts_used.insert(this);
+
+
+
+ func.nodes[p_id]=nd;
+}
+
+void VisualScript::remove_node(const StringName& p_func,int p_id){
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+ ERR_FAIL_COND(!func.nodes.has(p_id));
+ {
+ List<SequenceConnection> to_remove;
+
+ for (Set<SequenceConnection>::Element *E=func.sequence_connections.front();E;E=E->next()) {
+ if (E->get().from_node==p_id || E->get().to_node==p_id) {
+ to_remove.push_back(E->get());
+ }
+ }
+
+ while(to_remove.size()) {
+ func.sequence_connections.erase(to_remove.front()->get());
+ to_remove.pop_front();
+ }
+ }
+
+ {
+
+ List<DataConnection> to_remove;
+
+
+ for (Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) {
+ if (E->get().from_node==p_id || E->get().to_node==p_id) {
+ to_remove.push_back(E->get());
+ }
+ }
+
+ while(to_remove.size()) {
+ func.data_connections.erase(to_remove.front()->get());
+ to_remove.pop_front();
+ }
+ }
+
+ if (func.nodes[p_id].node->cast_to<VisualScriptFunction>()) {
+ func.function_id=-1; //revert to invalid
+ }
+
+ func.nodes[p_id].node->disconnect("ports_changed",this,"_node_ports_changed");
+ func.nodes[p_id].node->scripts_used.erase(this);
+
+ func.nodes.erase(p_id);
+
+
+}
+
+
+bool VisualScript::has_node(const StringName& p_func,int p_id) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_func),false);
+ const Function &func = functions[p_func];
+
+ return func.nodes.has(p_id);
+}
+
+Ref<VisualScriptNode> VisualScript::get_node(const StringName& p_func,int p_id) const{
+
+ ERR_FAIL_COND_V(!functions.has(p_func),Ref<VisualScriptNode>());
+ const Function &func = functions[p_func];
+
+ ERR_FAIL_COND_V(!func.nodes.has(p_id),Ref<VisualScriptNode>());
+
+ return func.nodes[p_id].node;
+}
+
+void VisualScript::set_node_pos(const StringName& p_func,int p_id,const Point2& p_pos) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+ ERR_FAIL_COND(!func.nodes.has(p_id));
+ func.nodes[p_id].pos=p_pos;
+}
+
+Point2 VisualScript::get_node_pos(const StringName& p_func,int p_id) const{
+
+ ERR_FAIL_COND_V(!functions.has(p_func),Point2());
+ const Function &func = functions[p_func];
+
+ ERR_FAIL_COND_V(!func.nodes.has(p_id),Point2());
+ return func.nodes[p_id].pos;
+}
+
+
+void VisualScript::get_node_list(const StringName& p_func,List<int> *r_nodes) const{
+
+ ERR_FAIL_COND(!functions.has(p_func));
+ const Function &func = functions[p_func];
+
+ for (const Map<int,Function::NodeData>::Element *E=func.nodes.front();E;E=E->next()) {
+ r_nodes->push_back(E->key());
+ }
+
+}
+
+
+void VisualScript::sequence_connect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node){
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+
+ SequenceConnection sc;
+ sc.from_node=p_from_node;
+ sc.from_output=p_from_output;
+ sc.to_node=p_to_node;
+ ERR_FAIL_COND(func.sequence_connections.has(sc));
+
+ func.sequence_connections.insert(sc);
+
+}
+
+void VisualScript::sequence_disconnect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node){
+
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+ SequenceConnection sc;
+ sc.from_node=p_from_node;
+ sc.from_output=p_from_output;
+ sc.to_node=p_to_node;
+ ERR_FAIL_COND(!func.sequence_connections.has(sc));
+
+ func.sequence_connections.erase(sc);
+
+}
+
+bool VisualScript::has_sequence_connection(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node) const{
+
+ ERR_FAIL_COND_V(!functions.has(p_func),false);
+ const Function &func = functions[p_func];
+
+ SequenceConnection sc;
+ sc.from_node=p_from_node;
+ sc.from_output=p_from_output;
+ sc.to_node=p_to_node;
+
+ return func.sequence_connections.has(sc);
+}
+
+void VisualScript::get_sequence_connection_list(const StringName& p_func,List<SequenceConnection> *r_connection) const {
+
+ ERR_FAIL_COND(!functions.has(p_func));
+ const Function &func = functions[p_func];
+
+ for (const Set<SequenceConnection>::Element *E=func.sequence_connections.front();E;E=E->next()) {
+ r_connection->push_back(E->get());
+ }
+}
+
+
+void VisualScript::data_connect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+ DataConnection dc;
+ dc.from_node=p_from_node;
+ dc.from_port=p_from_port;
+ dc.to_node=p_to_node;
+ dc.to_port=p_to_port;
+
+ ERR_FAIL_COND( func.data_connections.has(dc));
+
+ func.data_connections.insert(dc);
+}
+
+void VisualScript::data_disconnect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) {
+
+ ERR_FAIL_COND(!functions.has(p_func));
+ Function &func = functions[p_func];
+
+ DataConnection dc;
+ dc.from_node=p_from_node;
+ dc.from_port=p_from_port;
+ dc.to_node=p_to_node;
+ dc.to_port=p_to_port;
+
+ ERR_FAIL_COND( !func.data_connections.has(dc));
+
+ func.data_connections.erase(dc);
+
+}
+
+bool VisualScript::has_data_connection(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_func),false);
+ const Function &func = functions[p_func];
+
+ DataConnection dc;
+ dc.from_node=p_from_node;
+ dc.from_port=p_from_port;
+ dc.to_node=p_to_node;
+ dc.to_port=p_to_port;
+
+ return func.data_connections.has(dc);
+
+}
+
+bool VisualScript::is_input_value_port_connected(const StringName& p_func,int p_node,int p_port) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_func),false);
+ const Function &func = functions[p_func];
+
+ for (const Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) {
+ if (E->get().to_node==p_node && E->get().to_port==p_port)
+ return true;
+ }
+
+ return false;
+}
+
+bool VisualScript::get_input_value_port_connection_source(const StringName& p_func,int p_node,int p_port,int *r_node,int *r_port) const {
+
+ ERR_FAIL_COND_V(!functions.has(p_func),false);
+ const Function &func = functions[p_func];
+
+ for (const Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) {
+ if (E->get().to_node==p_node && E->get().to_port==p_port) {
+ *r_node=E->get().from_node;
+ *r_port=E->get().from_port;
+ return true;
+ }
+ }
+
+ return false;
+
+}
+
+void VisualScript::get_data_connection_list(const StringName& p_func,List<DataConnection> *r_connection) const {
+
+ ERR_FAIL_COND(!functions.has(p_func));
+ const Function &func = functions[p_func];
+
+ for (const Set<DataConnection>::Element *E=func.data_connections.front();E;E=E->next()) {
+ r_connection->push_back(E->get());
+ }
+}
+
+void VisualScript::add_variable(const StringName& p_name,const Variant& p_default_value,bool p_export) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!String(p_name).is_valid_identifier());
+ ERR_FAIL_COND(variables.has(p_name));
+
+ Variable v;
+ v.default_value=p_default_value;
+ v.info.type=p_default_value.get_type();
+ v.info.name=p_name;
+ v.info.hint=PROPERTY_HINT_NONE;
+ v._export=p_export;
+
+ variables[p_name]=v;
+
+#ifdef TOOLS_ENABLED
+ _update_placeholders();
+#endif
+
+}
+
+bool VisualScript::has_variable(const StringName& p_name) const {
+
+ return variables.has(p_name);
+}
+
+void VisualScript::remove_variable(const StringName& p_name) {
+
+ ERR_FAIL_COND(!variables.has(p_name));
+ variables.erase(p_name);
+
+#ifdef TOOLS_ENABLED
+ _update_placeholders();
+#endif
+}
+
+void VisualScript::set_variable_default_value(const StringName& p_name,const Variant& p_value){
+
+ ERR_FAIL_COND(!variables.has(p_name));
+
+ variables[p_name].default_value=p_value;
+
+#ifdef TOOLS_ENABLED
+ _update_placeholders();
+#endif
+
+
+}
+Variant VisualScript::get_variable_default_value(const StringName& p_name) const{
+
+ ERR_FAIL_COND_V(!variables.has(p_name),Variant());
+ return variables[p_name].default_value;
+
+}
+void VisualScript::set_variable_info(const StringName& p_name,const PropertyInfo& p_info){
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!variables.has(p_name));
+ variables[p_name].info=p_info;
+ variables[p_name].info.name=p_name;
+
+#ifdef TOOLS_ENABLED
+ _update_placeholders();
+#endif
+
+
+}
+PropertyInfo VisualScript::get_variable_info(const StringName& p_name) const{
+
+ ERR_FAIL_COND_V(!variables.has(p_name),PropertyInfo());
+ return variables[p_name].info;
+}
+
+void VisualScript::set_variable_export(const StringName& p_name,bool p_export) {
+
+ ERR_FAIL_COND(!variables.has(p_name));
+
+ variables[p_name]._export=p_export;
+}
+
+bool VisualScript::get_variable_export(const StringName& p_name) const {
+
+ ERR_FAIL_COND_V(!variables.has(p_name),false);
+ return variables[p_name]._export;
+
+}
+
+
+void VisualScript::_set_variable_info(const StringName& p_name,const Dictionary& p_info) {
+
+ PropertyInfo pinfo;
+ if (p_info.has("type"))
+ pinfo.type=Variant::Type(int(p_info["type"]));
+ if (p_info.has("name"))
+ pinfo.name=p_info["name"];
+ if (p_info.has("hint"))
+ pinfo.hint=PropertyHint(int(p_info["hint"]));
+ if (p_info.has("hint_string"))
+ pinfo.hint_string=p_info["hint_string"];
+ if (p_info.has("usage"))
+ pinfo.usage=p_info["usage"];
+
+ set_variable_info(p_name,pinfo);
+}
+
+Dictionary VisualScript::_get_variable_info(const StringName& p_name) const{
+
+ PropertyInfo pinfo=get_variable_info(p_name);
+ Dictionary d;
+ d["type"]=pinfo.type;
+ d["name"]=pinfo.name;
+ d["hint"]=pinfo.hint;
+ d["hint_string"]=pinfo.hint_string;
+ d["usage"]=pinfo.usage;
+
+ return d;
+}
+
+void VisualScript::get_variable_list(List<StringName> *r_variables) const{
+
+
+ for (Map<StringName,Variable>::Element *E=variables.front();E;E=E->next()) {
+ r_variables->push_back(E->key());
+ }
+
+ r_variables->sort_custom<StringName::AlphCompare>();
+}
+
+
+void VisualScript::set_instance_base_type(const StringName& p_type) {
+
+ ERR_FAIL_COND( instances.size() );
+ base_type=p_type;
+}
+
+
+void VisualScript::rename_variable(const StringName& p_name,const StringName& p_new_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!variables.has(p_name));
+ if (p_new_name==p_name)
+ return;
+
+ ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
+
+ ERR_FAIL_COND(functions.has(p_new_name));
+ ERR_FAIL_COND(variables.has(p_new_name));
+ ERR_FAIL_COND(custom_signals.has(p_new_name));
+
+ variables[p_new_name]=variables[p_name];
+ variables.erase(p_name);
+
+}
+
+void VisualScript::add_custom_signal(const StringName& p_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!String(p_name).is_valid_identifier());
+ ERR_FAIL_COND(custom_signals.has(p_name));
+
+ custom_signals[p_name]=Vector<Argument>();
+}
+
+bool VisualScript::has_custom_signal(const StringName& p_name) const {
+
+ return custom_signals.has(p_name);
+
+}
+void VisualScript::custom_signal_add_argument(const StringName& p_func,Variant::Type p_type,const String& p_name,int p_index) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_func));
+ Argument arg;
+ arg.type=p_type;
+ arg.name=p_name;
+ if (p_index<0)
+ custom_signals[p_func].push_back(arg);
+ else
+ custom_signals[p_func].insert(0,arg);
+
+}
+void VisualScript::custom_signal_set_argument_type(const StringName& p_func,int p_argidx,Variant::Type p_type) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_func));
+ ERR_FAIL_INDEX(p_argidx,custom_signals[p_func].size());
+ custom_signals[p_func][p_argidx].type=p_type;
+}
+Variant::Type VisualScript::custom_signal_get_argument_type(const StringName& p_func,int p_argidx) const {
+
+ ERR_FAIL_COND_V(!custom_signals.has(p_func),Variant::NIL);
+ ERR_FAIL_INDEX_V(p_argidx,custom_signals[p_func].size(),Variant::NIL);
+ return custom_signals[p_func][p_argidx].type;
+}
+void VisualScript::custom_signal_set_argument_name(const StringName& p_func,int p_argidx,const String& p_name) {
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_func));
+ ERR_FAIL_INDEX(p_argidx,custom_signals[p_func].size());
+ custom_signals[p_func][p_argidx].name=p_name;
+
+}
+String VisualScript::custom_signal_get_argument_name(const StringName& p_func,int p_argidx) const {
+
+ ERR_FAIL_COND_V(!custom_signals.has(p_func),String());
+ ERR_FAIL_INDEX_V(p_argidx,custom_signals[p_func].size(),String());
+ return custom_signals[p_func][p_argidx].name;
+
+}
+void VisualScript::custom_signal_remove_argument(const StringName& p_func,int p_argidx) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_func));
+ ERR_FAIL_INDEX(p_argidx,custom_signals[p_func].size());
+ custom_signals[p_func].remove(p_argidx);
+
+}
+
+int VisualScript::custom_signal_get_argument_count(const StringName& p_func) const {
+
+ ERR_FAIL_COND_V(!custom_signals.has(p_func),0);
+ return custom_signals[p_func].size();
+
+}
+void VisualScript::custom_signal_swap_argument(const StringName& p_func,int p_argidx,int p_with_argidx) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_func));
+ ERR_FAIL_INDEX(p_argidx,custom_signals[p_func].size());
+ ERR_FAIL_INDEX(p_with_argidx,custom_signals[p_func].size());
+
+ SWAP( custom_signals[p_func][p_argidx], custom_signals[p_func][p_with_argidx] );
+
+}
+void VisualScript::remove_custom_signal(const StringName& p_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_name));
+ custom_signals.erase(p_name);
+
+}
+
+void VisualScript::rename_custom_signal(const StringName& p_name,const StringName& p_new_name) {
+
+ ERR_FAIL_COND( instances.size() );
+ ERR_FAIL_COND(!custom_signals.has(p_name));
+ if (p_new_name==p_name)
+ return;
+
+ ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
+
+ ERR_FAIL_COND(functions.has(p_new_name));
+ ERR_FAIL_COND(variables.has(p_new_name));
+ ERR_FAIL_COND(custom_signals.has(p_new_name));
+
+ custom_signals[p_new_name]=custom_signals[p_name];
+ custom_signals.erase(p_name);
+
+}
+
+void VisualScript::get_custom_signal_list(List<StringName> *r_custom_signals) const {
+
+ for (const Map<StringName,Vector<Argument> >::Element *E=custom_signals.front();E;E=E->next()) {
+ r_custom_signals->push_back(E->key());
+ }
+
+ r_custom_signals->sort_custom<StringName::AlphCompare>();
+
+}
+
+int VisualScript::get_available_id() const {
+
+ int max_id=0;
+ for (Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+ if (E->get().nodes.empty())
+ continue;
+
+ int last_id = E->get().nodes.back()->key();
+ max_id=MAX(max_id,last_id+1);
+ }
+
+ return max_id;
+}
+
+/////////////////////////////////
+
+
+bool VisualScript::can_instance() const {
+
+ return true;//ScriptServer::is_scripting_enabled();
+
+}
+
+
+StringName VisualScript::get_instance_base_type() const {
+
+ return base_type;
+}
+
+Ref<Script> VisualScript::get_base_script() const {
+ return Ref<Script>(); // no inheritance in visual script
+}
+
+
+#ifdef TOOLS_ENABLED
+void VisualScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
+
+
+ placeholders.erase(p_placeholder);
+}
+
+
+void VisualScript::_update_placeholders() {
+
+ if (placeholders.size()==0)
+ return; //no bother if no placeholders
+ List<PropertyInfo> pinfo;
+ Map<StringName,Variant> values;
+
+ for (Map<StringName,Variable>::Element *E=variables.front();E;E=E->next()) {
+
+ if (!E->get()._export)
+ continue;
+
+ PropertyInfo p = E->get().info;
+ p.name=String(E->key());
+ pinfo.push_back(p);
+ values[p.name]=E->get().default_value;
+ }
+
+ for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
+
+ E->get()->update(pinfo,values);
+ }
+
+}
+
+#endif
+
+
+ScriptInstance* VisualScript::instance_create(Object *p_this) {
+
+
+
+#ifdef TOOLS_ENABLED
+
+ if (!ScriptServer::is_scripting_enabled()) {
+
+
+ PlaceHolderScriptInstance *sins = memnew( PlaceHolderScriptInstance(VisualScriptLanguage::singleton,Ref<Script>((Script*)this),p_this));
+ placeholders.insert(sins);
+
+ List<PropertyInfo> pinfo;
+ Map<StringName,Variant> values;
+
+ for (Map<StringName,Variable>::Element *E=variables.front();E;E=E->next()) {
+
+ if (!E->get()._export)
+ continue;
+
+ PropertyInfo p = E->get().info;
+ p.name=String(E->key());
+ pinfo.push_back(p);
+ values[p.name]=E->get().default_value;
+ }
+
+ sins->update(pinfo,values);
+
+ return sins;
+ }
+#endif
+
+
+ VisualScriptInstance *instance=memnew( VisualScriptInstance );
+ instance->create(Ref<VisualScript>(this),p_this);
+
+
+ if (VisualScriptLanguage::singleton->lock)
+ VisualScriptLanguage::singleton->lock->lock();
+
+ instances[p_this]=instance;
+
+ if (VisualScriptLanguage::singleton->lock)
+ VisualScriptLanguage::singleton->lock->unlock();
+
+ return instance;
+}
+
+bool VisualScript::instance_has(const Object *p_this) const {
+
+ return instances.has((Object*)p_this);
+}
+
+bool VisualScript::has_source_code() const {
+
+ return false;
+}
+
+String VisualScript::get_source_code() const {
+
+ return String();
+}
+
+void VisualScript::set_source_code(const String& p_code) {
+
+}
+
+Error VisualScript::reload(bool p_keep_state) {
+
+ return OK;
+}
+
+
+bool VisualScript::is_tool() const {
+
+ return false;
+}
+
+
+String VisualScript::get_node_type() const {
+
+ return String();
+}
+
+
+ScriptLanguage *VisualScript::get_language() const {
+
+ return VisualScriptLanguage::singleton;
+}
+
+
+bool VisualScript::has_script_signal(const StringName& p_signal) const {
+
+ return custom_signals.has(p_signal);
+}
+
+void VisualScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+
+ for (const Map<StringName,Vector<Argument> >::Element *E=custom_signals.front();E;E=E->next()) {
+
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get().size();i++) {
+ PropertyInfo arg;
+ arg.type=E->get()[i].type;
+ arg.name=E->get()[i].name;
+ mi.arguments.push_back(arg);
+ }
+
+
+ r_signals->push_back(mi);
+ }
+
+
+}
+
+
+bool VisualScript::get_property_default_value(const StringName& p_property,Variant& r_value) const {
+
+ if (!variables.has(p_property))
+ return false;
+
+ r_value=variables[ p_property ].default_value;
+ return true;
+}
+void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
+
+ for (Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+
+ MethodInfo mi;
+ mi.name=E->key();
+ if (E->get().function_id>=0) {
+
+ Ref<VisualScriptFunction> func=E->get().nodes[E->get().function_id].node;
+ if (func.is_valid()) {
+
+ for(int i=0;i<func->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.name=func->get_argument_name(i);
+ arg.type=func->get_argument_type(i);
+ mi.arguments.push_back(arg);
+ }
+ }
+ }
+
+ p_list->push_back(mi);
+ }
+}
+
+bool VisualScript::has_method(const StringName& p_method) const {
+
+ return functions.has(p_method);
+}
+MethodInfo VisualScript::get_method_info(const StringName& p_method) const{
+
+ const Map<StringName,Function>::Element *E=functions.find(p_method);
+ if (!E)
+ return MethodInfo();
+
+ MethodInfo mi;
+ mi.name=E->key();
+ if (E->get().function_id>=0) {
+
+ Ref<VisualScriptFunction> func=E->get().nodes[E->get().function_id].node;
+ if (func.is_valid()) {
+
+ for(int i=0;i<func->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.name=func->get_argument_name(i);
+ arg.type=func->get_argument_type(i);
+ mi.arguments.push_back(arg);
+ }
+ }
+ }
+
+ return mi;
+}
+
+void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+
+ List<StringName> vars;
+ get_variable_list(&vars);
+
+ for (List<StringName>::Element *E=vars.front();E;E=E->next()) {
+ if (!variables[E->get()]._export)
+ continue;
+ p_list->push_back(variables[E->get()].info);
+ }
+}
+
+#ifdef TOOLS_ENABLED
+bool VisualScript::are_subnodes_edited() const {
+
+ for(const Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+
+ for (const Map<int,Function::NodeData>::Element *F=E->get().nodes.front();F;F=F->next()) {
+ if (F->get().node->is_edited()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#endif
+
+void VisualScript::_set_data(const Dictionary& p_data) {
+
+ Dictionary d = p_data;
+ if (d.has("base_type"))
+ base_type=d["base_type"];
+
+ variables.clear();
+ Array vars=d["variables"];
+ for (int i=0;i<vars.size();i++) {
+
+ Dictionary v=vars[i];
+ StringName name = v["name"];
+ add_variable(name);
+ _set_variable_info(name,v);
+ set_variable_default_value(name,v["default_value"]);
+ set_variable_export(name,v.has("export") && bool(v["export"]));
+
+ }
+
+
+ custom_signals.clear();
+ Array sigs=d["signals"];
+ for (int i=0;i<sigs.size();i++) {
+
+ Dictionary cs=sigs[i];
+ add_custom_signal(cs["name"]);
+
+ Array args=cs["arguments"];
+ for(int j=0;j<args.size();j+=2) {
+ custom_signal_add_argument(cs["name"],Variant::Type(int(args[j+1])),args[j]);
+ }
+ }
+
+ Array funcs=d["functions"];
+ functions.clear();
+
+ for (int i=0;i<funcs.size();i++) {
+
+ Dictionary func=funcs[i];
+
+
+ StringName name=func["name"];
+ //int id=func["function_id"];
+ add_function(name);
+
+ set_function_scroll(name,func["scroll"]);
+
+ Array nodes = func["nodes"];
+
+ for(int i=0;i<nodes.size();i+=3) {
+
+ add_node(name,nodes[i],nodes[i+2],nodes[i+1]);
+ }
+
+
+ Array sequence_connections=func["sequence_connections"];
+
+ for (int j=0;j<sequence_connections.size();j+=3) {
+
+ sequence_connect(name,sequence_connections[j+0],sequence_connections[j+1],sequence_connections[j+2]);
+ }
+
+
+ Array data_connections=func["data_connections"];
+
+ for (int j=0;j<data_connections.size();j+=4) {
+
+ data_connect(name,data_connections[j+0],data_connections[j+1],data_connections[j+2],data_connections[j+3]);
+
+ }
+
+
+ }
+
+}
+
+Dictionary VisualScript::_get_data() const{
+
+ Dictionary d;
+ d["base_type"]=base_type;
+ Array vars;
+ for (const Map<StringName,Variable>::Element *E=variables.front();E;E=E->next()) {
+
+ Dictionary var = _get_variable_info(E->key());
+ var["name"]=E->key(); //make sure it's the right one
+ var["default_value"]=E->get().default_value;
+ var["export"]=E->get()._export;
+ vars.push_back(var);
+ }
+ d["variables"]=vars;
+
+ Array sigs;
+ for (const Map<StringName,Vector<Argument> >::Element *E=custom_signals.front();E;E=E->next()) {
+
+ Dictionary cs;
+ cs["name"]=E->key();
+ Array args;
+ for(int i=0;i<E->get().size();i++) {
+ args.push_back(E->get()[i].name);
+ args.push_back(E->get()[i].type);
+ }
+ cs["arguments"]=args;
+
+ sigs.push_back(cs);
+ }
+
+ d["signals"]=sigs;
+
+ Array funcs;
+
+ for (const Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) {
+
+ Dictionary func;
+ func["name"]=E->key();
+ func["function_id"]=E->get().function_id;
+ func["scroll"]=E->get().scroll;
+
+ Array nodes;
+
+ for (const Map<int,Function::NodeData>::Element *F=E->get().nodes.front();F;F=F->next()) {
+
+ nodes.push_back(F->key());
+ nodes.push_back(F->get().pos);
+ nodes.push_back(F->get().node);
+
+ }
+
+ func["nodes"]=nodes;
+
+ Array sequence_connections;
+
+ for (const Set<SequenceConnection>::Element *F=E->get().sequence_connections.front();F;F=F->next()) {
+
+ sequence_connections.push_back(F->get().from_node);
+ sequence_connections.push_back(F->get().from_output);
+ sequence_connections.push_back(F->get().to_node);
+
+ }
+
+
+ func["sequence_connections"]=sequence_connections;
+
+ Array data_connections;
+
+ for (const Set<DataConnection>::Element *F=E->get().data_connections.front();F;F=F->next()) {
+
+ data_connections.push_back(F->get().from_node);
+ data_connections.push_back(F->get().from_port);
+ data_connections.push_back(F->get().to_node);
+ data_connections.push_back(F->get().to_port);
+
+ }
+
+
+ func["data_connections"]=data_connections;
+
+ funcs.push_back(func);
+
+ }
+
+ d["functions"]=funcs;
+
+
+ return d;
+
+}
+
+void VisualScript::_bind_methods() {
+
+
+
+ ObjectTypeDB::bind_method(_MD("_node_ports_changed"),&VisualScript::_node_ports_changed);
+
+ ObjectTypeDB::bind_method(_MD("add_function","name"),&VisualScript::add_function);
+ ObjectTypeDB::bind_method(_MD("has_function","name"),&VisualScript::has_function);
+ ObjectTypeDB::bind_method(_MD("remove_function","name"),&VisualScript::remove_function);
+ ObjectTypeDB::bind_method(_MD("rename_function","name","new_name"),&VisualScript::rename_function);
+ ObjectTypeDB::bind_method(_MD("set_function_scroll","ofs"),&VisualScript::set_function_scroll);
+ ObjectTypeDB::bind_method(_MD("get_function_scroll"),&VisualScript::get_function_scroll);
+
+ ObjectTypeDB::bind_method(_MD("add_node","func","id","node","pos"),&VisualScript::add_node,DEFVAL(Point2()));
+ ObjectTypeDB::bind_method(_MD("remove_node","func","id"),&VisualScript::remove_node);
+ ObjectTypeDB::bind_method(_MD("get_function_node_id","name"),&VisualScript::get_function_node_id);
+
+ ObjectTypeDB::bind_method(_MD("get_node","func","id"),&VisualScript::get_node);
+ ObjectTypeDB::bind_method(_MD("has_node","func","id"),&VisualScript::has_node);
+ ObjectTypeDB::bind_method(_MD("set_node_pos","func","id","pos"),&VisualScript::set_node_pos);
+ ObjectTypeDB::bind_method(_MD("get_node_pos","func","id"),&VisualScript::get_node_pos);
+
+ ObjectTypeDB::bind_method(_MD("sequence_connect","func","from_node","from_output","to_node"),&VisualScript::sequence_connect);
+ ObjectTypeDB::bind_method(_MD("sequence_disconnect","func","from_node","from_output","to_node"),&VisualScript::sequence_disconnect);
+ ObjectTypeDB::bind_method(_MD("has_sequence_connection","func","from_node","from_output","to_node"),&VisualScript::has_sequence_connection);
+
+ ObjectTypeDB::bind_method(_MD("data_connect","func","from_node","from_port","to_node","to_port"),&VisualScript::data_connect);
+ ObjectTypeDB::bind_method(_MD("data_disconnect","func","from_node","from_port","to_node","to_port"),&VisualScript::data_disconnect);
+ ObjectTypeDB::bind_method(_MD("has_data_connection","func","from_node","from_port","to_node","to_port"),&VisualScript::has_data_connection);
+
+ ObjectTypeDB::bind_method(_MD("add_variable","name","default_value","export"),&VisualScript::add_variable,DEFVAL(Variant()),DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("has_variable","name"),&VisualScript::has_variable);
+ ObjectTypeDB::bind_method(_MD("remove_variable","name"),&VisualScript::remove_variable);
+ ObjectTypeDB::bind_method(_MD("set_variable_default_value","name","value"),&VisualScript::set_variable_default_value);
+ ObjectTypeDB::bind_method(_MD("get_variable_default_value","name"),&VisualScript::get_variable_default_value);
+ ObjectTypeDB::bind_method(_MD("set_variable_info","name","value"),&VisualScript::_set_variable_info);
+ ObjectTypeDB::bind_method(_MD("get_variable_info","name"),&VisualScript::_get_variable_info);
+ ObjectTypeDB::bind_method(_MD("set_variable_export","name","enable"),&VisualScript::set_variable_export);
+ ObjectTypeDB::bind_method(_MD("get_variable_export","name"),&VisualScript::get_variable_export);
+ ObjectTypeDB::bind_method(_MD("rename_variable","name","new_name"),&VisualScript::rename_variable);
+
+ ObjectTypeDB::bind_method(_MD("add_custom_signal","name"),&VisualScript::add_custom_signal);
+ ObjectTypeDB::bind_method(_MD("has_custom_signal","name"),&VisualScript::has_custom_signal);
+ ObjectTypeDB::bind_method(_MD("custom_signal_add_argument","name","type","argname","index"),&VisualScript::custom_signal_add_argument,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("custom_signal_set_argument_type","name","argidx","type"),&VisualScript::custom_signal_set_argument_type);
+ ObjectTypeDB::bind_method(_MD("custom_signal_get_argument_type","name","argidx"),&VisualScript::custom_signal_get_argument_type);
+ ObjectTypeDB::bind_method(_MD("custom_signal_set_argument_name","name","argidx","argname"),&VisualScript::custom_signal_set_argument_name);
+ ObjectTypeDB::bind_method(_MD("custom_signal_get_argument_name","name","argidx"),&VisualScript::custom_signal_get_argument_name);
+ ObjectTypeDB::bind_method(_MD("custom_signal_remove_argument","argidx"),&VisualScript::custom_signal_remove_argument);
+ ObjectTypeDB::bind_method(_MD("custom_signal_get_argument_count","name"),&VisualScript::custom_signal_get_argument_count);
+ ObjectTypeDB::bind_method(_MD("custom_signal_swap_argument","name","argidx","withidx"),&VisualScript::custom_signal_swap_argument);
+ ObjectTypeDB::bind_method(_MD("remove_custom_signal","name"),&VisualScript::remove_custom_signal);
+ ObjectTypeDB::bind_method(_MD("rename_custom_signal","name","new_name"),&VisualScript::rename_custom_signal);
+
+ //ObjectTypeDB::bind_method(_MD("set_variable_info","name","info"),&VScript::set_variable_info);
+ //ObjectTypeDB::bind_method(_MD("get_variable_info","name"),&VScript::set_variable_info);
+
+ ObjectTypeDB::bind_method(_MD("set_instance_base_type","type"),&VisualScript::set_instance_base_type);
+
+ ObjectTypeDB::bind_method(_MD("_set_data","data"),&VisualScript::_set_data);
+ ObjectTypeDB::bind_method(_MD("_get_data"),&VisualScript::_get_data);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY,"data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_data"),_SCS("_get_data"));
+
+ ADD_SIGNAL(MethodInfo("node_ports_changed",PropertyInfo(Variant::STRING,"function"),PropertyInfo(Variant::INT,"id")));
+}
+
+VisualScript::VisualScript() {
+
+ base_type="Object";
+
+}
+
+VisualScript::~VisualScript() {
+
+ while(!functions.empty()) {
+ remove_function(functions.front()->key());
+ }
+
+}
+
+////////////////////////////////////////////
+
+
+
+bool VisualScriptInstance::set(const StringName& p_name, const Variant& p_value) {
+
+
+ Map<StringName,Variant>::Element *E=variables.find(p_name);
+ if (!E)
+ return false;
+
+ E->get()=p_value;
+
+ return true;
+}
+
+
+bool VisualScriptInstance::get(const StringName& p_name, Variant &r_ret) const {
+
+ const Map<StringName,Variant>::Element *E=variables.find(p_name);
+ if (!E)
+ return false;
+
+ return E->get();
+}
+void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{
+
+ for (const Map<StringName,VisualScript::Variable>::Element *E=script->variables.front();E;E=E->next()) {
+
+ if (!E->get()._export)
+ continue;
+ PropertyInfo p = E->get().info;
+ p.name=String(E->key());
+ p_properties->push_back(p);
+
+ }
+}
+Variant::Type VisualScriptInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const{
+
+
+ const Map<StringName,VisualScript::Variable>::Element *E=script->variables.find(p_name);
+ if (!E) {
+ if (r_is_valid)
+ *r_is_valid=false;
+ ERR_FAIL_V(Variant::NIL);
+ }
+
+ if (r_is_valid)
+ *r_is_valid=true;
+
+ return E->get().info.type;
+
+}
+
+void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const{
+
+ for (const Map<StringName,VisualScript::Function>::Element *E=script->functions.front();E;E=E->next()) {
+
+ MethodInfo mi;
+ mi.name=E->key();
+ if (E->get().function_id>=0 && E->get().nodes.has(E->get().function_id)) {
+
+ Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
+ if (vsf.is_valid()) {
+
+ for(int i=0;i<vsf->get_argument_count();i++) {
+ PropertyInfo arg;
+ arg.name=vsf->get_argument_name(i);
+ arg.type=vsf->get_argument_type(i);
+
+ mi.arguments.push_back(arg);
+ }
+
+ //vsf->Get_ for now at least it does not return..
+ }
+ }
+
+ p_list->push_back(mi);
+ }
+
+}
+bool VisualScriptInstance::has_method(const StringName& p_method) const{
+
+ return script->functions.has(p_method);
+}
+
+
+//#define VSDEBUG(m_text) print_line(m_text)
+#define VSDEBUG(m_text)
+
+void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance* node,int p_pass,int *pass_stack,const Variant **input_args,Variant **output_args,Variant *variant_stack,Variant::CallError& r_error,String& error_str,VisualScriptNodeInstance** r_error_node) {
+
+ ERR_FAIL_COND(node->pass_idx==-1);
+
+ if (pass_stack[node->pass_idx]==p_pass)
+ return;
+
+ pass_stack[node->pass_idx]=p_pass;
+
+ if (!node->dependencies.empty()) {
+
+ int dc = node->dependencies.size();
+ VisualScriptNodeInstance **deps=node->dependencies.ptr();
+
+ for(int i=0;i<dc;i++) {
+
+ _dependency_step(deps[i],p_pass,pass_stack,input_args,output_args,variant_stack,r_error,error_str,r_error_node);
+ if (r_error.error!=Variant::CallError::CALL_OK)
+ return;
+
+ }
+ }
+
+
+ for(int i=0;i<node->input_port_count;i++) {
+
+ int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
+
+
+ if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
+ //is a default value (unassigned input port)
+ input_args[i]=&default_values[index];
+ } else {
+ //regular temporary in stack
+ input_args[i]=&variant_stack[index];
+
+ }
+ }
+ for(int i=0 ; i<node->output_port_count ; i++) {
+ output_args[i] = &variant_stack[ node->output_ports[i] ];
+ }
+
+ Variant *working_mem=node->working_mem_idx>=0 ? &variant_stack[node->working_mem_idx] : (Variant*)NULL;
+
+ node->step(input_args,output_args,VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE,working_mem,r_error,error_str);
+ //ignore return
+ if (r_error.error!=Variant::CallError::CALL_OK) {
+ *r_error_node=node;
+ }
+
+}
+
+Variant VisualScriptInstance::_call_internal(const StringName& p_method, void* p_stack, int p_stack_size, VisualScriptNodeInstance* p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield, Variant::CallError &r_error) {
+
+ Map<StringName,Function>::Element *F = functions.find(p_method);
+ ERR_FAIL_COND_V(!F,Variant());
+ Function *f=&F->get();
+
+ //this call goes separate, so it can e yielded and suspended
+ Variant *variant_stack=(Variant*)p_stack;
+ bool *sequence_bits = (bool*)(variant_stack + f->max_stack);
+ const Variant **input_args=(const Variant**)(sequence_bits+f->node_count);
+ Variant **output_args=(Variant**)(input_args + max_input_args);
+ int flow_max = f->flow_stack_size;
+ int* flow_stack = flow_max? (int*)(output_args + max_output_args) : (int*)NULL;
+ int *pass_stack = flow_stack + flow_max;
+
+ String error_str;
+
+ VisualScriptNodeInstance* node=p_node;
+ bool error=false;
+ int current_node_id=f->node;
+ Variant return_value;
+ Variant *working_mem=NULL;
+
+ int flow_stack_pos=p_flow_stack_pos;
+
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton()) {
+ VisualScriptLanguage::singleton->enter_function(this,&p_method,variant_stack,&working_mem,&current_node_id);
+ }
+#endif
+
+ while(true) {
+
+ p_pass++; //increment pass
+ current_node_id=node->get_id();
+
+ VSDEBUG("==========AT NODE: "+itos(current_node_id)+" base: "+node->get_base_node()->get_type());
+ VSDEBUG("AT STACK POS: "+itos(flow_stack_pos));
+
+
+ //setup working mem
+ working_mem=node->working_mem_idx>=0 ? &variant_stack[node->working_mem_idx] : (Variant*)NULL;
+
+ VSDEBUG("WORKING MEM: "+itos(node->working_mem_idx));
+
+ if (current_node_id==f->node) {
+ //if function node, set up function arguments from begining of stack
+
+ for(int i=0;i<f->argument_count;i++) {
+ input_args[i]=&variant_stack[i];
+ }
+ } else {
+
+ //run dependencies first
+
+
+ if (!node->dependencies.empty()) {
+
+ int dc = node->dependencies.size();
+ VisualScriptNodeInstance **deps=node->dependencies.ptr();
+
+ for(int i=0;i<dc;i++) {
+
+ _dependency_step(deps[i],p_pass,pass_stack,input_args,output_args,variant_stack,r_error,error_str,&node);
+ if (r_error.error!=Variant::CallError::CALL_OK) {
+ error=true;
+ current_node_id=node->id;
+ break;
+ }
+ }
+ }
+
+ if (!error) {
+
+ //setup input pointers normally
+ VSDEBUG("INPUT PORTS: "+itos(node->input_port_count));
+
+ for(int i=0 ; i<node->input_port_count ; i++) {
+
+
+ int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
+
+ if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
+ //is a default value (unassigned input port)
+ input_args[i]=&default_values[index];
+ VSDEBUG("\tPORT "+itos(i)+" DEFAULT VAL");
+ } else {
+ //regular temporary in stack
+ input_args[i]=&variant_stack[index];
+ VSDEBUG("PORT "+itos(i)+" AT STACK "+itos(index));
+
+ }
+ }
+ }
+ }
+
+ if (error)
+ break;
+
+ //setup output pointers
+
+ VSDEBUG("OUTPUT PORTS: "+itos(node->output_port_count));
+ for(int i=0 ; i<node->output_port_count ; i++) {
+ output_args[i] = &variant_stack[ node->output_ports[i] ];
+ VSDEBUG("PORT "+itos(i)+" AT STACK "+itos(node->output_ports[i]));
+ }
+
+ //do step
+
+ VisualScriptNodeInstance::StartMode start_mode;
+ {
+ if (p_resuming_yield)
+ start_mode=VisualScriptNodeInstance::START_MODE_RESUME_YIELD;
+ else if (!flow_stack || !(flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT)) //if there is a push bit, it means we are continuing a sequence
+ start_mode=VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE;
+ else
+ start_mode=VisualScriptNodeInstance::START_MODE_CONTINUE_SEQUENCE;
+ }
+
+ VSDEBUG("STEP - STARTSEQ: "+itos(start_mode));
+
+ int ret = node->step(input_args,output_args,start_mode,working_mem,r_error,error_str);
+
+ if (r_error.error!=Variant::CallError::CALL_OK) {
+ //use error from step
+ error=true;
+ break;
+ }
+
+ if (ret&VisualScriptNodeInstance::STEP_YIELD_BIT) {
+ //yielded!
+ if (node->get_working_memory_size()==0) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("A node yielded without working memory, please read the docs on how to yield properly!");
+ error=true;
+ break;
+
+ } else {
+ Ref<VisualScriptFunctionState> state = *working_mem;
+ if (!state.is_valid()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Node yielded, but did not return a function state in the first working memory.");
+ error=true;
+ break;
+
+ }
+
+ //step 1, capture all state
+ state->instance_id=get_owner_ptr()->get_instance_ID();
+ state->script_id=get_script()->get_instance_ID();
+ state->instance=this;
+ state->function=p_method;
+ state->working_mem_index=node->working_mem_idx;
+ state->variant_stack_size=f->max_stack;
+ state->node=node;
+ state->flow_stack_pos=flow_stack_pos;
+ state->stack.resize(p_stack_size);
+ state->pass=p_pass;
+ copymem(state->stack.ptr(),p_stack,p_stack_size);
+ //step 2, run away, return directly
+ r_error.error=Variant::CallError::CALL_OK;
+
+
+#ifdef DEBUG_ENABLED
+ //will re-enter later, so exiting
+ if (ScriptDebugger::get_singleton()) {
+ VisualScriptLanguage::singleton->exit_function();
+ }
+#endif
+
+ return state;
+
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton()) {
+ // line
+ bool do_break=false;
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0) {
+
+ if (ScriptDebugger::get_singleton()->get_depth()<=0)
+ ScriptDebugger::get_singleton()->set_lines_left( ScriptDebugger::get_singleton()->get_lines_left() -1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()<=0)
+ do_break=true;
+ }
+
+ if (ScriptDebugger::get_singleton()->is_breakpoint(current_node_id,source))
+ do_break=true;
+
+ if (do_break) {
+ VisualScriptLanguage::singleton->debug_break("Breakpoint",true);
+ }
+
+ ScriptDebugger::get_singleton()->line_poll();
+
+ }
+#endif
+ int output = ret & VisualScriptNodeInstance::STEP_MASK;
+
+ VSDEBUG("STEP RETURN: "+itos(ret));
+
+ if (ret & VisualScriptNodeInstance::STEP_EXIT_FUNCTION_BIT) {
+ if (node->get_working_memory_size()==0) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Return value must be assigned to first element of node working memory! Fix your node please.");
+ error=true;
+ } else {
+ //assign from working memory, first element
+ return_value=*working_mem;
+ }
+
+ VSDEBUG("EXITING FUNCTION - VALUE "+String(return_value));
+ break; //exit function requested, bye
+ }
+
+ VisualScriptNodeInstance *next=NULL; //next node
+
+ if ( (ret==output || ret&VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) && node->sequence_output_count) {
+ //if no exit bit was set, and has sequence outputs, guess next node
+ if (output<0 || output>=node->sequence_output_count) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Node returned an invalid sequence output: ")+itos(output);
+ error=true;
+ break;
+ }
+
+ next = node->sequence_outputs[output];
+ if (next) {
+ VSDEBUG("GOT NEXT NODE - "+itos(next->get_id()));
+ } else {
+ VSDEBUG("GOT NEXT NODE - NULL");
+ }
+ }
+
+ if (flow_stack) {
+
+ //update flow stack pos (may have changed)
+ flow_stack[flow_stack_pos] = current_node_id;
+
+ //add stack push bit if requested
+ if (ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) {
+
+ flow_stack[flow_stack_pos] |= VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT;
+ sequence_bits[ node ->sequence_index ]=true; //remember sequence bit
+ VSDEBUG("NEXT SEQ - FLAG BIT");
+ } else {
+ sequence_bits[ node ->sequence_index ]=false; //forget sequence bit
+ VSDEBUG("NEXT SEQ - NORMAL");
+ }
+
+
+ if (ret & VisualScriptNodeInstance::STEP_FLAG_GO_BACK_BIT) {
+ //go back request
+
+ if (flow_stack_pos>0) {
+ flow_stack_pos--;
+ node = instances[ flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_MASK ];
+ VSDEBUG("NEXT IS GO BACK");
+ } else {
+ VSDEBUG("NEXT IS GO BACK, BUT NO NEXT SO EXIT");
+ break; //simply exit without value or error
+ }
+ } else if (next) {
+
+
+ if (sequence_bits[next->sequence_index]) {
+ // what happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front
+ // because each node has a working memory, we can't really do a sub-sequence
+ // as a result, the sequence will be restarted and the stack will roll back to find where this node
+ // started the sequence
+
+ bool found = false;
+
+ for(int i=flow_stack_pos;i>=0;i--) {
+
+
+ if ( (flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK ) == next->get_id() ) {
+ flow_stack_pos=i; //roll back and remove bit
+ flow_stack[i]=next->get_id();
+ sequence_bits[next->sequence_index]=false;
+ found=true;
+ }
+ }
+
+ if (!found) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Found sequence bit but not the node in the stack, report bug!");
+ error=true;
+ break;
+ }
+
+ node=next;
+ VSDEBUG("RE-ENTERED A LOOP, RETURNED STACK POS TO - "+itos(flow_stack_pos));
+
+ } else {
+ // check for stack overflow
+ if (flow_stack_pos+1 >= flow_max) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Stack overflow with stack depth: ")+itos(output);
+ error=true;
+ break;
+ }
+
+ node = next;
+
+ flow_stack_pos++;
+ flow_stack[flow_stack_pos]=node->get_id();
+
+ VSDEBUG("INCREASE FLOW STACK");
+
+ }
+
+ } else {
+ //no next node, try to go back in stack to pushed bit
+
+ bool found = false;
+
+ for(int i=flow_stack_pos;i>=0;i--) {
+
+ VSDEBUG("FS "+itos(i)+" - "+itos(flow_stack[i]));
+ if (flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT) {
+
+ node = instances[ flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK ];
+ flow_stack_pos=i;
+ found=true;
+ break;
+ }
+ }
+
+ if (!found) {
+ VSDEBUG("NO NEXT NODE, NO GO BACK, EXITING");
+ break; //done, couldn't find a push stack bit
+ }
+
+ VSDEBUG("NO NEXT NODE, GO BACK TO: "+itos(flow_stack_pos));
+
+ }
+ } else {
+
+ node=next; //stackless mode, simply assign next node
+ }
+
+ }
+
+
+
+ if (error) {
+
+ //error
+ // function, file, line, error, explanation
+ String err_file = script->get_path();
+ String err_func = p_method;
+ int err_line=current_node_id; //not a line but it works as one
+
+ if (node && (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD || error_str==String())) {
+
+ if (error_str!=String()) {
+ error_str+=" ";
+ }
+
+ if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ int errorarg=r_error.argument;
+ error_str+="Cannot convert argument "+itos(errorarg+1)+" to "+Variant::get_type_name(r_error.expected)+".";
+ } else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+ error_str+="Expected "+itos(r_error.argument)+" arguments.";
+ } else if (r_error.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+ error_str+="Expected "+itos(r_error.argument)+" arguments.";
+ } else if (r_error.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+ error_str+="Invalid Call.";
+ } else if (r_error.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ error_str+="Base Instance is null";
+ }
+ }
+
+
+ //if (!GDScriptLanguage::get_singleton()->debug_break(err_text,false)) {
+ // debugger break did not happen
+
+ if (!VisualScriptLanguage::singleton->debug_break(error_str,false)) {
+
+ _err_print_error(err_func.utf8().get_data(),err_file.utf8().get_data(),err_line,error_str.utf8().get_data(),ERR_HANDLER_SCRIPT);
+ }
+
+ //}
+ } else {
+
+
+ //return_value=
+ }
+
+#ifdef DEBUG_ENABLED
+ if (ScriptDebugger::get_singleton()) {
+ VisualScriptLanguage::singleton->exit_function();
+ }
+#endif
+
+ //clean up variant stack
+ for(int i=0;i<f->max_stack;i++) {
+ variant_stack[i].~Variant();
+ }
+
+
+ return return_value;
+}
+
+
+Variant VisualScriptInstance::call(const StringName& p_method, const Variant** p_args, int p_argcount, Variant::CallError &r_error){
+
+ r_error.error=Variant::CallError::CALL_OK; //ok by default
+
+ Map<StringName,Function>::Element *F = functions.find(p_method);
+ if (!F) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
+ VSDEBUG("CALLING: "+String(p_method));
+
+ Function *f=&F->get();
+
+ int total_stack_size=0;
+
+ total_stack_size+=f->max_stack*sizeof(Variant); //variants
+ total_stack_size+=f->node_count*sizeof(bool);
+ total_stack_size+=(max_input_args+max_output_args)*sizeof(Variant*); //arguments
+ total_stack_size+=f->flow_stack_size*sizeof(int); //flow
+ total_stack_size+=f->pass_stack_size*sizeof(int);
+
+ VSDEBUG("STACK SIZE: "+itos(total_stack_size));
+ VSDEBUG("STACK VARIANTS: : "+itos(f->max_stack));
+ VSDEBUG("SEQBITS: : "+itos(f->node_count));
+ VSDEBUG("MAX INPUT: "+itos(max_input_args));
+ VSDEBUG("MAX OUTPUT: "+itos(max_output_args));
+ VSDEBUG("FLOW STACK SIZE: "+itos(f->flow_stack_size));
+ VSDEBUG("PASS STACK SIZE: "+itos(f->pass_stack_size));
+
+ void *stack = alloca(total_stack_size);
+
+ Variant *variant_stack=(Variant*)stack;
+ bool *sequence_bits = (bool*)(variant_stack + f->max_stack);
+ const Variant **input_args=(const Variant**)(sequence_bits+f->node_count);
+ Variant **output_args=(Variant**)(input_args + max_input_args);
+ int flow_max = f->flow_stack_size;
+ int* flow_stack = flow_max? (int*)(output_args + max_output_args) : (int*)NULL;
+ int *pass_stack = flow_stack + flow_max;
+
+ for(int i=0;i<f->node_count;i++) {
+ sequence_bits[i]=false; //all starts as false
+ }
+
+ zeromem(pass_stack,f->pass_stack_size*sizeof(int));
+
+ Map<int,VisualScriptNodeInstance*>::Element *E = instances.find(f->node);
+ if (!E) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+
+ ERR_EXPLAIN("No VisualScriptFunction node in function!");
+ ERR_FAIL_V(Variant());
+ }
+
+ VisualScriptNodeInstance *node = E->get();
+
+
+ if (flow_stack) {
+ flow_stack[0]=node->get_id();
+ }
+
+ VSDEBUG("ARGUMENTS: "+itos(f->argument_count)=" RECEIVED: "+itos(p_argcount));
+
+ if (p_argcount<f->argument_count) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=node->get_input_port_count();
+
+ return Variant();
+ }
+
+ if (p_argcount>f->argument_count) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument=node->get_input_port_count();
+
+ return Variant();
+ }
+
+ //allocate variant stack
+ for(int i=0;i<f->max_stack;i++) {
+ memnew_placement(&variant_stack[i],Variant);
+ }
+
+ //allocate function arguments (must be copied for yield to work properly)
+ for(int i=0;i<p_argcount;i++) {
+ variant_stack[i]=*p_args[i];
+ }
+
+ return _call_internal(p_method,stack,total_stack_size,node,0,0,false,r_error);
+
+
+}
+
+void VisualScriptInstance::notification(int p_notification){
+
+ //do nothing as this is called using virtual
+
+ Variant what=p_notification;
+ const Variant*whatp=&what;
+ Variant::CallError ce;
+ call(VisualScriptLanguage::singleton->notification,&whatp,1,ce); //do as call
+
+}
+
+Ref<Script> VisualScriptInstance::get_script() const{
+
+ return script;
+}
+
+ScriptInstance::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;
+ }
+
+ if (E->get().function_id>=0 && E->get().nodes.has(E->get().function_id)) {
+
+ Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
+ if (vsf.is_valid()) {
+
+ return vsf->get_rpc_mode();
+ }
+ }
+
+ return RPC_MODE_DISABLED;
+}
+
+ScriptInstance::RPCMode VisualScriptInstance::get_rset_mode(const StringName& p_variable) const {
+
+ return RPC_MODE_DISABLED;
+}
+
+
+void VisualScriptInstance::create(const Ref<VisualScript>& p_script,Object *p_owner) {
+
+ script=p_script;
+ owner=p_owner;
+ source=p_script->get_path();
+
+ max_input_args = 0;
+ max_output_args = 0;
+
+ if (p_owner->cast_to<Node>()) {
+ //turn on these if they exist and base is a node
+ Node* node = p_owner->cast_to<Node>();
+ if (p_script->functions.has("_process"))
+ node->set_process(true);
+ if (p_script->functions.has("_fixed_process"))
+ node->set_fixed_process(true);
+ if (p_script->functions.has("_input"))
+ node->set_process_input(true);
+ if (p_script->functions.has("_unhandled_input"))
+ node->set_process_unhandled_input(true);
+ if (p_script->functions.has("_unhandled_key_input"))
+ node->set_process_unhandled_key_input(true);
+ }
+
+ for(const Map<StringName,VisualScript::Variable>::Element *E=script->variables.front();E;E=E->next()) {
+ variables[E->key()]=E->get().default_value;
+ //no hacer que todo exporte, solo las que queres!
+ }
+
+
+ for(const Map<StringName,VisualScript::Function>::Element *E=script->functions.front();E;E=E->next()) {
+
+ Function function;
+ function.node=E->get().function_id;
+ function.max_stack=0;
+ function.flow_stack_size=0;
+ function.pass_stack_size=0;
+ function.node_count=0;
+ Map<StringName,int> local_var_indices;
+
+ if (function.node<0) {
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(),0,"No start node in function: "+String(E->key()));
+
+ ERR_CONTINUE( function.node < 0 );
+ }
+
+ {
+ Ref<VisualScriptFunction> func_node = script->get_node(E->key(),E->get().function_id);
+
+ if (func_node.is_null()) {
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(),0,"No VisualScriptFunction typed start node in function: "+String(E->key()));
+ }
+
+ ERR_CONTINUE( !func_node.is_valid() );
+
+ function.argument_count=func_node->get_argument_count();
+ function.max_stack+=function.argument_count;
+ function.flow_stack_size= func_node->is_stack_less() ? 0 : func_node->get_stack_size();
+
+ }
+
+ //multiple passes are required to set up this complex thing..
+
+
+
+
+ //first create the nodes
+ for (const Map<int,VisualScript::Function::NodeData>::Element *F=E->get().nodes.front();F;F=F->next()) {
+
+ Ref<VisualScriptNode> node = F->get().node;
+ VisualScriptNodeInstance *instance = node->instance(this); //create instance
+ ERR_FAIL_COND(!instance);
+
+ instance->base=node.ptr();
+
+ instance->id=F->key();
+ instance->input_port_count = node->get_input_value_port_count();
+ instance->input_ports=NULL;
+ instance->output_port_count = node->get_output_value_port_count();
+ instance->output_ports=NULL;
+ instance->sequence_output_count = node->get_output_sequence_port_count();
+ instance->sequence_index=function.node_count++;
+ instance->sequence_outputs=NULL;
+ instance->pass_idx=-1;
+
+ if (instance->input_port_count) {
+ instance->input_ports = memnew_arr(int,instance->input_port_count);
+ for(int i=0;i<instance->input_port_count;i++) {
+
+ instance->input_ports[i]=-1; //if not assigned, will become default value
+ }
+ }
+
+ if (instance->output_port_count) {
+ instance->output_ports = memnew_arr(int,instance->output_port_count);
+ for(int i=0;i<instance->output_port_count;i++) {
+ instance->output_ports[i]=-1; //if not assigned, will output to trash
+ }
+ }
+
+ if (instance->sequence_output_count) {
+ instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance*,instance->sequence_output_count);
+ for(int i=0;i<instance->sequence_output_count;i++) {
+ instance->sequence_outputs[i]=NULL; //if it remains null, flow ends here
+ }
+ }
+
+ if (node->cast_to<VisualScriptLocalVar>() || node->cast_to<VisualScriptLocalVarSet>()) {
+ //working memory is shared only for this node, for the same variables
+ Ref<VisualScriptLocalVar> vslv = node;
+
+ StringName var_name;
+
+ if (node->cast_to<VisualScriptLocalVar>())
+ var_name = String(node->cast_to<VisualScriptLocalVar>()->get_var_name()).strip_edges();
+ else
+ var_name = String(node->cast_to<VisualScriptLocalVarSet>()->get_var_name()).strip_edges();
+
+ if (!local_var_indices.has(var_name)) {
+ local_var_indices[var_name]=function.max_stack;
+ function.max_stack++;
+ }
+
+ instance->working_mem_idx=local_var_indices[var_name];
+
+ } else if (instance->get_working_memory_size()) {
+ instance->working_mem_idx = function.max_stack;
+ function.max_stack+=instance->get_working_memory_size();
+ } else {
+ instance->working_mem_idx=-1; //no working mem
+ }
+
+ max_input_args = MAX( max_input_args, instance->input_port_count );
+ max_output_args = MAX( max_output_args, instance->output_port_count );
+
+ instances[F->key()]=instance;
+
+
+ }
+
+ function.trash_pos = function.max_stack++; //create pos for trash
+
+ //second pass, do data connections
+
+ for(const Set<VisualScript::DataConnection>::Element *F=E->get().data_connections.front();F;F=F->next()) {
+
+ VisualScript::DataConnection dc = F->get();
+ ERR_CONTINUE(!instances.has(dc.from_node));
+ VisualScriptNodeInstance *from = instances[dc.from_node];
+ ERR_CONTINUE(!instances.has(dc.to_node));
+ VisualScriptNodeInstance *to = instances[dc.to_node];
+ ERR_CONTINUE(dc.from_port >= from->output_port_count);
+ ERR_CONTINUE(dc.to_port >= to->input_port_count);
+
+ if (from->output_ports[dc.from_port]==-1) {
+
+ int stack_pos = function.max_stack++;
+ from->output_ports[dc.from_port] = stack_pos;
+ }
+
+
+ if (from->get_sequence_output_count()==0 && to->dependencies.find(from)==-1) {
+ //if the node we are reading from has no output sequence, we must call step() before reading from it.
+ if (from->pass_idx==-1) {
+ from->pass_idx=function.pass_stack_size;
+ function.pass_stack_size++;
+ }
+ to->dependencies.push_back(from);
+ }
+
+ to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; //read from wherever the stack is
+
+ }
+
+ //third pass, do sequence connections
+
+ for(const Set<VisualScript::SequenceConnection>::Element *F=E->get().sequence_connections.front();F;F=F->next()) {
+
+ VisualScript::SequenceConnection sc = F->get();
+ ERR_CONTINUE(!instances.has(sc.from_node));
+ VisualScriptNodeInstance *from = instances[sc.from_node];
+ ERR_CONTINUE(!instances.has(sc.to_node));
+ VisualScriptNodeInstance *to = instances[sc.to_node];
+ ERR_CONTINUE(sc.from_output >= from->sequence_output_count);
+
+ from->sequence_outputs[sc.from_output]=to;
+
+ }
+
+ //fourth pass:
+ // 1) unassigned input ports to default values
+ // 2) connect unassigned output ports to trash
+
+
+ for (const Map<int,VisualScript::Function::NodeData>::Element *F=E->get().nodes.front();F;F=F->next()) {
+
+ ERR_CONTINUE(!instances.has(F->key()));
+
+ Ref<VisualScriptNode> node = F->get().node;
+ VisualScriptNodeInstance *instance = instances[F->key()];
+
+ // conect to default values
+ for(int i=0;i<instance->input_port_count;i++) {
+ if (instance->input_ports[i]==-1) {
+
+ //unassigned, connect to default val
+ instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT;
+ default_values.push_back( node->get_default_input_value(i) );
+ }
+ }
+
+ // conect to trash
+ for(int i=0;i<instance->output_port_count;i++) {
+ if (instance->output_ports[i]==-1) {
+ instance->output_ports[i] = function.trash_pos; //trash is same for all
+ }
+ }
+ }
+
+
+ functions[E->key()]=function;
+ }
+}
+
+ScriptLanguage *VisualScriptInstance::get_language(){
+
+ return VisualScriptLanguage::singleton;
+}
+
+
+VisualScriptInstance::VisualScriptInstance() {
+
+
+}
+
+VisualScriptInstance::~VisualScriptInstance() {
+
+ if (VisualScriptLanguage::singleton->lock)
+ VisualScriptLanguage::singleton->lock->lock();
+
+ script->instances.erase(owner);
+
+ if (VisualScriptLanguage::singleton->lock)
+ VisualScriptLanguage::singleton->lock->unlock();
+
+ for (Map<int,VisualScriptNodeInstance*>::Element *E=instances.front();E;E=E->next()) {
+ memdelete(E->get());
+ }
+}
+
+
+
+/////////////////////////////////////////////
+
+
+/////////////////////
+
+
+Variant VisualScriptFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ ERR_FAIL_COND_V(function==StringName(),Variant());
+
+#ifdef DEBUG_ENABLED
+ if (instance_id && !ObjectDB::get_instance(instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (script_id && !ObjectDB::get_instance(script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Array args;
+
+ if (p_argcount==0) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ return Variant();
+ } else if (p_argcount==1) {
+ //noooneee, reserved for me, me and only me.
+ } else {
+
+ for(int i=0;i<p_argcount-1;i++) {
+ args.push_back(*p_args[i]);
+ }
+ }
+
+ Ref<VisualScriptFunctionState> self = *p_args[p_argcount-1]; //hi, I'm myself, needed this to remain alive.
+
+ if (self.is_null()) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=p_argcount-1;
+ r_error.expected=Variant::OBJECT;
+ return Variant();
+ }
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Variant *working_mem = ((Variant*)stack.ptr()) + working_mem_index;
+
+ *working_mem=args; //arguments go to working mem.
+
+ Variant ret = instance->_call_internal(function,stack.ptr(),stack.size(),node,flow_stack_pos,pass,true,r_error);
+ function=StringName(); //invalidate
+ return ret;
+}
+
+void VisualScriptFunctionState::connect_to_signal(Object* p_obj, const String& p_signal, Array p_binds) {
+
+ Vector<Variant> binds;
+ for(int i=0;i<p_binds.size();i++) {
+ binds.push_back(p_binds[i]);
+ }
+ binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
+ p_obj->connect(p_signal,this,"_signal_callback",binds);
+}
+
+bool VisualScriptFunctionState::is_valid() const {
+
+ return function!=StringName();
+}
+
+Variant VisualScriptFunctionState::resume(Array p_args) {
+
+ ERR_FAIL_COND_V(function==StringName(),Variant());
+#ifdef DEBUG_ENABLED
+ if (instance_id && !ObjectDB::get_instance(instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (script_id && !ObjectDB::get_instance(script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ Variant::CallError r_error;
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Variant *working_mem = ((Variant*)stack.ptr()) + working_mem_index;
+
+ *working_mem=p_args; //arguments go to working mem.
+
+ Variant ret= instance->_call_internal(function,stack.ptr(),stack.size(),node,flow_stack_pos,pass,true,r_error);
+ function=StringName(); //invalidate
+ return ret;
+}
+
+
+void VisualScriptFunctionState::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("connect_to_signal","obj","signals","args"),&VisualScriptFunctionState::connect_to_signal);
+ ObjectTypeDB::bind_method(_MD("resume:Array","args"),&VisualScriptFunctionState::resume,DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&VisualScriptFunctionState::is_valid);
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback"));
+}
+
+VisualScriptFunctionState::VisualScriptFunctionState() {
+
+}
+
+VisualScriptFunctionState::~VisualScriptFunctionState() {
+
+ if (function!=StringName()) {
+ Variant *s = ((Variant*)stack.ptr());
+ for(int i=0;i<variant_stack_size;i++) {
+ s[i].~Variant();
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////
+
+String VisualScriptLanguage::get_name() const {
+
+ return "VisualScript";
+}
+
+/* LANGUAGE FUNCTIONS */
+void VisualScriptLanguage::init() {
+
+
+}
+String VisualScriptLanguage::get_type() const {
+
+ return "VisualScript";
+}
+String VisualScriptLanguage::get_extension() const {
+
+ return "vs";
+}
+Error VisualScriptLanguage::execute_file(const String& p_path) {
+
+ return OK;
+}
+void VisualScriptLanguage::finish() {
+
+
+}
+
+/* EDITOR FUNCTIONS */
+void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
+
+
+}
+void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
+
+
+}
+void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
+
+
+}
+Ref<Script> VisualScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
+
+ Ref<VisualScript> script;
+ script.instance();
+ script->set_instance_base_type(p_base_class_name);
+ return script;
+}
+bool VisualScriptLanguage::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 false;
+}
+Script *VisualScriptLanguage::create_script() const {
+
+ return memnew( VisualScript );
+}
+bool VisualScriptLanguage::has_named_classes() const {
+
+ return false;
+}
+int VisualScriptLanguage::find_function(const String& p_function,const String& p_code) const {
+
+ return -1;
+}
+String VisualScriptLanguage::make_function(const String& p_class,const String& p_name,const StringArray& p_args) const {
+
+ return String();
+}
+
+void VisualScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_line) const {
+
+
+}
+void VisualScriptLanguage::add_global_constant(const StringName& p_variable,const Variant& p_value) {
+
+
+}
+
+
+/* DEBUGGER FUNCTIONS */
+
+
+
+bool VisualScriptLanguage::debug_break_parse(const String& p_file, int p_node,const String& p_error) {
+ //break because of parse error
+
+ if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
+
+ _debug_parse_err_node=p_node;
+ _debug_parse_err_file=p_file;
+ _debug_error=p_error;
+ ScriptDebugger::get_singleton()->debug(this,false);
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+bool VisualScriptLanguage::debug_break(const String& p_error,bool p_allow_continue) {
+
+ if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
+
+ _debug_parse_err_node=-1;
+ _debug_parse_err_file="";
+ _debug_error=p_error;
+ ScriptDebugger::get_singleton()->debug(this,p_allow_continue);
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+
+String VisualScriptLanguage::debug_get_error() const {
+
+ return _debug_error;
+}
+
+int VisualScriptLanguage::debug_get_stack_level_count() const {
+
+ if (_debug_parse_err_node>=0)
+ return 1;
+
+
+ return _debug_call_stack_pos;
+}
+int VisualScriptLanguage::debug_get_stack_level_line(int p_level) const {
+
+ if (_debug_parse_err_node>=0)
+ return _debug_parse_err_node;
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,-1);
+
+ int l = _debug_call_stack_pos - p_level -1;
+
+ return *(_call_stack[l].current_id);
+
+}
+String VisualScriptLanguage::debug_get_stack_level_function(int p_level) const {
+
+ if (_debug_parse_err_node>=0)
+ return "";
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
+ int l = _debug_call_stack_pos - p_level -1;
+ return *_call_stack[l].function;
+}
+String VisualScriptLanguage::debug_get_stack_level_source(int p_level) const {
+
+ if (_debug_parse_err_node>=0)
+ return _debug_parse_err_file;
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
+ int l = _debug_call_stack_pos - p_level -1;
+ return _call_stack[l].instance->get_script_ptr()->get_path();
+
+}
+void VisualScriptLanguage::debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_node>=0)
+ return;
+
+ ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
+
+ int l = _debug_call_stack_pos - p_level -1;
+ const StringName *f = _call_stack[l].function;
+
+ ERR_FAIL_COND(!_call_stack[l].instance->functions.has(*f));
+// VisualScriptInstance::Function *func = &_call_stack[l].instance->functions[*f];
+
+ VisualScriptNodeInstance *node =_call_stack[l].instance->instances[*_call_stack[l].current_id];
+ ERR_FAIL_COND(!node);
+
+ p_locals->push_back("node_name");
+ p_values->push_back(node->get_base_node()->get_text());
+
+ for(int i=0;i<node->input_port_count;i++) {
+ String name = node->get_base_node()->get_input_value_port_info(i).name;
+ if (name==String()) {
+ name="in_"+itos(i);
+ }
+
+ p_locals->push_back("input/"+name);
+
+ //value is trickier
+
+ int in_from = node->input_ports[i];
+ int in_value = in_from&VisualScriptNodeInstance::INPUT_MASK;
+
+ if (in_from&VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
+ p_values->push_back(_call_stack[l].instance->default_values[in_value]);
+ } else {
+ p_values->push_back( _call_stack[l].stack[ in_value] );
+ }
+ }
+
+ for(int i=0;i<node->output_port_count;i++) {
+
+ String name = node->get_base_node()->get_output_value_port_info(i).name;
+ if (name==String()) {
+ name="out_"+itos(i);
+ }
+
+ p_locals->push_back("output/"+name);
+
+ //value is trickier
+
+ int in_from = node->output_ports[i];
+ p_values->push_back( _call_stack[l].stack[ in_from] );
+
+ }
+
+ for(int i=0;i<node->get_working_memory_size();i++) {
+ p_locals->push_back("working_mem/mem_"+itos(i));
+ p_values->push_back( (*_call_stack[l].work_mem)[i]);
+ }
+
+/*
+ ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
+
+
+ VisualFunction *f = _call_stack[l].function;
+
+ List<Pair<StringName,int> > locals;
+
+ f->debug_get_stack_member_state(*_call_stack[l].line,&locals);
+ for( List<Pair<StringName,int> >::Element *E = locals.front();E;E=E->next() ) {
+
+ p_locals->push_back(E->get().first);
+ p_values->push_back(_call_stack[l].stack[E->get().second]);
+ }
+*/
+}
+void VisualScriptLanguage::debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_node>=0)
+ return;
+
+ ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
+ int l = _debug_call_stack_pos - p_level -1;
+
+
+ Ref<VisualScript> vs = _call_stack[l].instance->get_script();
+ if (vs.is_null())
+ return;
+
+ List<StringName> vars;
+ vs->get_variable_list(&vars);
+ for (List<StringName>::Element *E=vars.front();E;E=E->next()) {
+ Variant v;
+ if (_call_stack[l].instance->get_variable(E->get(),&v)) {
+ p_members->push_back("variables/"+E->get());
+ p_values->push_back(v);
+ }
+ }
+}
+
+void VisualScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ //no globals are really reachable in gdscript
+}
+String VisualScriptLanguage::debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_node>=0)
+ return "";
+ return "";
+}
+
+
+
+void VisualScriptLanguage::reload_all_scripts() {
+
+
+}
+void VisualScriptLanguage::reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload) {
+
+
+}
+/* LOADER FUNCTIONS */
+
+void VisualScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("vs");
+
+}
+void VisualScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
+
+
+}
+void VisualScriptLanguage::get_public_constants(List<Pair<String,Variant> > *p_constants) const {
+
+
+}
+
+void VisualScriptLanguage::profiling_start() {
+
+
+}
+void VisualScriptLanguage::profiling_stop() {
+
+
+}
+
+int VisualScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,int p_info_max) {
+
+ return 0;
+}
+
+int VisualScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_info_max) {
+
+ return 0;
+}
+
+
+VisualScriptLanguage* VisualScriptLanguage::singleton=NULL;
+
+
+void VisualScriptLanguage::add_register_func(const String& p_name,VisualScriptNodeRegisterFunc p_func) {
+
+ ERR_FAIL_COND(register_funcs.has(p_name));
+ register_funcs[p_name]=p_func;
+}
+
+Ref<VisualScriptNode> VisualScriptLanguage::create_node_from_name(const String& p_name) {
+
+ ERR_FAIL_COND_V(!register_funcs.has(p_name),Ref<VisualScriptNode>());
+
+ return register_funcs[p_name](p_name);
+}
+
+void VisualScriptLanguage::get_registered_node_names(List<String> *r_names) {
+
+ for (Map<String,VisualScriptNodeRegisterFunc>::Element *E=register_funcs.front();E;E=E->next()) {
+ r_names->push_back(E->key());
+ }
+}
+
+
+VisualScriptLanguage::VisualScriptLanguage() {
+
+ notification="_notification";
+ _step="_step";
+ _subcall="_subcall";
+ singleton=this;
+#ifndef NO_THREADS
+ lock = Mutex::create();
+#endif
+
+
+ _debug_parse_err_node=-1;
+ _debug_parse_err_file="";
+ _debug_call_stack_pos=0;
+ int dmcs=GLOBAL_DEF("debug/script_max_call_stack",1024);
+ if (ScriptDebugger::get_singleton()) {
+ //debugging enabled!
+ _debug_max_call_stack = dmcs;
+ if (_debug_max_call_stack<1024)
+ _debug_max_call_stack=1024;
+ _call_stack = memnew_arr( CallLevel, _debug_max_call_stack+1 );
+
+ } else {
+ _debug_max_call_stack=0;
+ _call_stack=NULL;
+ }
+
+}
+
+VisualScriptLanguage::~VisualScriptLanguage() {
+
+ if (lock)
+ memdelete(lock);
+
+ if (_call_stack) {
+ memdelete_arr(_call_stack);
+ }
+ singleton=NULL;
+}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
new file mode 100644
index 0000000000..63b018b0c2
--- /dev/null
+++ b/modules/visual_script/visual_script.h
@@ -0,0 +1,618 @@
+#ifndef VSCRIPT_H
+#define VSCRIPT_H
+
+#include "script_language.h"
+#include "os/thread.h"
+
+class VisualScriptInstance;
+class VisualScriptNodeInstance;
+class VisualScript;
+
+class VisualScriptNode : public Resource {
+ OBJ_TYPE(VisualScriptNode,Resource)
+
+friend class VisualScript;
+
+ Set<VisualScript*> scripts_used;
+
+ Array default_input_values;
+ bool breakpoint;
+
+ void _set_default_input_values(Array p_values);
+ Array _get_default_input_values() const;
+
+ void validate_input_default_values();
+protected:
+
+ virtual bool _use_builtin_script() const { return false; }
+
+ void _notification(int p_what);
+ void ports_changed_notify();
+ static void _bind_methods();
+public:
+
+ Ref<VisualScript> get_visual_script() const;
+
+ virtual int get_output_sequence_port_count() const=0;
+ virtual bool has_input_sequence_port() const=0;
+
+ virtual String get_output_sequence_port_text(int p_port) const=0;
+
+ virtual bool has_mixed_input_and_sequence_ports() const { return false; }
+
+ virtual int get_input_value_port_count() const=0;
+ virtual int get_output_value_port_count() const=0;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const=0;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const=0;
+
+ void set_default_input_value(int p_port,const Variant& p_value);
+ Variant get_default_input_value(int p_port) const;
+
+ virtual String get_caption() const=0;
+ virtual String get_text() const=0;
+ virtual String get_category() const=0;
+
+ //used by editor, this is not really saved
+ void set_breakpoint(bool p_breakpoint);
+ bool is_breakpoint() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance)=0;
+
+ struct TypeGuess {
+
+ Variant::Type type;
+ InputEvent::Type ev_type;
+ StringName obj_type;
+ Ref<Script> script;
+
+ TypeGuess() { type=Variant::NIL; ev_type=InputEvent::NONE; }
+ };
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+ VisualScriptNode();
+};
+
+
+class VisualScriptNodeInstance {
+friend class VisualScriptInstance;
+friend class VisualScriptLanguage; //for debugger
+
+
+ enum { //input argument addressing
+ INPUT_SHIFT=1<<24,
+ INPUT_MASK=INPUT_SHIFT-1,
+ INPUT_DEFAULT_VALUE_BIT=INPUT_SHIFT, // from unassigned input port, using default value (edited by user)
+ };
+
+
+ int id;
+ int sequence_index;
+ VisualScriptNodeInstance **sequence_outputs;
+ int sequence_output_count;
+ Vector<VisualScriptNodeInstance*> dependencies;
+ int *input_ports;
+ int input_port_count;
+ int *output_ports;
+ int output_port_count;
+ int working_mem_idx;
+ int pass_idx;
+
+ VisualScriptNode *base;
+
+public:
+
+ enum StartMode {
+ START_MODE_BEGIN_SEQUENCE,
+ START_MODE_CONTINUE_SEQUENCE,
+ START_MODE_RESUME_YIELD
+ };
+
+ enum {
+ STEP_SHIFT=1<<24,
+ STEP_MASK=STEP_SHIFT-1,
+ STEP_FLAG_PUSH_STACK_BIT=STEP_SHIFT, //push bit to stack
+ STEP_FLAG_GO_BACK_BIT=STEP_SHIFT<<1, //go back to previous node
+ STEP_NO_ADVANCE_BIT=STEP_SHIFT<<2, //do not advance past this node
+ STEP_EXIT_FUNCTION_BIT=STEP_SHIFT<<3, //return from function
+ STEP_YIELD_BIT=STEP_SHIFT<<4, //yield (will find VisualScriptFunctionState state in first working memory)
+
+ FLOW_STACK_PUSHED_BIT=1<<30, //in flow stack, means bit was pushed (must go back here if end of sequence)
+ FLOW_STACK_MASK=FLOW_STACK_PUSHED_BIT-1
+
+ };
+
+ _FORCE_INLINE_ int get_input_port_count() const { return input_port_count; }
+ _FORCE_INLINE_ int get_output_port_count() const { return output_port_count; }
+ _FORCE_INLINE_ int get_sequence_output_count() const { return sequence_output_count; }
+
+ _FORCE_INLINE_ int get_id() const { return id; }
+
+ virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str)=0; //do a step, return which sequence port to go out
+
+ Ref<VisualScriptNode> get_base_node() { return Ref<VisualScriptNode>( base ); }
+
+ VisualScriptNodeInstance();
+ virtual ~VisualScriptNodeInstance();
+};
+
+
+class VisualScript : public Script {
+
+ OBJ_TYPE( VisualScript, Script )
+
+ RES_BASE_EXTENSION("vs");
+
+public:
+
+ struct SequenceConnection {
+
+ union {
+
+ struct {
+ uint64_t from_node : 24;
+ uint64_t from_output : 16;
+ uint64_t to_node : 24;
+ };
+ uint64_t id;
+ };
+
+ bool operator<(const SequenceConnection& p_connection) const {
+
+ return id<p_connection.id;
+ }
+ };
+
+ struct DataConnection {
+
+ union {
+
+ struct {
+ uint64_t from_node : 24;
+ uint64_t from_port : 8;
+ uint64_t to_node : 24;
+ uint64_t to_port : 8;
+ };
+ uint64_t id;
+ };
+
+ bool operator<(const DataConnection& p_connection) const {
+
+ return id<p_connection.id;
+ }
+ };
+
+
+private:
+friend class VisualScriptInstance;
+
+ StringName base_type;
+ struct Argument {
+ String name;
+ Variant::Type type;
+ };
+
+ struct Function {
+ struct NodeData {
+ Point2 pos;
+ Ref<VisualScriptNode> node;
+ };
+
+ Map<int,NodeData> nodes;
+
+ Set<SequenceConnection> sequence_connections;
+
+ Set<DataConnection> data_connections;
+
+ int function_id;
+
+ Vector2 scroll;
+
+
+ Function() { function_id=-1; }
+
+ };
+
+ struct Variable {
+ PropertyInfo info;
+ Variant default_value;
+ bool _export;
+ };
+
+
+
+ Map<StringName,Function> functions;
+ Map<StringName,Variable> variables;
+ Map<StringName,Vector<Argument> > custom_signals;
+
+ Map<Object*,VisualScriptInstance*> instances;
+
+#ifdef TOOLS_ENABLED
+ Set<PlaceHolderScriptInstance*> placeholders;
+ //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ void _update_placeholders();
+#endif
+
+ void _set_variable_info(const StringName& p_name,const Dictionary& p_info);
+ Dictionary _get_variable_info(const StringName& p_name) const;
+
+
+ void _set_data(const Dictionary& p_data);
+ Dictionary _get_data() const;
+
+protected:
+
+ void _node_ports_changed(int p_id);
+ static void _bind_methods();
+public:
+
+
+ void add_function(const StringName& p_name);
+ bool has_function(const StringName& p_name) const;
+ void remove_function(const StringName& p_name);
+ void rename_function(const StringName& p_name,const StringName& p_new_name);
+ void set_function_scroll(const StringName& p_name, const Vector2& p_scroll);
+ Vector2 get_function_scroll(const StringName& p_name) const;
+ void get_function_list(List<StringName> *r_functions) const;
+ int get_function_node_id(const StringName& p_name) const;
+
+
+ void add_node(const StringName& p_func,int p_id,const Ref<VisualScriptNode>& p_node,const Point2& p_pos=Point2());
+ void remove_node(const StringName& p_func,int p_id);
+ bool has_node(const StringName& p_func,int p_id) const;
+ Ref<VisualScriptNode> get_node(const StringName& p_func,int p_id) const;
+ void set_node_pos(const StringName& p_func,int p_id,const Point2& p_pos);
+ Point2 get_node_pos(const StringName& p_func,int p_id) const;
+ void get_node_list(const StringName& p_func,List<int> *r_nodes) const;
+
+ void sequence_connect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node);
+ void sequence_disconnect(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node);
+ bool has_sequence_connection(const StringName& p_func,int p_from_node,int p_from_output,int p_to_node) const;
+ void get_sequence_connection_list(const StringName& p_func,List<SequenceConnection> *r_connection) const;
+
+ void data_connect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port);
+ void data_disconnect(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port);
+ bool has_data_connection(const StringName& p_func,int p_from_node,int p_from_port,int p_to_node,int p_to_port) const;
+ void get_data_connection_list(const StringName& p_func,List<DataConnection> *r_connection) const;
+ bool is_input_value_port_connected(const StringName& p_name,int p_node,int p_port) const;
+ bool get_input_value_port_connection_source(const StringName& p_name,int p_node,int p_port,int *r_node,int *r_port) const;
+
+ void add_variable(const StringName& p_name,const Variant& p_default_value=Variant(),bool p_export=false);
+ bool has_variable(const StringName& p_name) const;
+ void remove_variable(const StringName& p_name);
+ void set_variable_default_value(const StringName& p_name,const Variant& p_value);
+ Variant get_variable_default_value(const StringName& p_name) const;
+ void set_variable_info(const StringName& p_name,const PropertyInfo& p_info);
+ PropertyInfo get_variable_info(const StringName& p_name) const;
+ void set_variable_export(const StringName& p_name,bool p_export);
+ bool get_variable_export(const StringName& p_name) const;
+ void get_variable_list(List<StringName> *r_variables) const;
+ void rename_variable(const StringName& p_name,const StringName& p_new_name);
+
+
+ void add_custom_signal(const StringName& p_name);
+ bool has_custom_signal(const StringName& p_name) const;
+ void custom_signal_add_argument(const StringName& p_func,Variant::Type p_type,const String& p_name,int p_index=-1);
+ void custom_signal_set_argument_type(const StringName& p_func,int p_argidx,Variant::Type p_type);
+ Variant::Type custom_signal_get_argument_type(const StringName& p_func,int p_argidx) const;
+ void custom_signal_set_argument_name(const StringName& p_func,int p_argidx,const String& p_name);
+ String custom_signal_get_argument_name(const StringName& p_func,int p_argidx) const;
+ void custom_signal_remove_argument(const StringName& p_func,int p_argidx);
+ int custom_signal_get_argument_count(const StringName& p_func) const;
+ void custom_signal_swap_argument(const StringName& p_func,int p_argidx,int p_with_argidx);
+ void remove_custom_signal(const StringName& p_name);
+ void rename_custom_signal(const StringName& p_name,const StringName& p_new_name);
+
+ void get_custom_signal_list(List<StringName> *r_custom_signals) const;
+
+ int get_available_id() const;
+
+ void set_instance_base_type(const StringName& p_type);
+
+ virtual bool can_instance() const;
+
+ virtual Ref<Script> get_base_script() const;
+ virtual StringName get_instance_base_type() const;
+ virtual ScriptInstance* instance_create(Object *p_this);
+ virtual bool instance_has(const Object *p_this) const;
+
+
+ virtual bool has_source_code() const;
+ virtual String get_source_code() const;
+ virtual void set_source_code(const String& p_code);
+ virtual Error reload(bool p_keep_state=false);
+
+ virtual bool is_tool() const;
+
+ virtual String get_node_type() const;
+
+ virtual ScriptLanguage *get_language() const;
+
+ virtual bool has_script_signal(const StringName& p_signal) const;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+
+ virtual bool get_property_default_value(const StringName& p_property,Variant& r_value) const;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const;
+
+ virtual bool has_method(const StringName& p_method) const;
+ virtual MethodInfo get_method_info(const StringName& p_method) const;
+
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+
+#ifdef TOOLS_ENABLED
+ virtual bool are_subnodes_edited() const;
+#endif
+
+ VisualScript();
+ ~VisualScript();
+};
+
+
+class VisualScriptInstance : public ScriptInstance {
+ Object *owner;
+ Ref<VisualScript> script;
+
+ Map<StringName,Variant> variables; //using variable path, not script
+ Map<int,VisualScriptNodeInstance*> instances;
+
+ struct Function {
+ 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;
+
+ Vector<Variant> default_values;
+ int max_input_args,max_output_args;
+
+ StringName source;
+
+ void _dependency_step(VisualScriptNodeInstance* node, int p_pass, int *pass_stack, const Variant **input_args, Variant **output_args, Variant *variant_stack, Variant::CallError& r_error, String& error_str, VisualScriptNodeInstance **r_error_node);
+ Variant _call_internal(const StringName& p_method, void* p_stack,int p_stack_size, VisualScriptNodeInstance* p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield,Variant::CallError &r_error);
+
+
+ //Map<StringName,Function> functions;
+friend class VisualScriptFunctionState; //for yield
+friend class VisualScriptLanguage; //for debugger
+public:
+ virtual bool set(const StringName& p_name, const Variant& p_value);
+ virtual bool get(const StringName& p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+ virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName& p_method) const;
+ virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
+ virtual void notification(int p_notification);
+
+ bool set_variable(const StringName& p_variable,const Variant& p_value) {
+
+ Map<StringName,Variant>::Element *E=variables.find(p_variable);
+ if (!E)
+ return false;
+
+ E->get()=p_value;
+ return true;
+ }
+
+ bool get_variable(const StringName& p_variable,Variant* r_variable) const {
+
+ const Map<StringName,Variant>::Element *E=variables.find(p_variable);
+ if (!E)
+ return false;
+
+ *r_variable=E->get();
+ return true;
+
+ }
+
+ virtual Ref<Script> get_script() const;
+
+ _FORCE_INLINE_ VisualScript *get_script_ptr() { return script.ptr(); }
+ _FORCE_INLINE_ Object *get_owner_ptr() { return owner; }
+
+ void create(const Ref<VisualScript>& p_script,Object *p_owner);
+
+ virtual ScriptLanguage *get_language();
+
+ virtual RPCMode get_rpc_mode(const StringName& p_method) const;
+ virtual RPCMode get_rset_mode(const StringName& p_variable) const;
+
+ VisualScriptInstance();
+ ~VisualScriptInstance();
+};
+
+
+class VisualScriptFunctionState : public Reference {
+
+ OBJ_TYPE(VisualScriptFunctionState,Reference);
+friend class VisualScriptInstance;
+
+ ObjectID instance_id;
+ ObjectID script_id;
+ VisualScriptInstance *instance;
+ StringName function;
+ Vector<uint8_t> stack;
+ int working_mem_index;
+ int variant_stack_size;
+ VisualScriptNodeInstance *node;
+ int flow_stack_pos;
+ int pass;
+
+ Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+protected:
+ static void _bind_methods();
+public:
+
+ void connect_to_signal(Object* p_obj,const String& p_signal,Array p_binds);
+ bool is_valid() const;
+ Variant resume(Array p_args);
+ VisualScriptFunctionState();
+ ~VisualScriptFunctionState();
+};
+
+
+typedef Ref<VisualScriptNode> (*VisualScriptNodeRegisterFunc)(const String& p_type);
+
+class VisualScriptLanguage : public ScriptLanguage {
+
+ Map<String,VisualScriptNodeRegisterFunc> register_funcs;
+
+ struct CallLevel {
+
+ Variant *stack;
+ Variant **work_mem;
+ const StringName *function;
+ VisualScriptInstance *instance;
+ int *current_id;
+
+ };
+
+
+ int _debug_parse_err_node;
+ String _debug_parse_err_file;
+ String _debug_error;
+ int _debug_call_stack_pos;
+ int _debug_max_call_stack;
+ CallLevel *_call_stack;
+
+public:
+ StringName notification;
+ StringName _get_output_port_unsequenced;
+ StringName _step;
+ StringName _subcall;
+
+ static VisualScriptLanguage* singleton;
+
+ Mutex *lock;
+
+ bool debug_break(const String& p_error,bool p_allow_continue=true);
+ bool debug_break_parse(const String& p_file, int p_node,const String& p_error);
+
+ _FORCE_INLINE_ void enter_function(VisualScriptInstance *p_instance,const StringName* p_function, Variant *p_stack, Variant **p_work_mem,int *current_id) {
+
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
+
+ if (_debug_call_stack_pos >= _debug_max_call_stack) {
+ //stack overflow
+ _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
+
+ _call_stack[_debug_call_stack_pos].stack=p_stack;
+ _call_stack[_debug_call_stack_pos].instance=p_instance;
+ _call_stack[_debug_call_stack_pos].function=p_function;
+ _call_stack[_debug_call_stack_pos].work_mem=p_work_mem;
+ _call_stack[_debug_call_stack_pos].current_id=current_id;
+ _debug_call_stack_pos++;
+ }
+
+ _FORCE_INLINE_ void exit_function() {
+
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
+
+ if (_debug_call_stack_pos==0) {
+
+ _debug_error="Stack Underflow (Engine Bug)";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
+
+ _debug_call_stack_pos--;
+ }
+
+ //////////////////////////////////////
+
+ virtual String get_name() const;
+
+ /* LANGUAGE FUNCTIONS */
+ virtual void init();
+ virtual String get_type() const;
+ virtual String get_extension() const;
+ virtual Error execute_file(const String& p_path) ;
+ virtual void finish();
+
+ /* EDITOR FUNCTIONS */
+ virtual void get_reserved_words(List<String> *p_words) const;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const;
+ virtual Ref<Script> get_template(const String& p_class_name, const String& p_base_class_name) const;
+ 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=NULL) const;
+ virtual Script *create_script() const;
+ virtual bool has_named_classes() const;
+ virtual int find_function(const String& p_function,const String& p_code) const;
+ virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) 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);
+
+
+ /* DEBUGGER FUNCTIONS */
+
+ virtual String debug_get_error() const;
+ virtual int debug_get_stack_level_count() const;
+ virtual int debug_get_stack_level_line(int p_level) const;
+ virtual String debug_get_stack_level_function(int p_level) const;
+ virtual String debug_get_stack_level_source(int p_level) const;
+ virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1);
+
+
+ virtual void reload_all_scripts();
+ virtual void reload_tool_script(const Ref<Script>& p_script,bool p_soft_reload);
+ /* LOADER FUNCTIONS */
+
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const;
+ virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const;
+
+ virtual void profiling_start();
+ virtual void profiling_stop();
+
+ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr,int p_info_max);
+ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_info_max);
+
+ void add_register_func(const String& p_name,VisualScriptNodeRegisterFunc p_func);
+ Ref<VisualScriptNode> create_node_from_name(const String& p_name);
+ void get_registered_node_names(List<String> *r_names);
+
+
+ VisualScriptLanguage();
+ ~VisualScriptLanguage();
+
+};
+
+//aid for registering
+template<class T>
+static Ref<VisualScriptNode> create_node_generic(const String& p_name) {
+
+ Ref<T> node;
+ node.instance();
+ return node;
+}
+
+
+
+#endif // VSCRIPT_H
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
new file mode 100644
index 0000000000..1d0bca0b30
--- /dev/null
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -0,0 +1,1197 @@
+#include "visual_script_builtin_funcs.h"
+#include "math_funcs.h"
+#include "object_type_db.h"
+#include "reference.h"
+#include "func_ref.h"
+#include "os/os.h"
+#include "variant_parser.h"
+#include "io/marshalls.h"
+
+const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX]={
+ "sin",
+ "cos",
+ "tan",
+ "sinh",
+ "cosh",
+ "tanh",
+ "asin",
+ "acos",
+ "atan",
+ "atan2",
+ "sqrt",
+ "fmod",
+ "fposmod",
+ "floor",
+ "ceil",
+ "round",
+ "abs",
+ "sign",
+ "pow",
+ "log",
+ "exp",
+ "is_nan",
+ "is_inf",
+ "ease",
+ "decimals",
+ "stepify",
+ "lerp",
+ "dectime",
+ "randomize",
+ "randi",
+ "randf",
+ "rand_range",
+ "seed",
+ "rand_seed",
+ "deg2rad",
+ "rad2deg",
+ "linear2db",
+ "db2linear",
+ "max",
+ "min",
+ "clamp",
+ "nearest_po2",
+ "weakref",
+ "funcref",
+ "convert",
+ "typeof",
+ "type_exists",
+ "str",
+ "print",
+ "printerr",
+ "printraw",
+ "var2str",
+ "str2var",
+ "var2bytes",
+ "bytes2var",
+};
+
+
+
+int VisualScriptBuiltinFunc::get_output_sequence_port_count() const {
+
+ return has_input_sequence_port() ? 1 : 0;
+}
+
+bool VisualScriptBuiltinFunc::has_input_sequence_port() const{
+
+ switch(func) {
+
+ case MATH_RANDOMIZE:
+ case TEXT_PRINT:
+ case TEXT_PRINTERR:
+ case TEXT_PRINTRAW:
+ return true;
+ default:
+ return false;
+
+ }
+
+}
+
+int VisualScriptBuiltinFunc::get_input_value_port_count() const{
+
+
+ switch(func) {
+
+ case MATH_RANDOMIZE:
+ case MATH_RAND:
+ case MATH_RANDF:
+ return 0;
+ case MATH_SIN:
+ case MATH_COS:
+ case MATH_TAN:
+ case MATH_SINH:
+ case MATH_COSH:
+ case MATH_TANH:
+ case MATH_ASIN:
+ case MATH_ACOS:
+ case MATH_ATAN:
+ case MATH_SQRT:
+ case MATH_FLOOR:
+ case MATH_CEIL:
+ case MATH_ROUND:
+ case MATH_ABS:
+ case MATH_SIGN:
+ case MATH_LOG:
+ case MATH_EXP:
+ case MATH_ISNAN:
+ case MATH_ISINF:
+ case MATH_DECIMALS:
+ case MATH_SEED:
+ case MATH_RANDSEED:
+ case MATH_DEG2RAD:
+ case MATH_RAD2DEG:
+ case MATH_LINEAR2DB:
+ case MATH_DB2LINEAR:
+ case LOGIC_NEAREST_PO2:
+ case OBJ_WEAKREF:
+ case TYPE_OF:
+ case TEXT_STR:
+ case TEXT_PRINT:
+ case TEXT_PRINTERR:
+ case TEXT_PRINTRAW:
+ case VAR_TO_STR:
+ case STR_TO_VAR:
+ case VAR_TO_BYTES:
+ case BYTES_TO_VAR:
+ case TYPE_EXISTS:
+ return 1;
+ case MATH_ATAN2:
+ case MATH_FMOD:
+ case MATH_FPOSMOD:
+ case MATH_POW:
+ case MATH_EASE:
+ case MATH_STEPIFY:
+ case MATH_RANDOM:
+ case LOGIC_MAX:
+ case LOGIC_MIN:
+ case FUNC_FUNCREF:
+ case TYPE_CONVERT:
+ return 2;
+ case MATH_LERP:
+ case MATH_DECTIME:
+ case LOGIC_CLAMP:
+ return 3;
+ case FUNC_MAX:{}
+
+ }
+ return 0;
+}
+int VisualScriptBuiltinFunc::get_output_value_port_count() const{
+
+ switch(func) {
+ case MATH_RANDOMIZE:
+ case TEXT_PRINT:
+ case TEXT_PRINTERR:
+ case TEXT_PRINTRAW:
+ case MATH_SEED:
+ return 0;
+ case MATH_RANDSEED:
+ return 2;
+ default:
+ return 1;
+ }
+
+ return 1;
+}
+
+String VisualScriptBuiltinFunc::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const{
+
+ switch(func) {
+
+ case MATH_SIN:
+ case MATH_COS:
+ case MATH_TAN:
+ case MATH_SINH:
+ case MATH_COSH:
+ case MATH_TANH:
+ case MATH_ASIN:
+ case MATH_ACOS:
+ case MATH_ATAN:
+ case MATH_ATAN2:
+ case MATH_SQRT: {
+ return PropertyInfo(Variant::REAL,"num");
+ } break;
+ case MATH_FMOD:
+ case MATH_FPOSMOD: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"x");
+ else
+ return PropertyInfo(Variant::REAL,"y");
+ } break;
+ case MATH_FLOOR:
+ case MATH_CEIL:
+ case MATH_ROUND:
+ case MATH_ABS:
+ case MATH_SIGN: {
+ return PropertyInfo(Variant::REAL,"num");
+
+ } break;
+
+ case MATH_POW: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"x");
+ else
+ return PropertyInfo(Variant::REAL,"y");
+ } break;
+ case MATH_LOG:
+ case MATH_EXP:
+ case MATH_ISNAN:
+ case MATH_ISINF: {
+ return PropertyInfo(Variant::REAL,"num");
+ } break;
+ case MATH_EASE: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"s");
+ else
+ return PropertyInfo(Variant::REAL,"curve");
+ } break;
+ case MATH_DECIMALS: {
+ return PropertyInfo(Variant::REAL,"step");
+ } break;
+ case MATH_STEPIFY: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"s");
+ else
+ return PropertyInfo(Variant::REAL,"steps");
+ } break;
+ case MATH_LERP: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"from");
+ else if (p_idx==1)
+ return PropertyInfo(Variant::REAL,"to");
+ else
+ return PropertyInfo(Variant::REAL,"weight");
+
+ } break;
+ case MATH_DECTIME: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"value");
+ else if (p_idx==1)
+ return PropertyInfo(Variant::REAL,"amount");
+ else
+ return PropertyInfo(Variant::REAL,"step");
+ } break;
+ case MATH_RANDOMIZE: {
+
+ } break;
+ case MATH_RAND: {
+
+ } break;
+ case MATH_RANDF: {
+
+ } break;
+ case MATH_RANDOM: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"from");
+ else
+ return PropertyInfo(Variant::REAL,"to");
+ } break;
+ case MATH_SEED: {
+ return PropertyInfo(Variant::INT,"seed");
+ } break;
+ case MATH_RANDSEED: {
+ return PropertyInfo(Variant::INT,"seed");
+ } break;
+ case MATH_DEG2RAD: {
+ return PropertyInfo(Variant::REAL,"deg");
+ } break;
+ case MATH_RAD2DEG: {
+ return PropertyInfo(Variant::REAL,"rad");
+ } break;
+ case MATH_LINEAR2DB: {
+ return PropertyInfo(Variant::REAL,"nrg");
+ } break;
+ case MATH_DB2LINEAR: {
+ return PropertyInfo(Variant::REAL,"db");
+ } break;
+ case LOGIC_MAX: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"a");
+ else
+ return PropertyInfo(Variant::REAL,"b");
+ } break;
+ case LOGIC_MIN: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"a");
+ else
+ return PropertyInfo(Variant::REAL,"b");
+ } break;
+ case LOGIC_CLAMP: {
+ if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"a");
+ else if (p_idx==0)
+ return PropertyInfo(Variant::REAL,"min");
+ else
+ return PropertyInfo(Variant::REAL,"max");
+ } break;
+ case LOGIC_NEAREST_PO2: {
+ return PropertyInfo(Variant::INT,"num");
+ } break;
+ case OBJ_WEAKREF: {
+
+ return PropertyInfo(Variant::OBJECT,"source");
+
+ } break;
+ case FUNC_FUNCREF: {
+
+ if (p_idx==0)
+ return PropertyInfo(Variant::OBJECT,"instance");
+ else
+ return PropertyInfo(Variant::STRING,"funcname");
+
+ } break;
+ case TYPE_CONVERT: {
+
+ if (p_idx==0)
+ return PropertyInfo(Variant::NIL,"what");
+ else
+ return PropertyInfo(Variant::STRING,"type");
+ } break;
+ case TYPE_OF: {
+ return PropertyInfo(Variant::NIL,"what");
+
+ } break;
+ case TYPE_EXISTS: {
+
+ return PropertyInfo(Variant::STRING,"type");
+
+ } break;
+ case TEXT_STR: {
+
+ return PropertyInfo(Variant::NIL,"value");
+
+
+ } break;
+ case TEXT_PRINT: {
+
+ return PropertyInfo(Variant::NIL,"value");
+
+ } break;
+ case TEXT_PRINTERR: {
+ return PropertyInfo(Variant::NIL,"value");
+
+ } break;
+ case TEXT_PRINTRAW: {
+
+ return PropertyInfo(Variant::NIL,"value");
+
+ } break;
+ case VAR_TO_STR: {
+ return PropertyInfo(Variant::NIL,"var");
+
+ } break;
+ case STR_TO_VAR: {
+
+ return PropertyInfo(Variant::STRING,"string");
+ } break;
+ case VAR_TO_BYTES: {
+ return PropertyInfo(Variant::NIL,"var");
+
+ } break;
+ case BYTES_TO_VAR: {
+
+ return PropertyInfo(Variant::RAW_ARRAY,"bytes");
+ } break;
+ case FUNC_MAX:{}
+ }
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) const{
+
+ Variant::Type t=Variant::NIL;
+ switch(func) {
+
+ case MATH_SIN:
+ case MATH_COS:
+ case MATH_TAN:
+ case MATH_SINH:
+ case MATH_COSH:
+ case MATH_TANH:
+ case MATH_ASIN:
+ case MATH_ACOS:
+ case MATH_ATAN:
+ case MATH_ATAN2:
+ case MATH_SQRT:
+ case MATH_FMOD:
+ case MATH_FPOSMOD:
+ case MATH_FLOOR:
+ case MATH_CEIL: {
+ t=Variant::REAL;
+ } break;
+ case MATH_ROUND: {
+ t=Variant::INT;
+ } break;
+ case MATH_ABS: {
+ t=Variant::NIL;
+ } break;
+ case MATH_SIGN: {
+ t=Variant::NIL;
+ } break;
+ case MATH_POW:
+ case MATH_LOG:
+ case MATH_EXP: {
+ t=Variant::REAL;
+ } break;
+ case MATH_ISNAN:
+ case MATH_ISINF: {
+ t=Variant::BOOL;
+ } break;
+ case MATH_EASE: {
+ t=Variant::REAL;
+ } break;
+ case MATH_DECIMALS: {
+ t=Variant::INT;
+ } break;
+ case MATH_STEPIFY:
+ case MATH_LERP:
+ case MATH_DECTIME: {
+ t=Variant::REAL;
+
+ } break;
+ case MATH_RANDOMIZE: {
+
+ } break;
+ case MATH_RAND: {
+
+ t=Variant::INT;
+ } break;
+ case MATH_RANDF:
+ case MATH_RANDOM: {
+ t=Variant::REAL;
+ } break;
+ case MATH_SEED: {
+
+ } break;
+ case MATH_RANDSEED: {
+
+ if (p_idx==0)
+ return PropertyInfo(Variant::INT,"rnd");
+ else
+ return PropertyInfo(Variant::INT,"seed");
+ } break;
+ case MATH_DEG2RAD:
+ case MATH_RAD2DEG:
+ case MATH_LINEAR2DB:
+ case MATH_DB2LINEAR: {
+ t=Variant::REAL;
+ } break;
+ case LOGIC_MAX:
+ case LOGIC_MIN:
+ case LOGIC_CLAMP: {
+
+
+ } break;
+
+ case LOGIC_NEAREST_PO2: {
+ t=Variant::NIL;
+ } break;
+ case OBJ_WEAKREF: {
+
+ t=Variant::OBJECT;
+
+ } break;
+ case FUNC_FUNCREF: {
+
+ t=Variant::OBJECT;
+
+ } break;
+ case TYPE_CONVERT: {
+
+
+
+ } break;
+ case TYPE_OF: {
+ t=Variant::INT;
+
+ } break;
+ case TYPE_EXISTS: {
+
+ t=Variant::BOOL;
+
+ } break;
+ case TEXT_STR: {
+
+ t=Variant::STRING;
+
+ } break;
+ case TEXT_PRINT: {
+
+
+ } break;
+ case TEXT_PRINTERR: {
+
+ } break;
+ case TEXT_PRINTRAW: {
+
+ } break;
+ case VAR_TO_STR: {
+ t=Variant::STRING;
+ } break;
+ case STR_TO_VAR: {
+
+ } break;
+ case VAR_TO_BYTES: {
+ t=Variant::RAW_ARRAY;
+
+ } break;
+ case BYTES_TO_VAR: {
+
+
+ } break;
+ case FUNC_MAX:{}
+ }
+
+ return PropertyInfo(t,"");
+}
+
+String VisualScriptBuiltinFunc::get_caption() const {
+
+ return "BuiltinFunc";
+}
+
+String VisualScriptBuiltinFunc::get_text() const {
+
+ return func_name[func];
+}
+
+void VisualScriptBuiltinFunc::set_func(BuiltinFunc p_which) {
+
+ ERR_FAIL_INDEX(p_which,FUNC_MAX);
+ func=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() {
+ return func;
+}
+
+
+#define VALIDATE_ARG_NUM(m_arg) \
+ if (!p_inputs[m_arg]->is_num()) {\
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
+ r_error.argument=m_arg;\
+ r_error.expected=Variant::REAL;\
+ return 0;\
+ }
+
+class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptBuiltinFunc *node;
+ VisualScriptInstance *instance;
+
+ VisualScriptBuiltinFunc::BuiltinFunc func;
+
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ switch(func) {
+ case VisualScriptBuiltinFunc::MATH_SIN: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::sin(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_COS: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::cos(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_TAN: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::tan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SINH: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::sinh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_COSH: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::cosh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_TANH: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::tanh(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ASIN: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::asin(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ACOS: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::acos(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ATAN: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::atan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ATAN2: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::atan2(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SQRT: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::sqrt(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FMOD: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::fmod(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FPOSMOD: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::fposmod(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_FLOOR: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::floor(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_CEIL: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::ceil(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ROUND: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::round(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ABS: {
+
+ if (p_inputs[0]->get_type()==Variant::INT) {
+
+ int64_t i = *p_inputs[0];
+ *p_outputs[0]=ABS(i);
+ } else if (p_inputs[0]->get_type()==Variant::REAL) {
+
+ real_t r = *p_inputs[0];
+ *p_outputs[0]=Math::abs(r);
+ } else {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::REAL;
+
+ }
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SIGN: {
+
+ if (p_inputs[0]->get_type()==Variant::INT) {
+
+ int64_t i = *p_inputs[0];
+ *p_outputs[0]= i < 0 ? -1 : ( i > 0 ? +1 : 0);
+ } else if (p_inputs[0]->get_type()==Variant::REAL) {
+
+ real_t r = *p_inputs[0];
+ *p_outputs[0]= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
+ } else {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::REAL;
+
+ }
+ } break;
+ case VisualScriptBuiltinFunc::MATH_POW: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::pow(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LOG: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::log(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_EXP: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::exp(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ISNAN: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::is_nan(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_ISINF: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::is_inf(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_EASE: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::ease(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DECIMALS: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::step_decimals(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_STEPIFY: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::stepify(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LERP: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *p_outputs[0]=Math::lerp(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DECTIME: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ *p_outputs[0]=Math::dectime(*p_inputs[0],*p_inputs[1],*p_inputs[2]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
+ Math::randomize();
+
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RAND: {
+ *p_outputs[0]=Math::rand();
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDF: {
+ *p_outputs[0]=Math::randf();
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDOM: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *p_outputs[0]=Math::random(*p_inputs[0],*p_inputs[1]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_SEED: {
+
+ VALIDATE_ARG_NUM(0);
+ uint32_t seed=*p_inputs[0];
+ Math::seed(seed);
+
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RANDSEED: {
+
+ VALIDATE_ARG_NUM(0);
+ uint32_t seed=*p_inputs[0];
+ int ret = Math::rand_from_seed(&seed);
+ Array reta;
+ reta.push_back(ret);
+ reta.push_back(seed);
+ *p_outputs[0]=reta;
+
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DEG2RAD: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::deg2rad(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_RAD2DEG: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::rad2deg(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::linear2db(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {
+
+ VALIDATE_ARG_NUM(0);
+ *p_outputs[0]=Math::db2linear(*p_inputs[0]);
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_MAX: {
+
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ *p_outputs[0]=MAX(a,b);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
+
+ *p_outputs[0]=MAX(a,b);
+ }
+
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_MIN: {
+
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT) {
+
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ *p_outputs[0]=MIN(a,b);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
+
+ *p_outputs[0]=MIN(a,b);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_CLAMP: {
+
+ if (p_inputs[0]->get_type()==Variant::INT && p_inputs[1]->get_type()==Variant::INT && p_inputs[2]->get_type()==Variant::INT) {
+
+ int64_t a = *p_inputs[0];
+ int64_t b = *p_inputs[1];
+ int64_t c = *p_inputs[2];
+ *p_outputs[0]=CLAMP(a,b,c);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ real_t a = *p_inputs[0];
+ real_t b = *p_inputs[1];
+ real_t c = *p_inputs[2];
+
+ *p_outputs[0]=CLAMP(a,b,c);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {
+
+ VALIDATE_ARG_NUM(0);
+ int64_t num = *p_inputs[0];
+ *p_outputs[0] = nearest_power_of_2(num);
+ } break;
+ case VisualScriptBuiltinFunc::OBJ_WEAKREF: {
+
+ if (p_inputs[0]->get_type()!=Variant::OBJECT) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+
+ return 0;
+
+ }
+
+ if (p_inputs[0]->is_ref()) {
+
+ REF r = *p_inputs[0];
+ if (!r.is_valid()) {
+
+ return 0;
+ }
+
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_ref(r);
+ *p_outputs[0]=wref;
+ } else {
+ Object *obj = *p_inputs[0];
+ if (!obj) {
+
+ return 0;
+ }
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_obj(obj);
+ *p_outputs[0]=wref;
+ }
+
+
+
+
+ } break;
+ case VisualScriptBuiltinFunc::FUNC_FUNCREF: {
+
+ if (p_inputs[0]->get_type()!=Variant::OBJECT) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+
+ return 0;
+
+ }
+ if (p_inputs[1]->get_type()!=Variant::STRING && p_inputs[1]->get_type()!=Variant::NODE_PATH) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=1;
+ r_error.expected=Variant::STRING;
+
+ return 0;
+
+ }
+
+ Ref<FuncRef> fr = memnew( FuncRef);
+
+ fr->set_instance(*p_inputs[0]);
+ fr->set_function(*p_inputs[1]);
+
+ *p_outputs[0]=fr;
+
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_CONVERT: {
+
+ VALIDATE_ARG_NUM(1);
+ int type=*p_inputs[1];
+ if (type<0 || type>=Variant::VARIANT_MAX) {
+
+ *p_outputs[0]=RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return 0;
+
+ } else {
+
+
+ *p_outputs[0]=Variant::construct(Variant::Type(type),p_inputs,1,r_error);
+ }
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_OF: {
+
+
+ *p_outputs[0] = p_inputs[0]->get_type();
+
+ } break;
+ case VisualScriptBuiltinFunc::TYPE_EXISTS: {
+
+
+ *p_outputs[0] = ObjectTypeDB::type_exists(*p_inputs[0]);
+
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_STR: {
+
+ String str = *p_inputs[0];
+
+ *p_outputs[0]=str;
+
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_PRINT: {
+
+ String str = *p_inputs[0];
+ print_line(str);
+
+
+ } break;
+
+ case VisualScriptBuiltinFunc::TEXT_PRINTERR: {
+
+ String str = *p_inputs[0];
+
+ //str+="\n";
+ OS::get_singleton()->printerr("%s\n",str.utf8().get_data());
+
+
+ } break;
+ case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
+ String str = *p_inputs[0];
+
+ //str+="\n";
+ OS::get_singleton()->print("%s",str.utf8().get_data());
+
+
+ } break;
+ case VisualScriptBuiltinFunc::VAR_TO_STR: {
+
+ String vars;
+ VariantWriter::write_to_string(*p_inputs[0],vars);
+ *p_outputs[0]=vars;
+ } break;
+ case VisualScriptBuiltinFunc::STR_TO_VAR: {
+
+ if (p_inputs[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+
+ return 0;
+ }
+
+ VariantParser::StreamString ss;
+ ss.s=*p_inputs[0];
+
+ String errs;
+ int line;
+ Error err = VariantParser::parse(&ss,*p_outputs[0],errs,line);
+
+ if (err!=OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ *p_outputs[0]="Parse error at line "+itos(line)+": "+errs;
+ return 0;
+ }
+
+ } break;
+ case VisualScriptBuiltinFunc::VAR_TO_BYTES: {
+
+
+ ByteArray barr;
+ int len;
+ Error err = encode_variant(*p_inputs[0],NULL,len);
+ if (err) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::NIL;
+ *p_outputs[0]="Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
+ return 0;
+ }
+
+ barr.resize(len);
+ {
+ ByteArray::Write w = barr.write();
+ encode_variant(*p_inputs[0],w.ptr(),len);
+
+ }
+ *p_outputs[0]=barr;
+ } break;
+ case VisualScriptBuiltinFunc::BYTES_TO_VAR: {
+
+ if (p_inputs[0]->get_type()!=Variant::RAW_ARRAY) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::RAW_ARRAY;
+
+ return 0;
+ }
+
+ ByteArray varr=*p_inputs[0];
+ Variant ret;
+ {
+ ByteArray::Read r=varr.read();
+ Error err = decode_variant(ret,r.ptr(),varr.size(),NULL);
+ if (err!=OK) {
+ *p_outputs[0]=RTR("Not enough bytes for decoding bytes, or invalid format.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::RAW_ARRAY;
+ return 0;
+ }
+
+ }
+
+ *p_outputs[0]=ret;
+
+ } break;
+ default: {}
+ }
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptBuiltinFunc::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceBuiltinFunc * instance = memnew(VisualScriptNodeInstanceBuiltinFunc );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->func=func;
+ return instance;
+}
+
+
+void VisualScriptBuiltinFunc::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_func","which"),&VisualScriptBuiltinFunc::set_func);
+ ObjectTypeDB::bind_method(_MD("get_func"),&VisualScriptBuiltinFunc::get_func);
+
+ String cc;
+
+ for(int i=0;i<FUNC_MAX;i++) {
+
+ if (i>0)
+ cc+=",";
+ cc+=func_name[i];
+ }
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"function",PROPERTY_HINT_ENUM,cc),_SCS("set_func"),_SCS("get_func"));
+}
+
+VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() {
+
+ func=MATH_SIN;
+}
+
+template<VisualScriptBuiltinFunc::BuiltinFunc func>
+static Ref<VisualScriptNode> create_builtin_func_node(const String& p_name) {
+
+ Ref<VisualScriptBuiltinFunc> node;
+ node.instance();
+ node->set_func(func);
+ return node;
+}
+
+void register_visual_script_builtin_func_node() {
+
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/sin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/cos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/tan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TAN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/sinh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SINH>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/cosh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COSH>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/tanh",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TANH>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/asin",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ASIN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/acos",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ACOS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan2",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN2>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/sqrt",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SQRT>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/fmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FMOD>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/fposmod",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FPOSMOD>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/floor",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FLOOR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/ceil",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CEIL>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/round",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ROUND>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/abs",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ABS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/sign",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIGN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/pow",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POW>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/log",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LOG>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/exp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EXP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/isnan",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISNAN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/isinf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISINF>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/decimals",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECIMALS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/random",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOM>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/deg2rad",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DEG2RAD>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear",create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/max",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/min",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/clamp",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_CLAMP>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2",create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref",create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/funcref",create_builtin_func_node<VisualScriptBuiltinFunc::FUNC_FUNCREF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/str",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/print",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/printraw",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTRAW>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2str",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_STR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/str2var",create_builtin_func_node<VisualScriptBuiltinFunc::STR_TO_VAR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2bytes",create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_BYTES>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/bytes2var",create_builtin_func_node<VisualScriptBuiltinFunc::BYTES_TO_VAR>);
+
+}
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
new file mode 100644
index 0000000000..ebf227a192
--- /dev/null
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -0,0 +1,110 @@
+#ifndef VISUAL_SCRIPT_BUILTIN_FUNCS_H
+#define VISUAL_SCRIPT_BUILTIN_FUNCS_H
+
+#include "visual_script.h"
+
+
+class VisualScriptBuiltinFunc : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptBuiltinFunc,VisualScriptNode)
+public:
+
+ enum BuiltinFunc {
+ MATH_SIN,
+ MATH_COS,
+ MATH_TAN,
+ MATH_SINH,
+ MATH_COSH,
+ MATH_TANH,
+ MATH_ASIN,
+ MATH_ACOS,
+ MATH_ATAN,
+ MATH_ATAN2,
+ MATH_SQRT,
+ MATH_FMOD,
+ MATH_FPOSMOD,
+ MATH_FLOOR,
+ MATH_CEIL,
+ MATH_ROUND,
+ MATH_ABS,
+ MATH_SIGN,
+ MATH_POW,
+ MATH_LOG,
+ MATH_EXP,
+ MATH_ISNAN,
+ MATH_ISINF,
+ MATH_EASE,
+ MATH_DECIMALS,
+ MATH_STEPIFY,
+ MATH_LERP,
+ MATH_DECTIME,
+ MATH_RANDOMIZE,
+ MATH_RAND,
+ MATH_RANDF,
+ MATH_RANDOM,
+ MATH_SEED,
+ MATH_RANDSEED,
+ MATH_DEG2RAD,
+ MATH_RAD2DEG,
+ MATH_LINEAR2DB,
+ MATH_DB2LINEAR,
+ LOGIC_MAX,
+ LOGIC_MIN,
+ LOGIC_CLAMP,
+ LOGIC_NEAREST_PO2,
+ OBJ_WEAKREF,
+ FUNC_FUNCREF,
+ TYPE_CONVERT,
+ TYPE_OF,
+ TYPE_EXISTS,
+ TEXT_STR,
+ TEXT_PRINT,
+ TEXT_PRINTERR,
+ TEXT_PRINTRAW,
+ VAR_TO_STR,
+ STR_TO_VAR,
+ VAR_TO_BYTES,
+ BYTES_TO_VAR,
+ FUNC_MAX
+ };
+
+private:
+ static const char* func_name[FUNC_MAX];
+ BuiltinFunc func;
+protected:
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_func(BuiltinFunc p_which);
+ BuiltinFunc get_func();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptBuiltinFunc();
+};
+
+VARIANT_ENUM_CAST(VisualScriptBuiltinFunc::BuiltinFunc)
+
+
+void register_visual_script_builtin_func_node();
+
+
+#endif // VISUAL_SCRIPT_BUILTIN_FUNCS_H
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
new file mode 100644
index 0000000000..14708799af
--- /dev/null
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -0,0 +1,3495 @@
+#include "visual_script_editor.h"
+#include "tools/editor/editor_node.h"
+#include "visual_script_nodes.h"
+#include "visual_script_flow_control.h"
+#include "visual_script_func_nodes.h"
+#include "visual_script_expression.h"
+#include "os/input.h"
+#include "tools/editor/editor_resource_preview.h"
+#include "os/keyboard.h"
+
+#ifdef TOOLS_ENABLED
+class VisualScriptEditorSignalEdit : public Object {
+
+ OBJ_TYPE(VisualScriptEditorSignalEdit,Object)
+
+ StringName sig;
+public:
+ UndoRedo *undo_redo;
+ Ref<VisualScript> script;
+
+
+protected:
+
+ static void _bind_methods() {
+ ObjectTypeDB::bind_method("_sig_changed",&VisualScriptEditorSignalEdit::_sig_changed);
+ }
+
+ void _sig_changed() {
+
+ _change_notify();
+ }
+
+ bool _set(const StringName& p_name, const Variant& p_value) {
+
+ if (sig==StringName())
+ return false;
+
+ if (p_name=="argument_count") {
+
+ int new_argc=p_value;
+ int argc = script->custom_signal_get_argument_count(sig);
+ if (argc==new_argc)
+ return true;
+
+ undo_redo->create_action("Change Signal Arguments");
+
+
+
+ if (new_argc < argc) {
+ for(int i=new_argc;i<argc;i++) {
+ undo_redo->add_do_method(script.ptr(),"custom_signal_remove_argument",sig,new_argc);
+ undo_redo->add_undo_method(script.ptr(),"custom_signal_add_argument",sig,script->custom_signal_get_argument_name(sig,i),script->custom_signal_get_argument_type(sig,i),-1);
+ }
+ } else if (new_argc>argc) {
+
+ for(int i=argc;i<new_argc;i++) {
+
+ undo_redo->add_do_method(script.ptr(),"custom_signal_add_argument",sig,Variant::NIL,"arg"+itos(i+1),-1);
+ undo_redo->add_undo_method(script.ptr(),"custom_signal_remove_argument",sig,argc);
+ }
+ }
+
+ undo_redo->add_do_method(this,"_sig_changed");
+ undo_redo->add_undo_method(this,"_sig_changed");
+
+ undo_redo->commit_action();
+
+ return true;
+ }
+ if (String(p_name).begins_with("argument/")) {
+ int idx = String(p_name).get_slice("/",1).to_int()-1;
+ ERR_FAIL_INDEX_V(idx,script->custom_signal_get_argument_count(sig),false);
+ String what = String(p_name).get_slice("/",2);
+ if (what=="type") {
+
+ int old_type = script->custom_signal_get_argument_type(sig,idx);
+ int new_type=p_value;
+ undo_redo->create_action("Change Argument Type");
+ undo_redo->add_do_method(script.ptr(),"custom_signal_set_argument_type",sig,idx,new_type);
+ undo_redo->add_undo_method(script.ptr(),"custom_signal_set_argument_type",sig,idx,old_type);
+ undo_redo->commit_action();
+
+ return true;
+ }
+
+ if (what=="name") {
+
+ String old_name = script->custom_signal_get_argument_name(sig,idx);
+ String new_name=p_value;
+ undo_redo->create_action("Change Argument name");
+ undo_redo->add_do_method(script.ptr(),"custom_signal_set_argument_name",sig,idx,new_name);
+ undo_redo->add_undo_method(script.ptr(),"custom_signal_set_argument_name",sig,idx,old_name);
+ undo_redo->commit_action();
+ return true;
+ }
+
+
+ }
+
+
+ return false;
+ }
+
+ bool _get(const StringName& p_name,Variant &r_ret) const {
+
+ if (sig==StringName())
+ return false;
+
+ if (p_name=="argument_count") {
+ r_ret = script->custom_signal_get_argument_count(sig);
+ return true;
+ }
+ if (String(p_name).begins_with("argument/")) {
+ int idx = String(p_name).get_slice("/",1).to_int()-1;
+ ERR_FAIL_INDEX_V(idx,script->custom_signal_get_argument_count(sig),false);
+ String what = String(p_name).get_slice("/",2);
+ if (what=="type") {
+ r_ret = script->custom_signal_get_argument_type(sig,idx);
+ return true;
+ }
+ if (what=="name") {
+ r_ret = script->custom_signal_get_argument_name(sig,idx);
+ return true;
+ }
+
+
+
+ }
+
+ return false;
+ }
+ void _get_property_list( List<PropertyInfo> *p_list) const {
+
+ if (sig==StringName())
+ return;
+
+ p_list->push_back(PropertyInfo(Variant::INT,"argument_count",PROPERTY_HINT_RANGE,"0,256"));
+ String argt="Variant";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ for(int i=0;i<script->custom_signal_get_argument_count(sig);i++) {
+ p_list->push_back(PropertyInfo(Variant::INT,"argument/"+itos(i+1)+"/type",PROPERTY_HINT_ENUM,argt));
+ p_list->push_back(PropertyInfo(Variant::STRING,"argument/"+itos(i+1)+"/name"));
+ }
+ }
+
+public:
+
+
+ void edit(const StringName& p_sig) {
+
+ sig=p_sig;
+ _change_notify();
+ }
+
+ VisualScriptEditorSignalEdit() { undo_redo=NULL; }
+};
+
+class VisualScriptEditorVariableEdit : public Object {
+
+ OBJ_TYPE(VisualScriptEditorVariableEdit,Object)
+
+ StringName var;
+public:
+ UndoRedo *undo_redo;
+ Ref<VisualScript> script;
+
+
+protected:
+
+ static void _bind_methods() {
+ ObjectTypeDB::bind_method("_var_changed",&VisualScriptEditorVariableEdit::_var_changed);
+ ObjectTypeDB::bind_method("_var_value_changed",&VisualScriptEditorVariableEdit::_var_value_changed);
+ }
+
+ void _var_changed() {
+
+ _change_notify();
+ }
+ void _var_value_changed() {
+
+ _change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general
+ }
+
+ bool _set(const StringName& p_name, const Variant& p_value) {
+
+ if (var==StringName())
+ return false;
+
+
+
+ if (String(p_name)=="value") {
+ undo_redo->create_action("Set Variable Default Value");
+ Variant current=script->get_variable_default_value(var);
+ undo_redo->add_do_method(script.ptr(),"set_variable_default_value",var,p_value);
+ undo_redo->add_undo_method(script.ptr(),"set_variable_default_value",var,current);
+ undo_redo->add_do_method(this,"_var_value_changed");
+ undo_redo->add_undo_method(this,"_var_value_changed");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ Dictionary d = script->call("get_variable_info",var);
+
+ if (String(p_name)=="type") {
+
+ Dictionary dc=d.copy();
+ dc["type"]=p_value;
+ undo_redo->create_action("Set Variable Type");
+ undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc);
+ undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d);
+ undo_redo->add_do_method(this,"_var_changed");
+ undo_redo->add_undo_method(this,"_var_changed");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ if (String(p_name)=="hint") {
+
+ Dictionary dc=d.copy();
+ dc["hint"]=p_value;
+ undo_redo->create_action("Set Variable Type");
+ undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc);
+ undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d);
+ undo_redo->add_do_method(this,"_var_changed");
+ undo_redo->add_undo_method(this,"_var_changed");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ if (String(p_name)=="hint_string") {
+
+ Dictionary dc=d.copy();
+ dc["hint_string"]=p_value;
+ undo_redo->create_action("Set Variable Type");
+ undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc);
+ undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d);
+ undo_redo->add_do_method(this,"_var_changed");
+ undo_redo->add_undo_method(this,"_var_changed");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ if (String(p_name)=="export") {
+ script->set_variable_export(var,p_value);
+ return true;
+ }
+
+
+ return false;
+ }
+
+ bool _get(const StringName& p_name,Variant &r_ret) const {
+
+ if (var==StringName())
+ return false;
+
+ if (String(p_name)=="value") {
+ r_ret=script->get_variable_default_value(var);
+ return true;
+ }
+
+ PropertyInfo pinfo = script->get_variable_info(var);
+
+ if (String(p_name)=="type") {
+ r_ret=pinfo.type;
+ return true;
+ }
+ if (String(p_name)=="hint") {
+ r_ret=pinfo.hint;
+ return true;
+ }
+ if (String(p_name)=="hint_string") {
+ r_ret=pinfo.hint_string;
+ return true;
+ }
+
+ if (String(p_name)=="export") {
+ r_ret=script->get_variable_export(var);
+ return true;
+ }
+
+ return false;
+ }
+ void _get_property_list( List<PropertyInfo> *p_list) const {
+
+ if (var==StringName())
+ return;
+
+ String argt="Variant";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+ p_list->push_back(PropertyInfo(Variant::INT,"type",PROPERTY_HINT_ENUM,argt));
+ p_list->push_back(PropertyInfo(script->get_variable_info(var).type,"value",script->get_variable_info(var).hint,script->get_variable_info(var).hint_string,PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT,"hint",PROPERTY_HINT_ENUM,"None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,BitFlags,AllFlags,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText"));
+ p_list->push_back(PropertyInfo(Variant::STRING,"hint_string"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"export"));
+
+ }
+
+public:
+
+
+ void edit(const StringName& p_var) {
+
+ var=p_var;
+ _change_notify();
+ }
+
+ VisualScriptEditorVariableEdit() { undo_redo=NULL; }
+};
+
+static Color _color_from_type(Variant::Type p_type) {
+ Color color;
+ switch(p_type) {
+ case Variant::NIL: color = Color::html("69ecbd"); break;
+
+ case Variant::BOOL: color = Color::html("8da6f0"); break;
+ case Variant::INT: color = Color::html("7dc6ef"); break;
+ case Variant::REAL: color = Color::html("61daf4"); break;
+ case Variant::STRING: color = Color::html("6ba7ec"); break;
+
+ case Variant::VECTOR2: color = Color::html("bd91f1"); break;
+ case Variant::RECT2: color = Color::html("f191a5"); break;
+ case Variant::VECTOR3: color = Color::html("d67dee"); break;
+ case Variant::MATRIX32: color = Color::html("c4ec69"); break;
+ case Variant::PLANE: color = Color::html("f77070"); break;
+ case Variant::QUAT: color = Color::html("ec69a3"); break;
+ case Variant::_AABB: color = Color::html("ee7991"); break;
+ case Variant::MATRIX3: color = Color::html("e3ec69"); break;
+ case Variant::TRANSFORM: color = Color::html("ecd669"); break;
+
+ case Variant::COLOR: color = Color::html("9dff70"); break;
+ case Variant::IMAGE: color = Color::html("93f1b9"); break;
+ case Variant::NODE_PATH: color = Color::html("6993ec"); break;
+ case Variant::_RID: color = Color::html("69ec9a"); break;
+ case Variant::OBJECT: color = Color::html("79f3e8"); break;
+ case Variant::INPUT_EVENT: color = Color::html("adf18f"); break;
+ case Variant::DICTIONARY: color = Color::html("77edb1"); break;
+
+ case Variant::ARRAY: color = Color::html("e0e0e0"); break;
+ case Variant::RAW_ARRAY: color = Color::html("aaf4c8"); break;
+ case Variant::INT_ARRAY: color = Color::html("afdcf5"); break;
+ case Variant::REAL_ARRAY: color = Color::html("97e7f8"); break;
+ case Variant::STRING_ARRAY: color = Color::html("9dc4f2"); break;
+ case Variant::VECTOR2_ARRAY: color = Color::html("d1b3f5"); break;
+ case Variant::VECTOR3_ARRAY: color = Color::html("df9bf2"); break;
+ case Variant::COLOR_ARRAY: color = Color::html("e9ff97"); break;
+
+ default:
+ color.set_hsv(p_type/float(Variant::VARIANT_MAX),0.7,0.7);
+ }
+ return color;
+}
+
+
+
+void VisualScriptEditor::_update_graph_connections() {
+
+ graph->clear_connections();
+
+ List<VisualScript::SequenceConnection> sequence_conns;
+ script->get_sequence_connection_list(edited_func,&sequence_conns);
+
+
+ for (List<VisualScript::SequenceConnection>::Element *E=sequence_conns.front();E;E=E->next()) {
+
+ graph->connect_node(itos(E->get().from_node),E->get().from_output,itos(E->get().to_node),0);
+ }
+
+ List<VisualScript::DataConnection> data_conns;
+ script->get_data_connection_list(edited_func,&data_conns);
+
+ for (List<VisualScript::DataConnection>::Element *E=data_conns.front();E;E=E->next()) {
+
+ VisualScript::DataConnection dc=E->get();
+
+
+ Ref<VisualScriptNode> from_node = script->get_node(edited_func,E->get().from_node);
+ Ref<VisualScriptNode> to_node = script->get_node(edited_func,E->get().to_node);
+
+ if (to_node->has_input_sequence_port()) {
+ dc.to_port++;
+ }
+
+ dc.from_port+=from_node->get_output_sequence_port_count();
+
+ graph->connect_node(itos(E->get().from_node),dc.from_port,itos(E->get().to_node),dc.to_port);
+ }
+
+}
+
+
+void VisualScriptEditor::_update_graph(int p_only_id) {
+
+ if (updating_graph)
+ return;
+
+ updating_graph=true;
+
+ //byebye all nodes
+ if (p_only_id>=0) {
+ if (graph->has_node(itos(p_only_id))) {
+ Node* gid = graph->get_node(itos(p_only_id));
+ if (gid)
+ memdelete(gid);
+ }
+ } else {
+
+ for(int i=0;i<graph->get_child_count();i++) {
+
+ if (graph->get_child(i)->cast_to<GraphNode>()) {
+ memdelete(graph->get_child(i));
+ i--;
+ }
+ }
+ }
+
+ if (!script->has_function(edited_func)) {
+ graph->hide();
+ select_func_text->show();
+ updating_graph=false;
+ return;
+ }
+
+ graph->show();
+ select_func_text->hide();
+
+ Ref<Texture> type_icons[Variant::VARIANT_MAX]={
+ Control::get_icon("MiniVariant","EditorIcons"),
+ Control::get_icon("MiniBoolean","EditorIcons"),
+ Control::get_icon("MiniInteger","EditorIcons"),
+ Control::get_icon("MiniFloat","EditorIcons"),
+ Control::get_icon("MiniString","EditorIcons"),
+ Control::get_icon("MiniVector2","EditorIcons"),
+ Control::get_icon("MiniRect2","EditorIcons"),
+ Control::get_icon("MiniVector3","EditorIcons"),
+ Control::get_icon("MiniMatrix32","EditorIcons"),
+ Control::get_icon("MiniPlane","EditorIcons"),
+ Control::get_icon("MiniQuat","EditorIcons"),
+ Control::get_icon("MiniAabb","EditorIcons"),
+ Control::get_icon("MiniMatrix3","EditorIcons"),
+ Control::get_icon("MiniTransform","EditorIcons"),
+ Control::get_icon("MiniColor","EditorIcons"),
+ Control::get_icon("MiniImage","EditorIcons"),
+ Control::get_icon("MiniPath","EditorIcons"),
+ Control::get_icon("MiniRid","EditorIcons"),
+ Control::get_icon("MiniObject","EditorIcons"),
+ Control::get_icon("MiniInput","EditorIcons"),
+ Control::get_icon("MiniDictionary","EditorIcons"),
+ Control::get_icon("MiniArray","EditorIcons"),
+ Control::get_icon("MiniRawArray","EditorIcons"),
+ Control::get_icon("MiniIntArray","EditorIcons"),
+ Control::get_icon("MiniFloatArray","EditorIcons"),
+ Control::get_icon("MiniStringArray","EditorIcons"),
+ Control::get_icon("MiniVector2Array","EditorIcons"),
+ Control::get_icon("MiniVector3Array","EditorIcons"),
+ Control::get_icon("MiniColorArray","EditorIcons")
+ };
+
+
+
+ Ref<Texture> seq_port = Control::get_icon("VisualShaderPort","EditorIcons");
+
+ List<int> ids;
+ script->get_node_list(edited_func,&ids);
+ StringName editor_icons="EditorIcons";
+
+ for(List<int>::Element *E=ids.front();E;E=E->next()) {
+
+ if (p_only_id>=0 && p_only_id!=E->get())
+ continue;
+
+ Ref<VisualScriptNode> node = script->get_node(edited_func,E->get());
+ Vector2 pos = script->get_node_pos(edited_func,E->get());
+
+ GraphNode *gnode = memnew( GraphNode );
+ gnode->set_title(node->get_caption());
+ if (error_line==E->get()) {
+ gnode->set_overlay(GraphNode::OVERLAY_POSITION);
+ } else if (node->is_breakpoint()) {
+ gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT);
+ }
+
+ if (EditorSettings::get_singleton()->has("visual_script_editor/color_"+node->get_category())) {
+ gnode->set_modulate(EditorSettings::get_singleton()->get("visual_script_editor/color_"+node->get_category()));
+ }
+
+
+
+ gnode->set_meta("__vnode",node);
+ gnode->set_name(itos(E->get()));
+ gnode->connect("dragged",this,"_node_moved",varray(E->get()));
+ gnode->connect("close_request",this,"_remove_node",varray(E->get()),CONNECT_DEFERRED);
+
+
+ if (E->get()!=script->get_function_node_id(edited_func)) {
+ //function can't be erased
+ gnode->set_show_close_button(true);
+ }
+
+
+ Label *text = memnew( Label );
+ text->set_text(node->get_text());
+ gnode->add_child(text);
+ if (node->cast_to<VisualScriptExpression>()) {
+ text->add_font_override("font",get_font("source","EditorFonts"));
+ }
+
+ if (node->cast_to<VisualScriptComment>()) {
+ Ref<VisualScriptComment> vsc=node;
+ gnode->set_comment(true);
+ gnode->set_resizeable(true);
+ gnode->set_custom_minimum_size(vsc->get_size()*EDSCALE);
+ gnode->connect("resize_request",this,"_comment_node_resized",varray(E->get()));
+
+ }
+
+
+ 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,Color(1,1,1,1),single_seq_output,TYPE_SEQUENCE,Color(1,1,1,1),seq_port,seq_port);
+ gnode->set_offset(pos*EDSCALE);
+ slot_idx++;
+
+
+ int mixed_seq_ports=0;
+
+ if (!single_seq_output) {
+
+ if (node->has_mixed_input_and_sequence_ports()) {
+ mixed_seq_ports=node->get_output_sequence_port_count();
+ } else {
+ for(int i=0;i<node->get_output_sequence_port_count();i++) {
+
+ Label *text2 = memnew( Label );
+ text2->set_text(node->get_output_sequence_port_text(i));
+ text2->set_align(Label::ALIGN_RIGHT);
+ gnode->add_child(text2);
+ gnode->set_slot(slot_idx,false,0,Color(),true,TYPE_SEQUENCE,Color(1,1,1,1),seq_port,seq_port);
+ slot_idx++;
+ }
+ }
+ }
+
+ for(int i=0;i<MAX(node->get_output_value_port_count(),MAX(mixed_seq_ports,node->get_input_value_port_count()));i++) {
+
+ bool left_ok=false;
+ Variant::Type left_type=Variant::NIL;
+ String left_name;
+
+
+
+ if (i<node->get_input_value_port_count()) {
+ PropertyInfo pi = node->get_input_value_port_info(i);
+ left_ok=true;
+ left_type=pi.type;
+ left_name=pi.name;
+ }
+
+ bool right_ok=false;
+ Variant::Type right_type=Variant::NIL;
+ String right_name;
+
+ if (i>=mixed_seq_ports && i<node->get_output_value_port_count()+mixed_seq_ports) {
+ PropertyInfo pi = node->get_output_value_port_info(i-mixed_seq_ports);
+ right_ok=true;
+ right_type=pi.type;
+ right_name=pi.name;
+ }
+
+ HBoxContainer *hbc = memnew( HBoxContainer);
+
+ if (left_ok) {
+
+ Ref<Texture> t;
+ if (left_type>=0 && left_type<Variant::VARIANT_MAX) {
+ t=type_icons[left_type];
+ }
+ if (t.is_valid()) {
+ TextureFrame *tf = memnew(TextureFrame);
+ tf->set_texture(t);
+ tf->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED);
+ hbc->add_child(tf);
+ }
+
+ hbc->add_child(memnew(Label(left_name)));
+
+ if (left_type!=Variant::NIL && !script->is_input_value_port_connected(edited_func,E->get(),i)) {
+
+ PropertyInfo pi = node->get_input_value_port_info(i);
+ Button *button = memnew( Button );
+ Variant value = node->get_default_input_value(i);
+ if (value.get_type()!=left_type) {
+ //different type? for now convert
+ //not the same, reconvert
+ Variant::CallError ce;
+ const Variant *existingp=&value;
+ value = Variant::construct(left_type,&existingp,1,ce,false);
+ }
+
+ if (left_type==Variant::COLOR) {
+ button->set_custom_minimum_size(Size2(30,0)*EDSCALE);
+ button->connect("draw",this,"_draw_color_over_button",varray(button,value));
+ } else if (left_type==Variant::OBJECT && Ref<Resource>(value).is_valid()) {
+
+ Ref<Resource> res = value;
+ Array arr;
+ arr.push_back(button->get_instance_ID());
+ arr.push_back(String(value));
+ EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res,this,"_button_resource_previewed",arr);
+
+ } else if (pi.type==Variant::INT && pi.hint==PROPERTY_HINT_ENUM){
+
+ button->set_text(pi.hint_string.get_slice(",",value));
+ } else {
+
+ button->set_text(value);
+ }
+ button->connect("pressed",this,"_default_value_edited",varray(button,E->get(),i));
+ hbc->add_child(button);
+ }
+ } else {
+ Control *c = memnew(Control);
+ c->set_custom_minimum_size(Size2(10,0)*EDSCALE);
+ hbc->add_child(c);
+ }
+
+ hbc->add_spacer();
+
+ if (i<mixed_seq_ports) {
+
+ Label *text2 = memnew( Label );
+ text2->set_text(node->get_output_sequence_port_text(i));
+ text2->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(text2);
+ }
+
+ if (right_ok) {
+
+ hbc->add_child(memnew(Label(right_name)));
+
+ Ref<Texture> t;
+ if (right_type>=0 && right_type<Variant::VARIANT_MAX) {
+ t=type_icons[right_type];
+ }
+ if (t.is_valid()) {
+ TextureFrame *tf = memnew(TextureFrame);
+ tf->set_texture(t);
+ tf->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED);
+ hbc->add_child(tf);
+ }
+
+ }
+
+ gnode->add_child(hbc);
+
+ if (i<mixed_seq_ports) {
+ gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),true,TYPE_SEQUENCE,Color(1,1,1,1),Ref<Texture>(),seq_port);
+ } else {
+ gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),right_ok,right_type,_color_from_type(right_type));
+ }
+
+ slot_idx++;
+ }
+
+ graph->add_child(gnode);
+
+ if (gnode->is_comment()) {
+ graph->move_child(gnode,0);
+ }
+ }
+
+ _update_graph_connections();
+ graph->call_deferred("set_scroll_ofs",script->get_function_scroll(edited_func)*EDSCALE); //may need to adapt a bit, let it do so
+ updating_graph=false;
+
+}
+
+void VisualScriptEditor::_update_members() {
+
+
+ updating_members=true;
+
+ members->clear();
+ TreeItem *root = members->create_item();
+
+ TreeItem *functions = members->create_item(root);
+ functions->set_selectable(0,false);
+ functions->set_text(0,TTR("Functions:"));
+ functions->add_button(0,Control::get_icon("Override","EditorIcons"),1);
+ functions->add_button(0,Control::get_icon("Add","EditorIcons"),0);
+ functions->set_custom_bg_color(0,Control::get_color("prop_section","Editor"));
+
+ List<StringName> func_names;
+ script->get_function_list(&func_names);
+ for (List<StringName>::Element *E=func_names.front();E;E=E->next()) {
+ TreeItem *ti = members->create_item(functions) ;
+ ti->set_text(0,E->get());
+ ti->set_selectable(0,true);
+ ti->set_editable(0,true);
+ //ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); function arguments are in the node now
+ ti->add_button(0,Control::get_icon("Del","EditorIcons"),1);
+ ti->set_metadata(0,E->get());
+ if (E->get()==edited_func) {
+ ti->set_custom_bg_color(0,get_color("prop_category","Editor"));
+ ti->set_custom_color(0,Color(1,1,1,1));
+ }
+ if (selected==E->get())
+ ti->select(0);
+ }
+
+ TreeItem *variables = members->create_item(root);
+ variables->set_selectable(0,false);
+ variables->set_text(0,TTR("Variables:"));
+ variables->add_button(0,Control::get_icon("Add","EditorIcons"));
+ variables->set_custom_bg_color(0,Control::get_color("prop_section","Editor"));
+
+ Ref<Texture> type_icons[Variant::VARIANT_MAX]={
+ Control::get_icon("MiniVariant","EditorIcons"),
+ Control::get_icon("MiniBoolean","EditorIcons"),
+ Control::get_icon("MiniInteger","EditorIcons"),
+ Control::get_icon("MiniFloat","EditorIcons"),
+ Control::get_icon("MiniString","EditorIcons"),
+ Control::get_icon("MiniVector2","EditorIcons"),
+ Control::get_icon("MiniRect2","EditorIcons"),
+ Control::get_icon("MiniVector3","EditorIcons"),
+ Control::get_icon("MiniMatrix32","EditorIcons"),
+ Control::get_icon("MiniPlane","EditorIcons"),
+ Control::get_icon("MiniQuat","EditorIcons"),
+ Control::get_icon("MiniAabb","EditorIcons"),
+ Control::get_icon("MiniMatrix3","EditorIcons"),
+ Control::get_icon("MiniTransform","EditorIcons"),
+ Control::get_icon("MiniColor","EditorIcons"),
+ Control::get_icon("MiniImage","EditorIcons"),
+ Control::get_icon("MiniPath","EditorIcons"),
+ Control::get_icon("MiniRid","EditorIcons"),
+ Control::get_icon("MiniObject","EditorIcons"),
+ Control::get_icon("MiniInput","EditorIcons"),
+ Control::get_icon("MiniDictionary","EditorIcons"),
+ Control::get_icon("MiniArray","EditorIcons"),
+ Control::get_icon("MiniRawArray","EditorIcons"),
+ Control::get_icon("MiniIntArray","EditorIcons"),
+ Control::get_icon("MiniFloatArray","EditorIcons"),
+ Control::get_icon("MiniStringArray","EditorIcons"),
+ Control::get_icon("MiniVector2Array","EditorIcons"),
+ Control::get_icon("MiniVector3Array","EditorIcons"),
+ Control::get_icon("MiniColorArray","EditorIcons")
+ };
+
+ List<StringName> var_names;
+ script->get_variable_list(&var_names);
+ for (List<StringName>::Element *E=var_names.front();E;E=E->next()) {
+ TreeItem *ti = members->create_item(variables);
+
+ ti->set_text(0,E->get());
+ Variant var = script->get_variable_default_value(E->get());
+ ti->set_suffix(0,"="+String(var));
+ ti->set_icon(0,type_icons[script->get_variable_info(E->get()).type]);
+
+ ti->set_selectable(0,true);
+ ti->set_editable(0,true);
+ ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0);
+ ti->add_button(0,Control::get_icon("Del","EditorIcons"),1);
+ ti->set_metadata(0,E->get());
+ if (selected==E->get())
+ ti->select(0);
+ }
+
+ TreeItem *_signals = members->create_item(root);
+ _signals->set_selectable(0,false);
+ _signals->set_text(0,TTR("Signals:"));
+ _signals->add_button(0,Control::get_icon("Add","EditorIcons"));
+ _signals->set_custom_bg_color(0,Control::get_color("prop_section","Editor"));
+
+ List<StringName> signal_names;
+ script->get_custom_signal_list(&signal_names);
+ for (List<StringName>::Element *E=signal_names.front();E;E=E->next()) {
+ TreeItem *ti = members->create_item(_signals);
+ ti->set_text(0,E->get());
+ ti->set_selectable(0,true);
+ ti->set_editable(0,true);
+ ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0);
+ ti->add_button(0,Control::get_icon("Del","EditorIcons"),1);
+ ti->set_metadata(0,E->get());
+ if (selected==E->get())
+ ti->select(0);
+ }
+
+ String base_type=script->get_instance_base_type();
+ String icon_type=base_type;
+ if (!Control::has_icon(base_type,"EditorIcons")) {
+ icon_type="Object";
+ }
+
+ base_type_select->set_text(base_type);
+ base_type_select->set_icon(Control::get_icon(icon_type,"EditorIcons"));
+
+ updating_members=false;
+
+}
+
+void VisualScriptEditor::_member_selected() {
+
+ if (updating_members)
+ return;
+
+ TreeItem *ti=members->get_selected();
+ ERR_FAIL_COND(!ti);
+
+
+ selected=ti->get_metadata(0);
+// print_line("selected: "+String(selected));
+
+
+ if (ti->get_parent()==members->get_root()->get_children()) {
+
+ if (edited_func!=selected) {
+
+ revert_on_drag=edited_func;
+ edited_func=selected;
+ _update_members();
+ _update_graph();
+ }
+
+ return; //or crash because it will become invalid
+
+ }
+
+
+
+}
+
+void VisualScriptEditor::_member_edited() {
+
+ if (updating_members)
+ return;
+
+ TreeItem *ti=members->get_edited();
+ ERR_FAIL_COND(!ti);
+
+ String name = ti->get_metadata(0);
+ String new_name = ti->get_text(0);
+
+ if (name==new_name)
+ return;
+
+ if (!new_name.is_valid_identifier()) {
+
+ EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:")+" "+new_name);
+ updating_members=true;
+ ti->set_text(0,name);
+ updating_members=false;
+ return;
+
+ }
+
+ if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) {
+
+ EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:")+" "+new_name);
+ updating_members=true;
+ ti->set_text(0,name);
+ updating_members=false;
+ return;
+ }
+
+ TreeItem *root=members->get_root();
+
+ if (ti->get_parent()==root->get_children()) {
+
+ if (edited_func==selected) {
+ edited_func=new_name;
+ }
+ selected=new_name;
+
+
+ _update_graph();
+
+ undo_redo->create_action(TTR("Rename Function"));
+ undo_redo->add_do_method(script.ptr(),"rename_function",name,new_name);
+ undo_redo->add_undo_method(script.ptr(),"rename_function",new_name,name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+
+ return; //or crash because it will become invalid
+
+ }
+
+ if (ti->get_parent()==root->get_children()->get_next()) {
+
+ selected=new_name;
+ undo_redo->create_action(TTR("Rename Variable"));
+ undo_redo->add_do_method(script.ptr(),"rename_variable",name,new_name);
+ undo_redo->add_undo_method(script.ptr(),"rename_variable",new_name,name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+
+ return; //or crash because it will become invalid
+ }
+
+ if (ti->get_parent()==root->get_children()->get_next()->get_next()) {
+
+ selected=new_name;
+ undo_redo->create_action(TTR("Rename Signal"));
+ undo_redo->add_do_method(script.ptr(),"rename_custom_signal",name,new_name);
+ undo_redo->add_undo_method(script.ptr(),"rename_custom_signal",new_name,name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+
+ return; //or crash because it will become invalid
+ }
+}
+
+void VisualScriptEditor::_override_pressed(int p_id) {
+
+ //override a virtual function or method from base type
+
+ ERR_FAIL_COND(!virtuals_in_menu.has(p_id));
+
+ VirtualInMenu vim=virtuals_in_menu[p_id];
+
+ String name = _validate_name(vim.name);
+ selected=name;
+ edited_func=selected;
+ Ref<VisualScriptFunction> func_node;
+ func_node.instance();
+ func_node->set_name(vim.name);
+
+ undo_redo->create_action(TTR("Add Function"));
+ undo_redo->add_do_method(script.ptr(),"add_function",name);
+ for(int i=0;i<vim.args.size();i++) {
+ func_node->add_argument(vim.args[i].first,vim.args[i].second);
+ }
+
+
+ undo_redo->add_do_method(script.ptr(),"add_node",name,script->get_available_id(),func_node);
+ if (vim.ret!=Variant::NIL || vim.ret_variant) {
+ Ref<VisualScriptReturn> ret_node;
+ ret_node.instance();
+ ret_node->set_return_type(vim.ret);
+ ret_node->set_enable_return_value(true);
+ ret_node->set_name(vim.name);
+ undo_redo->add_do_method(script.ptr(),"add_node",name,script->get_available_id()+1,ret_node,Vector2(500,0));
+
+ }
+
+ undo_redo->add_undo_method(script.ptr(),"remove_function",name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+
+
+ _update_graph();
+}
+
+void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) {
+
+ TreeItem *ti=p_item->cast_to<TreeItem>();
+
+ TreeItem *root=members->get_root();
+
+ if (ti->get_parent()==root) {
+ //main buttons
+ if (ti==root->get_children()) {
+ //add function, this one uses menu
+
+ if (p_button==1) {
+ new_function_menu->clear();
+ new_function_menu->set_size(Size2(0,0));
+ int idx=0;
+
+ virtuals_in_menu.clear();
+
+ List<MethodInfo> mi;
+ ObjectTypeDB::get_method_list(script->get_instance_base_type(),&mi);
+ for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) {
+ MethodInfo mi=E->get();
+ if (mi.flags&METHOD_FLAG_VIRTUAL) {
+
+ VirtualInMenu vim;
+ vim.name=mi.name;
+ vim.ret=mi.return_val.type;
+ if (mi.return_val.name!=String())
+ vim.ret_variant=true;
+ else
+ vim.ret_variant=false;
+
+ String desc;
+
+ if (mi.return_val.type==Variant::NIL)
+ desc="var";
+ else
+ desc=Variant::get_type_name(mi.return_val.type);
+ desc+=" "+mi.name+" ( ";
+
+
+ for(int i=0;i<mi.arguments.size();i++) {
+
+ if (i>0)
+ desc+=", ";
+
+ if (mi.arguments[i].type==Variant::NIL)
+ desc+="var ";
+ else
+ desc+=Variant::get_type_name(mi.arguments[i].type)+" ";
+
+ desc+=mi.arguments[i].name;
+
+ Pair<Variant::Type,String> p;
+ p.first=mi.arguments[i].type;
+ p.second=mi.arguments[i].name;
+ vim.args.push_back( p );
+
+ }
+
+ desc+=" )";
+
+ virtuals_in_menu[idx]=vim;
+
+ new_function_menu->add_item(desc,idx);
+ idx++;
+ }
+ }
+
+ Rect2 pos = members->get_item_rect(ti);
+ new_function_menu->set_pos(members->get_global_pos()+pos.pos+Vector2(0,pos.size.y));
+ new_function_menu->popup();
+ return;
+ } else if (p_button==0) {
+
+
+ String name = _validate_name("new_function");
+ selected=name;
+ edited_func=selected;
+
+ Ref<VisualScriptFunction> func_node;
+ func_node.instance();
+ func_node->set_name(name);
+
+ undo_redo->create_action(TTR("Add Function"));
+ undo_redo->add_do_method(script.ptr(),"add_function",name);
+ undo_redo->add_do_method(script.ptr(),"add_node",name,script->get_available_id(),func_node);
+ undo_redo->add_undo_method(script.ptr(),"remove_function",name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+
+ _update_graph();
+ }
+
+ return; //or crash because it will become invalid
+
+ }
+
+ if (ti==root->get_children()->get_next()) {
+ //add variable
+ String name = _validate_name("new_variable");
+ selected=name;
+
+ undo_redo->create_action(TTR("Add Variable"));
+ undo_redo->add_do_method(script.ptr(),"add_variable",name);
+ undo_redo->add_undo_method(script.ptr(),"remove_variable",name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+ return; //or crash because it will become invalid
+
+ }
+
+ if (ti==root->get_children()->get_next()->get_next()) {
+ //add variable
+ String name = _validate_name("new_signal");
+ selected=name;
+
+ undo_redo->create_action(TTR("Add Signal"));
+ undo_redo->add_do_method(script.ptr(),"add_custom_signal",name);
+ undo_redo->add_undo_method(script.ptr(),"remove_custom_signal",name);
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+ return; //or crash because it will become invalid
+
+ }
+
+ } else {
+
+ if (ti->get_parent()==root->get_children()) {
+ //edit/remove function
+ String name = ti->get_metadata(0);
+
+ if (p_button==1) {
+ //delete the function
+ undo_redo->create_action(TTR("Remove Function"));
+ undo_redo->add_do_method(script.ptr(),"remove_function",name);
+ undo_redo->add_undo_method(script.ptr(),"add_function",name);
+ List<int> nodes;
+ script->get_node_list(name,&nodes);
+ for (List<int>::Element *E=nodes.front();E;E=E->next()) {
+ undo_redo->add_undo_method(script.ptr(),"add_node",name,E->get(),script->get_node(name,E->get()),script->get_node_pos(name,E->get()));
+ }
+
+ List<VisualScript::SequenceConnection> seq_connections;
+
+ script->get_sequence_connection_list(name,&seq_connections);
+
+ for (List<VisualScript::SequenceConnection>::Element *E=seq_connections.front();E;E=E->next()) {
+ undo_redo->add_undo_method(script.ptr(),"sequence_connect",name,E->get().from_node,E->get().from_output,E->get().to_node);
+ }
+
+ List<VisualScript::DataConnection> data_connections;
+
+ script->get_data_connection_list(name,&data_connections);
+
+ for (List<VisualScript::DataConnection>::Element *E=data_connections.front();E;E=E->next()) {
+ undo_redo->add_undo_method(script.ptr(),"data_connect",name,E->get().from_node,E->get().from_port,E->get().to_node,E->get().to_port);
+ }
+
+ //for(int i=0;i<script->function_get_argument_count(name);i++) {
+ //// undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i));
+ //}
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ } else if (p_button==0) {
+
+ }
+ return; //or crash because it will become invalid
+
+ }
+
+ if (ti->get_parent()==root->get_children()->get_next()) {
+ //edit/remove variable
+
+ String name = ti->get_metadata(0);
+
+ if (p_button==1) {
+
+
+ undo_redo->create_action(TTR("Remove Variable"));
+ undo_redo->add_do_method(script.ptr(),"remove_variable",name);
+ undo_redo->add_undo_method(script.ptr(),"add_variable",name,script->get_variable_default_value(name));
+ undo_redo->add_undo_method(script.ptr(),"set_variable_info",name,script->call("get_variable_info",name)); //return as dict
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+ return; //or crash because it will become invalid
+ } else if (p_button==0) {
+
+ variable_editor->edit(name);
+ edit_variable_dialog->set_title(TTR("Editing Variable:")+" "+name);
+ edit_variable_dialog->popup_centered_minsize(Size2(400,200)*EDSCALE);
+ }
+
+ }
+
+ if (ti->get_parent()==root->get_children()->get_next()->get_next()) {
+ //edit/remove variable
+ String name = ti->get_metadata(0);
+
+ if (p_button==1) {
+
+ undo_redo->create_action(TTR("Remove Signal"));
+ undo_redo->add_do_method(script.ptr(),"remove_custom_signal",name);
+ undo_redo->add_undo_method(script.ptr(),"add_custom_signal",name);
+
+ for(int i=0;i<script->custom_signal_get_argument_count(name);i++) {
+ undo_redo->add_undo_method(script.ptr(),"custom_signal_add_argument",name,script->custom_signal_get_argument_name(name,i),script->custom_signal_get_argument_type(name,i));
+ }
+
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+ } else if (p_button==0) {
+
+ signal_editor->edit(name);
+ edit_signal_dialog->set_title(TTR("Editing Signal:")+" "+name);
+ edit_signal_dialog->popup_centered_minsize(Size2(400,300)*EDSCALE);
+ }
+
+ return; //or crash because it will become invalid
+
+ }
+
+
+ }
+}
+
+void VisualScriptEditor::_available_node_doubleclicked() {
+
+ TreeItem *item = nodes->get_selected();
+
+ if (!item)
+ return;
+
+ String which = item->get_metadata(0);
+ if (which==String())
+ return;
+
+ Vector2 ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5;
+
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ while(true) {
+ bool exists=false;
+ List<int> existing;
+ script->get_node_list(edited_func,&existing);
+ for (List<int>::Element *E=existing.front();E;E=E->next()) {
+ Point2 pos = script->get_node_pos(edited_func,E->get());
+ if (pos.distance_to(ofs)<15) {
+ ofs+=Vector2(graph->get_snap(),graph->get_snap());
+ exists=true;
+ break;
+ }
+ }
+
+ if (exists)
+ continue;
+ break;
+
+ }
+
+
+ Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(which);
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+
+}
+
+void VisualScriptEditor::_update_available_nodes() {
+
+ nodes->clear();
+
+ TreeItem *root = nodes->create_item();
+
+ Map<String,TreeItem*> path_cache;
+
+ String filter = node_filter->get_text();
+
+ List<String> fnodes;
+ VisualScriptLanguage::singleton->get_registered_node_names(&fnodes);
+
+ for (List<String>::Element *E=fnodes.front();E;E=E->next()) {
+
+
+ Vector<String> path = E->get().split("/");
+
+ if (filter!=String() && path.size() && path[path.size()-1].findn(filter)==-1)
+ continue;
+
+ String sp;
+ TreeItem* parent=root;
+
+ for(int i=0;i<path.size()-1;i++) {
+
+ if (i>0)
+ sp+=",";
+ sp+=path[i];
+ if (!path_cache.has(sp)) {
+ TreeItem* pathn = nodes->create_item(parent);
+ pathn->set_selectable(0,false);
+ pathn->set_text(0,path[i].capitalize());
+ path_cache[sp]=pathn;
+ parent=pathn;
+ if (filter==String()) {
+ pathn->set_collapsed(true); //should remember state
+ }
+ } else {
+ parent=path_cache[sp];
+ }
+ }
+
+ TreeItem *item = nodes->create_item(parent);
+ item->set_text(0,path[path.size()-1].capitalize());
+ item->set_selectable(0,true);
+ item->set_metadata(0,E->get());
+ }
+
+}
+
+String VisualScriptEditor::_validate_name(const String& p_name) const {
+
+ String valid=p_name;
+
+ int counter=1;
+ while(true) {
+
+ bool exists = script->has_function(valid) || script->has_variable(valid) || script->has_custom_signal(valid);
+
+ if (exists) {
+ counter++;
+ valid=p_name+"_"+itos(counter);
+ continue;
+ }
+
+ break;
+ }
+
+ return valid;
+}
+
+void VisualScriptEditor::_on_nodes_delete() {
+
+
+ List<int> to_erase;
+
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ if (gn->is_selected() && gn->is_close_button_visible()) {
+ to_erase.push_back(gn->get_name().operator String().to_int());
+ }
+ }
+ }
+
+ if (to_erase.empty())
+ return;
+
+ undo_redo->create_action("Remove VisualScript Nodes");
+
+ for(List<int>::Element*F=to_erase.front();F;F=F->next()) {
+
+
+ undo_redo->add_do_method(script.ptr(),"remove_node",edited_func,F->get());
+ undo_redo->add_undo_method(script.ptr(),"add_node",edited_func,F->get(),script->get_node(edited_func,F->get()),script->get_node_pos(edited_func,F->get()));
+
+
+ List<VisualScript::SequenceConnection> sequence_conns;
+ script->get_sequence_connection_list(edited_func,&sequence_conns);
+
+
+ for (List<VisualScript::SequenceConnection>::Element *E=sequence_conns.front();E;E=E->next()) {
+
+ if (E->get().from_node==F->get() || E->get().to_node==F->get()) {
+ undo_redo->add_undo_method(script.ptr(),"sequence_connect",edited_func,E->get().from_node,E->get().from_output,E->get().to_node);
+ }
+ }
+
+ List<VisualScript::DataConnection> data_conns;
+ script->get_data_connection_list(edited_func,&data_conns);
+
+ for (List<VisualScript::DataConnection>::Element *E=data_conns.front();E;E=E->next()) {
+
+ if (E->get().from_node==F->get() || E->get().to_node==F->get()) {
+ undo_redo->add_undo_method(script.ptr(),"data_connect",edited_func,E->get().from_node,E->get().from_port,E->get().to_node,E->get().to_port);
+ }
+ }
+
+ }
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+}
+
+
+void VisualScriptEditor::_on_nodes_duplicate() {
+
+
+ List<int> to_duplicate;
+
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ if (gn->is_selected() && gn->is_close_button_visible()) {
+ to_duplicate.push_back(gn->get_name().operator String().to_int());
+ }
+ }
+ }
+
+ if (to_duplicate.empty())
+ return;
+
+ undo_redo->create_action("Duplicate VisualScript Nodes");
+ int idc=script->get_available_id()+1;
+
+ Set<int> to_select;
+
+ for(List<int>::Element*F=to_duplicate.front();F;F=F->next()) {
+
+ Ref<VisualScriptNode> node = script->get_node(edited_func,F->get());
+
+ Ref<VisualScriptNode> dupe = node->duplicate();
+
+ int new_id = idc++;
+ to_select.insert(new_id);
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,dupe,script->get_node_pos(edited_func,F->get())+Vector2(20,20));
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+
+ }
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ int id = gn->get_name().operator String().to_int();
+ gn->set_selected(to_select.has(id));
+
+ }
+ }
+
+ if (to_select.size()) {
+ EditorNode::get_singleton()->push_item(script->get_node(edited_func,to_select.front()->get()).ptr());
+ }
+
+}
+
+void VisualScriptEditor::_input(const InputEvent& p_event) {
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && !p_event.mouse_button.pressed && p_event.mouse_button.button_index==BUTTON_LEFT) {
+ revert_on_drag=String(); //so we can still drag functions
+ }
+}
+
+Variant VisualScriptEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) {
+
+
+ if (p_from==nodes) {
+
+ TreeItem *it = nodes->get_item_at_pos(p_point);
+ if (!it)
+ return Variant();
+ String type=it->get_metadata(0);
+ if (type==String())
+ return Variant();
+
+ Dictionary dd;
+ dd["type"]="visual_script_node_drag";
+ dd["node_type"]=type;
+
+ Label *label = memnew(Label);
+ label->set_text(it->get_text(0));
+ set_drag_preview(label);
+ return dd;
+ }
+
+ if (p_from==members) {
+
+
+ TreeItem *it = members->get_item_at_pos(p_point);
+ if (!it)
+ return Variant();
+
+ String type=it->get_metadata(0);
+
+ if (type==String())
+ return Variant();
+
+
+ Dictionary dd;
+ TreeItem *root=members->get_root();
+
+ if (it->get_parent()==root->get_children()) {
+
+ dd["type"]="visual_script_function_drag";
+ dd["function"]=type;
+ if (revert_on_drag!=String()) {
+ edited_func=revert_on_drag; //revert so function does not change
+ revert_on_drag=String();
+ _update_graph();
+ }
+ } else if (it->get_parent()==root->get_children()->get_next()) {
+
+ dd["type"]="visual_script_variable_drag";
+ dd["variable"]=type;
+ } else if (it->get_parent()==root->get_children()->get_next()->get_next()) {
+
+ dd["type"]="visual_script_signal_drag";
+ dd["signal"]=type;
+
+ } else {
+ return Variant();
+ }
+
+
+
+
+
+
+ Label *label = memnew(Label);
+ label->set_text(it->get_text(0));
+ set_drag_preview(label);
+ return dd;
+ }
+ return Variant();
+}
+
+bool VisualScriptEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const{
+
+ if (p_from==graph) {
+
+
+ Dictionary d = p_data;
+ if (d.has("type") &&
+ (
+ String(d["type"])=="visual_script_node_drag" ||
+ String(d["type"])=="visual_script_function_drag" ||
+ String(d["type"])=="visual_script_variable_drag" ||
+ String(d["type"])=="visual_script_signal_drag" ||
+ String(d["type"])=="obj_property" ||
+ String(d["type"])=="resource" ||
+ String(d["type"])=="files" ||
+ String(d["type"])=="nodes"
+ ) ) {
+
+
+ if (String(d["type"])=="obj_property") {
+
+#ifdef OSX_ENABLED
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a Getter. Hold Shift to drop a generic signature."));
+#else
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."));
+#endif
+ }
+
+ if (String(d["type"])=="nodes") {
+
+#ifdef OSX_ENABLED
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a simple reference to the node."));
+#else
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node."));
+#endif
+ }
+
+ if (String(d["type"])=="visual_script_variable_drag") {
+
+#ifdef OSX_ENABLED
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a Variable Setter."));
+#else
+ const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter."));
+#endif
+ }
+
+ return true;
+
+ }
+
+
+
+ }
+
+
+ return false;
+}
+
+#ifdef TOOLS_ENABLED
+
+static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) {
+
+ if (p_edited_scene!=p_current_node && p_current_node->get_owner()!=p_edited_scene)
+ return NULL;
+
+ Ref<Script> scr = p_current_node->get_script();
+
+ if (scr.is_valid() && scr==script)
+ return p_current_node;
+
+ for(int i=0;i<p_current_node->get_child_count();i++) {
+ Node *n = _find_script_node(p_edited_scene,p_current_node->get_child(i),script);
+ if (n)
+ return n;
+ }
+
+ return NULL;
+}
+
+#endif
+
+
+
+void VisualScriptEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from){
+
+ if (p_from==graph) {
+
+ Dictionary d = p_data;
+ if (d.has("type") && String(d["type"])=="visual_script_node_drag") {
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+
+ ofs/=EDSCALE;
+
+ Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(d["node_type"]);
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+ }
+
+ if (d.has("type") && String(d["type"])=="visual_script_variable_drag") {
+
+#ifdef OSX_ENABLED
+ bool use_set = Input::get_singleton()->is_key_pressed(KEY_META);
+#else
+ bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+#endif
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ Ref<VisualScriptNode> vnode;
+ if (use_set) {
+ Ref<VisualScriptVariableSet> vnodes;
+ vnodes.instance();
+ vnodes->set_variable(d["variable"]);
+ vnode=vnodes;
+ } else {
+
+ Ref<VisualScriptVariableGet> vnodeg;
+ vnodeg.instance();
+ vnodeg->set_variable(d["variable"]);
+ vnode=vnodeg;
+ }
+
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+ }
+
+ if (d.has("type") && String(d["type"])=="visual_script_function_drag") {
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ Ref<VisualScriptFunctionCall> vnode;
+ vnode.instance();
+ vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
+
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ undo_redo->add_do_method(vnode.ptr(),"set_base_type",script->get_instance_base_type());
+ undo_redo->add_do_method(vnode.ptr(),"set_function",d["function"]);
+
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+
+ }
+
+
+ if (d.has("type") && String(d["type"])=="visual_script_signal_drag") {
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ Ref<VisualScriptEmitSignal> vnode;
+ vnode.instance();
+ vnode->set_signal(d["signal"]);
+
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+ }
+
+ if (d.has("type") && String(d["type"])=="resource") {
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ Ref<VisualScriptPreload> prnode;
+ prnode.instance();
+ prnode->set_preload(d["resource"]);
+
+ int new_id = script->get_available_id();
+
+ undo_redo->create_action(TTR("Add Preload Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,prnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ Node* node = graph->get_node(itos(new_id));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+ }
+
+ if (d.has("type") && String(d["type"])=="files") {
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+
+ Array files = d["files"];
+
+ List<int> new_ids;
+ int new_id = script->get_available_id();
+
+ if (files.size()) {
+ undo_redo->create_action(TTR("Add Preload Node"));
+
+ for(int i=0;i<files.size();i++) {
+
+ Ref<Resource> res = ResourceLoader::load(files[i]);
+ if (!res.is_valid())
+ continue;
+
+ Ref<VisualScriptPreload> prnode;
+ prnode.instance();
+ prnode->set_preload(res);
+
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,prnode,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ new_ids.push_back(new_id);
+ new_id++;
+ ofs+=Vector2(20,20)*EDSCALE;
+ }
+
+
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+ }
+
+ for(List<int>::Element *E=new_ids.front();E;E=E->next()) {
+
+ Node* node = graph->get_node(itos(E->get()));
+ if (node) {
+ graph->set_selected(node);
+ _node_selected(node);
+ }
+ }
+ }
+
+ if (d.has("type") && String(d["type"])=="nodes") {
+
+ Node* sn = _find_script_node(get_tree()->get_edited_scene_root(),get_tree()->get_edited_scene_root(),script);
+
+
+ if (!sn) {
+ EditorNode::get_singleton()->show_warning("Can't drop nodes because script '"+get_name()+"' is not used in this scene.");
+ return;
+ }
+
+
+#ifdef OSX_ENABLED
+ bool use_node = Input::get_singleton()->is_key_pressed(KEY_META);
+#else
+ bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+#endif
+
+
+ Array nodes = d["nodes"];
+
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+ ofs/=EDSCALE;
+
+ undo_redo->create_action(TTR("Add Node(s) From Tree"));
+ int base_id = script->get_available_id();
+
+ if (nodes.size()>1) {
+ use_node=true;
+ }
+
+ for(int i=0;i<nodes.size();i++) {
+
+ NodePath np = nodes[i];
+ Node *node = get_node(np);
+ if (!node) {
+ continue;
+ }
+
+ Ref<VisualScriptNode> n;
+
+ if (use_node) {
+ Ref<VisualScriptSceneNode> scene_node;
+ scene_node.instance();
+ scene_node->set_node_path(sn->get_path_to(node));
+ n=scene_node;
+
+
+ } else {
+ Ref<VisualScriptFunctionCall> call;
+ call.instance();
+ call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH);
+ call->set_base_path(sn->get_path_to(node));;
+ call->set_base_type(node->get_type());
+ n=call;
+
+ method_select->select_method_from_instance(node);
+ selecting_method_id=base_id;
+
+ }
+
+
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,base_id,n,ofs);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,base_id);
+
+ base_id++;
+ ofs+=Vector2(25,25);
+ }
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+
+ }
+
+ if (d.has("type") && String(d["type"])=="obj_property") {
+
+ Node* sn = _find_script_node(get_tree()->get_edited_scene_root(),get_tree()->get_edited_scene_root(),script);
+
+
+ if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ EditorNode::get_singleton()->show_warning("Can't drop properties because script '"+get_name()+"' is not used in this scene.\nDrop holding 'Shift' to just copy the signature.");
+ return;
+ }
+
+ Object *obj=d["object"];
+
+ if (!obj)
+ return;
+
+ Node *node = obj->cast_to<Node>();
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+
+ ofs/=EDSCALE;
+#ifdef OSX_ENABLED
+ bool use_get = Input::get_singleton()->is_key_pressed(KEY_META);
+#else
+ bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+#endif
+
+ if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+
+
+ if (use_get)
+ undo_redo->create_action(TTR("Add Getter Property"));
+ else
+ undo_redo->create_action(TTR("Add Setter Property"));
+
+ int base_id = script->get_available_id();
+
+ Ref<VisualScriptNode> vnode;
+
+ if (!use_get) {
+
+ Ref<VisualScriptPropertySet> pset;
+ pset.instance();
+ pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+ pset->set_base_type(obj->get_type());
+ /*if (use_value) {
+ pset->set_use_builtin_value(true);
+ pset->set_builtin_value(d["value"]);
+ }*/
+ vnode=pset;
+ } else {
+
+ Ref<VisualScriptPropertyGet> pget;
+ pget.instance();
+ pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+ pget->set_base_type(obj->get_type());
+
+ vnode=pget;
+
+ }
+
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,base_id,vnode,ofs);
+ undo_redo->add_do_method(vnode.ptr(),"set_property",d["property"]);
+ if (!use_get) {
+ undo_redo->add_do_method(vnode.ptr(),"set_default_input_value",0,d["value"]);
+ }
+
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,base_id);
+
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+
+ } else {
+
+
+
+ if (use_get)
+ undo_redo->create_action(TTR("Add Getter Property"));
+ else
+ undo_redo->create_action(TTR("Add Setter Property"));
+
+ int base_id = script->get_available_id();
+
+ Ref<VisualScriptNode> vnode;
+
+ if (!use_get) {
+
+ Ref<VisualScriptPropertySet> pset;
+ pset.instance();
+ if (sn==node) {
+ pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
+ } else {
+ pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
+ pset->set_base_path(sn->get_path_to(node));
+ }
+
+ vnode=pset;
+ } else {
+
+ Ref<VisualScriptPropertyGet> pget;
+ pget.instance();
+ if (sn==node) {
+ pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
+ } else {
+ pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
+ pget->set_base_path(sn->get_path_to(node));
+ }
+ vnode=pget;
+
+ }
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,base_id,vnode,ofs);
+ undo_redo->add_do_method(vnode.ptr(),"set_property",d["property"]);
+ if (!use_get) {
+ undo_redo->add_do_method(vnode.ptr(),"set_default_input_value",0,d["value"]);
+ }
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,base_id);
+
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+ undo_redo->commit_action();
+ }
+
+
+ }
+
+
+ }
+
+
+}
+
+
+void VisualScriptEditor::_selected_method(const String& p_method) {
+
+ Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func,selecting_method_id);
+ if (!vsfc.is_valid())
+ return;
+ vsfc->set_function(p_method);
+
+}
+
+void VisualScriptEditor::_draw_color_over_button(Object* obj,Color p_color) {
+
+ Button *button = obj->cast_to<Button>();
+ if (!button)
+ return;
+
+ Ref<StyleBox> normal = get_stylebox("normal","Button" );
+ button->draw_rect(Rect2(normal->get_offset(),button->get_size()-normal->get_minimum_size()),p_color);
+
+}
+
+void VisualScriptEditor::_button_resource_previewed(const String& p_path,const Ref<Texture>& p_preview,Variant p_ud) {
+
+
+ Array ud=p_ud;
+ ERR_FAIL_COND(ud.size()!=2);
+
+ ObjectID id = ud[0];
+ Object *obj = ObjectDB::get_instance(id);
+
+ if (!obj)
+ return;
+
+ Button *b = obj->cast_to<Button>();
+ ERR_FAIL_COND(!b);
+
+ if (p_preview.is_null()) {
+ b->set_text(ud[1]);
+ } else {
+
+ b->set_icon(p_preview);
+ }
+
+}
+
+/////////////////////////
+
+
+
+void VisualScriptEditor::apply_code() {
+
+
+}
+
+Ref<Script> VisualScriptEditor::get_edited_script() const{
+
+ return script;
+}
+
+Vector<String> VisualScriptEditor::get_functions(){
+
+ return Vector<String>();
+}
+
+void VisualScriptEditor::set_edited_script(const Ref<Script>& p_script){
+
+ script=p_script;
+ signal_editor->script=p_script;
+ signal_editor->undo_redo=undo_redo;
+ variable_editor->script=p_script;
+ variable_editor->undo_redo=undo_redo;
+
+
+ script->connect("node_ports_changed",this,"_node_ports_changed");
+
+ _update_members();
+ _update_available_nodes();
+}
+
+void VisualScriptEditor::reload_text(){
+
+
+}
+
+String VisualScriptEditor::get_name(){
+
+ String name;
+
+ if (script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) {
+ name=script->get_path().get_file();
+ if (is_unsaved()) {
+ name+="(*)";
+ }
+ } else if (script->get_name()!="")
+ name=script->get_name();
+ else
+ name=script->get_type()+"("+itos(script->get_instance_ID())+")";
+
+ return name;
+
+}
+
+Ref<Texture> VisualScriptEditor::get_icon(){
+
+ return Control::get_icon("VisualScript","EditorIcons");
+}
+
+bool VisualScriptEditor::is_unsaved(){
+#ifdef TOOLS_ENABLED
+
+ return script->is_edited() || script->are_subnodes_edited();
+#else
+ return false;
+#endif
+}
+
+Variant VisualScriptEditor::get_edit_state(){
+
+ Dictionary d;
+ d["function"]=edited_func;
+ d["scroll"]=graph->get_scroll_ofs();
+ d["zoom"]=graph->get_zoom();
+ d["using_snap"]=graph->is_using_snap();
+ d["snap"]=graph->get_snap();
+ return d;
+}
+
+void VisualScriptEditor::set_edit_state(const Variant& p_state){
+
+ Dictionary d = p_state;
+ if (d.has("function")) {
+ edited_func=p_state;
+ selected=edited_func;
+
+ }
+
+ _update_graph();
+ _update_members();
+
+ if (d.has("scroll")) {
+ graph->set_scroll_ofs(d["scroll"]);
+ }
+ if (d.has("zoom")) {
+ graph->set_zoom(d["zoom"]);
+ }
+ if (d.has("snap")) {
+ graph->set_snap(d["snap"]);
+ }
+ if (d.has("snap_enabled")) {
+ graph->set_use_snap(d["snap_enabled"]);
+ }
+
+}
+
+
+void VisualScriptEditor::_center_on_node(int p_id) {
+
+ Node *n = graph->get_node(itos(p_id));
+ if (!n)
+ return;
+ GraphNode *gn = n->cast_to<GraphNode>();
+ if (gn) {
+ gn->set_selected(true);
+ Vector2 new_scroll = gn->get_offset() - graph->get_size()*0.5 + gn->get_size()*0.5;
+ graph->set_scroll_ofs( new_scroll );
+ script->set_function_scroll(edited_func,new_scroll/EDSCALE);
+ script->set_edited(true); //so it's saved
+
+ }
+}
+
+void VisualScriptEditor::goto_line(int p_line, bool p_with_error){
+
+ p_line+=1; //add one because script lines begin from 0.
+
+ if (p_with_error)
+ error_line=p_line;
+
+ List<StringName> functions;
+ script->get_function_list(&functions);
+ for (List<StringName>::Element *E=functions.front();E;E=E->next()) {
+
+ if (script->has_node(E->get(),p_line)) {
+
+ edited_func=E->get();
+ selected=edited_func;
+ _update_graph();
+ _update_members();
+
+ call_deferred("_center_on_node",p_line); //editor might be just created and size might not exist yet
+
+ return;
+ }
+ }
+}
+
+void VisualScriptEditor::trim_trailing_whitespace(){
+
+
+}
+
+void VisualScriptEditor::ensure_focus(){
+
+ graph->grab_focus();
+}
+
+void VisualScriptEditor::tag_saved_version(){
+
+
+}
+
+void VisualScriptEditor::reload(bool p_soft){
+
+
+}
+
+void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints){
+
+ List<StringName> functions;
+ script->get_function_list(&functions);
+ for (List<StringName>::Element *E=functions.front();E;E=E->next()) {
+
+ List<int> nodes;
+ script->get_node_list(E->get(),&nodes);
+ for (List<int>::Element *F=nodes.front();F;F=F->next()) {
+
+ Ref<VisualScriptNode> vsn = script->get_node(E->get(),F->get());
+ if (vsn->is_breakpoint()) {
+ p_breakpoints->push_back(F->get()-1); //subtract 1 because breakpoints in text start from zero
+ }
+ }
+ }
+}
+
+bool VisualScriptEditor::goto_method(const String& p_method){
+
+ if (!script->has_function(p_method))
+ return false;
+
+ edited_func=p_method;
+ selected=edited_func;
+ _update_members();
+ _update_graph();
+ return true;
+}
+
+void VisualScriptEditor::add_callback(const String& p_function,StringArray p_args){
+
+ if (script->has_function(p_function)) {
+ edited_func=p_function;
+ selected=edited_func;
+ _update_members();
+ _update_graph();
+ return;
+ }
+
+ Ref<VisualScriptFunction> func;
+ func.instance();
+ for(int i=0;i<p_args.size();i++) {
+
+ String name = p_args[i];
+ Variant::Type type=Variant::NIL;
+
+ if (name.find(":")!=-1) {
+ String tt = name.get_slice(":",1);
+ name=name.get_slice(":",0);
+ for(int j=0;j<Variant::VARIANT_MAX;j++) {
+
+ String tname = Variant::get_type_name(Variant::Type(j));
+ if (tname==tt) {
+ type=Variant::Type(j);
+ break;
+ }
+ }
+ }
+
+ func->add_argument(type,name);
+ }
+
+ func->set_name(p_function);
+ script->add_function(p_function);
+ script->add_node(p_function,script->get_available_id(),func);
+
+ edited_func=p_function;
+ selected=edited_func;
+ _update_members();
+ _update_graph();
+ graph->call_deferred("set_scroll_ofs",script->get_function_scroll(edited_func)); //for first time it might need to be later
+
+ //undo_redo->clear_history();
+
+}
+
+void VisualScriptEditor::update_settings(){
+
+ _update_graph();
+}
+
+void VisualScriptEditor::set_debugger_active(bool p_active) {
+ if (!p_active) {
+ error_line=-1;
+ _update_graph(); //clear line break
+ }
+}
+
+void VisualScriptEditor::set_tooltip_request_func(String p_method,Object* p_obj){
+
+
+}
+
+Control *VisualScriptEditor::get_edit_menu(){
+
+ return edit_menu;
+}
+
+void VisualScriptEditor::_change_base_type() {
+
+ select_base_type->popup(true);
+}
+
+void VisualScriptEditor::_change_base_type_callback() {
+
+ String bt = select_base_type->get_selected_type();
+
+ ERR_FAIL_COND(bt==String());
+ undo_redo->create_action("Change Base Type");
+ undo_redo->add_do_method(script.ptr(),"set_instance_base_type",bt);
+ undo_redo->add_undo_method(script.ptr(),"set_instance_base_type",script->get_instance_base_type());
+ undo_redo->add_do_method(this,"_update_members");
+ undo_redo->add_undo_method(this,"_update_members");
+ undo_redo->commit_action();
+
+}
+
+void VisualScriptEditor::_node_selected(Node* p_node) {
+
+ Ref<VisualScriptNode> vnode = p_node->get_meta("__vnode");
+ if (vnode.is_null())
+ return;
+
+ EditorNode::get_singleton()->push_item(vnode.ptr()); //edit node in inspector
+}
+
+static bool _get_out_slot(const Ref<VisualScriptNode>& p_node,int p_slot,int& r_real_slot,bool& r_sequence) {
+
+ if (p_slot<p_node->get_output_sequence_port_count()) {
+ r_sequence=true;
+ r_real_slot=p_slot;
+
+ return true;
+ }
+
+ r_real_slot=p_slot-p_node->get_output_sequence_port_count();
+ r_sequence=false;
+
+ return (r_real_slot<p_node->get_output_value_port_count());
+
+}
+
+static bool _get_in_slot(const Ref<VisualScriptNode>& p_node,int p_slot,int& r_real_slot,bool& r_sequence) {
+
+ if (p_slot==0 && p_node->has_input_sequence_port()) {
+ r_sequence=true;
+ r_real_slot=0;
+ return true;
+ }
+
+
+ r_real_slot=p_slot-(p_node->has_input_sequence_port()?1:0);
+ r_sequence=false;
+
+ return r_real_slot<p_node->get_input_value_port_count();
+
+}
+
+
+void VisualScriptEditor::_begin_node_move() {
+
+ undo_redo->create_action("Move Node(s)");
+}
+
+void VisualScriptEditor::_end_node_move() {
+
+ undo_redo->commit_action();
+}
+
+void VisualScriptEditor::_move_node(String func,int p_id,const Vector2& p_to) {
+
+
+
+ if (func==String(edited_func)) {
+ Node* node = graph->get_node(itos(p_id));
+ if (node && node->cast_to<GraphNode>())
+ node->cast_to<GraphNode>()->set_offset(p_to);
+ }
+ script->set_node_pos(edited_func,p_id,p_to/EDSCALE);
+}
+
+void VisualScriptEditor::_node_moved(Vector2 p_from,Vector2 p_to, int p_id) {
+
+ undo_redo->add_do_method(this,"_move_node",String(edited_func),p_id,p_to);
+ undo_redo->add_undo_method(this,"_move_node",String(edited_func),p_id,p_from);
+}
+
+void VisualScriptEditor::_remove_node(int p_id) {
+
+
+ undo_redo->create_action("Remove VisualScript Node");
+
+ undo_redo->add_do_method(script.ptr(),"remove_node",edited_func,p_id);
+ undo_redo->add_undo_method(script.ptr(),"add_node",edited_func,p_id,script->get_node(edited_func,p_id),script->get_node_pos(edited_func,p_id));
+
+
+ List<VisualScript::SequenceConnection> sequence_conns;
+ script->get_sequence_connection_list(edited_func,&sequence_conns);
+
+
+ for (List<VisualScript::SequenceConnection>::Element *E=sequence_conns.front();E;E=E->next()) {
+
+ if (E->get().from_node==p_id || E->get().to_node==p_id) {
+ undo_redo->add_undo_method(script.ptr(),"sequence_connect",edited_func,E->get().from_node,E->get().from_output,E->get().to_node);
+ }
+ }
+
+ List<VisualScript::DataConnection> data_conns;
+ script->get_data_connection_list(edited_func,&data_conns);
+
+ for (List<VisualScript::DataConnection>::Element *E=data_conns.front();E;E=E->next()) {
+
+ if (E->get().from_node==p_id || E->get().to_node==p_id) {
+ undo_redo->add_undo_method(script.ptr(),"data_connect",edited_func,E->get().from_node,E->get().from_port,E->get().to_node,E->get().to_port);
+ }
+ }
+
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+}
+
+
+void VisualScriptEditor::_node_ports_changed(const String& p_func,int p_id) {
+
+ if (p_func!=String(edited_func))
+ return;
+
+ _update_graph(p_id);
+}
+
+void VisualScriptEditor::_graph_connected(const String& p_from,int p_from_slot,const String& p_to,int p_to_slot) {
+
+ Ref<VisualScriptNode> from_node = script->get_node(edited_func,p_from.to_int());
+ ERR_FAIL_COND(!from_node.is_valid());
+
+ bool from_seq;
+ int from_port;
+
+ if (!_get_out_slot(from_node,p_from_slot,from_port,from_seq))
+ return; //can't connect this, it' s invalid
+
+ Ref<VisualScriptNode> to_node = script->get_node(edited_func,p_to.to_int());
+ ERR_FAIL_COND(!to_node.is_valid());
+
+ bool to_seq;
+ int to_port;
+
+ if (!_get_in_slot(to_node,p_to_slot,to_port,to_seq))
+ return; //can't connect this, it' s invalid
+
+
+ ERR_FAIL_COND(from_seq!=to_seq);
+
+
+ undo_redo->create_action("Connect Nodes");
+
+ if (from_seq) {
+ undo_redo->add_do_method(script.ptr(),"sequence_connect",edited_func,p_from.to_int(),from_port,p_to.to_int());
+ undo_redo->add_undo_method(script.ptr(),"sequence_disconnect",edited_func,p_from.to_int(),from_port,p_to.to_int());
+ } else {
+ undo_redo->add_do_method(script.ptr(),"data_connect",edited_func,p_from.to_int(),from_port,p_to.to_int(),to_port);
+ undo_redo->add_undo_method(script.ptr(),"data_disconnect",edited_func,p_from.to_int(),from_port,p_to.to_int(),to_port);
+ //update nodes in sgraph
+ undo_redo->add_do_method(this,"_update_graph",p_from.to_int());
+ undo_redo->add_do_method(this,"_update_graph",p_to.to_int());
+ undo_redo->add_undo_method(this,"_update_graph",p_from.to_int());
+ undo_redo->add_undo_method(this,"_update_graph",p_to.to_int());
+ }
+
+ undo_redo->add_do_method(this,"_update_graph_connections");
+ undo_redo->add_undo_method(this,"_update_graph_connections");
+
+ undo_redo->commit_action();
+
+}
+
+void VisualScriptEditor::_graph_disconnected(const String& p_from,int p_from_slot,const String& p_to,int p_to_slot){
+
+ Ref<VisualScriptNode> from_node = script->get_node(edited_func,p_from.to_int());
+ ERR_FAIL_COND(!from_node.is_valid());
+
+ bool from_seq;
+ int from_port;
+
+ if (!_get_out_slot(from_node,p_from_slot,from_port,from_seq))
+ return; //can't connect this, it' s invalid
+
+ Ref<VisualScriptNode> to_node = script->get_node(edited_func,p_to.to_int());
+ ERR_FAIL_COND(!to_node.is_valid());
+
+ bool to_seq;
+ int to_port;
+
+ if (!_get_in_slot(to_node,p_to_slot,to_port,to_seq))
+ return; //can't connect this, it' s invalid
+
+
+ ERR_FAIL_COND(from_seq!=to_seq);
+
+
+ undo_redo->create_action("Connect Nodes");
+
+ if (from_seq) {
+ undo_redo->add_do_method(script.ptr(),"sequence_disconnect",edited_func,p_from.to_int(),from_port,p_to.to_int());
+ undo_redo->add_undo_method(script.ptr(),"sequence_connect",edited_func,p_from.to_int(),from_port,p_to.to_int());
+ } else {
+ undo_redo->add_do_method(script.ptr(),"data_disconnect",edited_func,p_from.to_int(),from_port,p_to.to_int(),to_port);
+ undo_redo->add_undo_method(script.ptr(),"data_connect",edited_func,p_from.to_int(),from_port,p_to.to_int(),to_port);
+ //update nodes in sgraph
+ undo_redo->add_do_method(this,"_update_graph",p_from.to_int());
+ undo_redo->add_do_method(this,"_update_graph",p_to.to_int());
+ undo_redo->add_undo_method(this,"_update_graph",p_from.to_int());
+ undo_redo->add_undo_method(this,"_update_graph",p_to.to_int());
+ }
+ undo_redo->add_do_method(this,"_update_graph_connections");
+ undo_redo->add_undo_method(this,"_update_graph_connections");
+
+ undo_redo->commit_action();
+}
+
+
+
+void VisualScriptEditor::_graph_connect_to_empty(const String& p_from,int p_from_slot,const Vector2& p_release_pos) {
+
+ Node* node = graph->get_node(p_from);
+ if (!node)
+ return;
+
+ GraphNode *gn = node->cast_to<GraphNode>();
+ if (!gn)
+ return;
+
+ Ref<VisualScriptNode> vsn = script->get_node(edited_func,p_from.to_int());
+ if (!vsn.is_valid())
+ return;
+
+ if (p_from_slot<vsn->get_output_sequence_port_count()) {
+
+ port_action_popup->clear();
+ port_action_popup->add_item(TTR("Condition"),CREATE_COND);
+ port_action_popup->add_item(TTR("Sequence"),CREATE_SEQUENCE);
+ port_action_popup->add_item(TTR("Switch"),CREATE_SWITCH);
+ port_action_popup->add_item(TTR("Iterator"),CREATE_ITERATOR);
+ port_action_popup->add_item(TTR("While"),CREATE_WHILE);
+ port_action_popup->add_item(TTR("Return"),CREATE_RETURN);
+
+ port_action_node=p_from.to_int();
+ port_action_output=p_from_slot;
+
+ } else {
+ port_action_popup->clear();
+ port_action_popup->add_item(TTR("Call"),CREATE_CALL);
+ port_action_popup->add_item(TTR("Get"),CREATE_GET);
+ port_action_popup->add_item(TTR("Set"),CREATE_SET);
+
+
+ port_action_output=p_from_slot-vsn->get_output_sequence_port_count();
+ port_action_node=p_from.to_int();
+
+
+ }
+
+ port_action_pos=p_release_pos;
+ port_action_popup->set_size(Size2(1,1));
+ port_action_popup->set_pos(graph->get_global_pos()+p_release_pos);
+ port_action_popup->popup();
+}
+
+VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_node,int p_output,Set<int> &visited_nodes) {
+
+
+ VisualScriptNode::TypeGuess tg;
+ tg.type=Variant::NIL;
+
+ if (visited_nodes.has(p_node))
+ return tg; //no loop
+
+ visited_nodes.insert(p_node);
+
+ Ref<VisualScriptNode> node = script->get_node(edited_func,p_node);
+
+ if (!node.is_valid()) {
+
+ return tg;
+ }
+
+ Vector<VisualScriptNode::TypeGuess> in_guesses;
+
+ for(int i=0;i<node->get_input_value_port_count();i++) {
+ PropertyInfo pi = node->get_input_value_port_info(i);
+ VisualScriptNode::TypeGuess g;
+ g.type=pi.type;
+
+ if (g.type==Variant::NIL || g.type==Variant::OBJECT) {
+ //any or object input, must further guess what this is
+ int from_node;
+ int from_port;
+
+ if (script->get_input_value_port_connection_source(edited_func,p_node,i,&from_node,&from_port)) {
+
+ g = _guess_output_type(from_node,from_port,visited_nodes);
+ } else {
+ Variant defval = node->get_default_input_value(i);
+ if (defval.get_type()==Variant::OBJECT) {
+
+ Object *obj = defval;
+
+ if (obj) {
+
+ g.type=Variant::OBJECT;
+ g.obj_type=obj->get_type();
+ g.script=obj->get_script();
+ }
+ }
+ }
+
+ }
+
+ in_guesses.push_back(g);
+ }
+
+ return node->guess_output_type(in_guesses.ptr(),p_output);
+}
+
+void VisualScriptEditor::_port_action_menu(int p_option) {
+
+ Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap,snap));
+ }
+ ofs/=EDSCALE;
+
+ bool seq_connect=false;
+
+ Ref<VisualScriptNode> vnode;
+ Set<int> vn;
+
+ switch(p_option) {
+
+ case CREATE_CALL: {
+
+ Ref<VisualScriptFunctionCall> n;
+ n.instance();
+ vnode=n;
+
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn);
+
+ if (tg.type==Variant::OBJECT) {
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
+
+ if (tg.obj_type!=StringName()) {
+ n->set_base_type(tg.obj_type);
+ } else {
+ n->set_base_type("Object");
+ }
+
+ if (tg.script.is_valid()) {
+ n->set_base_script(tg.script->get_path());
+ new_connect_node_select->select_method_from_script(tg.script);
+ } else {
+ new_connect_node_select->select_method_from_base_type(n->get_base_type());
+ }
+
+
+ } else {
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
+ n->set_basic_type(tg.type);
+ new_connect_node_select->select_method_from_basic_type(tg.type);
+ }
+
+
+
+ } break;
+ case CREATE_SET: {
+
+ Ref<VisualScriptPropertySet> n;
+ n.instance();
+ vnode=n;
+
+
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn);
+
+ if (tg.type==Variant::OBJECT) {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+
+ if (tg.obj_type!=StringName()) {
+ n->set_base_type(tg.obj_type);
+ } else {
+ n->set_base_type("Object");
+ }
+
+ if (tg.script.is_valid()) {
+ n->set_base_script(tg.script->get_path());
+ new_connect_node_select->select_property_from_script(tg.script);
+ } else {
+ new_connect_node_select->select_property_from_base_type(n->get_base_type());
+ }
+
+
+ } else {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
+ n->set_basic_type(tg.type);
+ new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type);
+ }
+ } break;
+ case CREATE_GET: {
+
+ Ref<VisualScriptPropertyGet> n;
+ n.instance();
+ vnode=n;
+
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn);
+
+ if (tg.type==Variant::OBJECT) {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+
+ if (tg.obj_type!=StringName()) {
+ n->set_base_type(tg.obj_type);
+ } else {
+ n->set_base_type("Object");
+ }
+
+ if (tg.script.is_valid()) {
+ n->set_base_script(tg.script->get_path());
+ new_connect_node_select->select_property_from_script(tg.script);
+ } else {
+ new_connect_node_select->select_property_from_base_type(n->get_base_type());
+ }
+
+
+ } else {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
+ n->set_basic_type(tg.type);
+ new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type);
+ }
+
+ } break;
+ case CREATE_COND: {
+
+ Ref<VisualScriptCondition> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+ case CREATE_SEQUENCE: {
+
+ Ref<VisualScriptSequence> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+ case CREATE_SWITCH: {
+
+ Ref<VisualScriptSwitch> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+ case CREATE_ITERATOR: {
+
+ Ref<VisualScriptIterator> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+ case CREATE_WHILE: {
+
+ Ref<VisualScriptWhile> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+ case CREATE_RETURN: {
+
+ Ref<VisualScriptReturn> n;
+ n.instance();
+ vnode=n;
+ seq_connect=true;
+
+ } break;
+
+ }
+
+ int new_id = script->get_available_id();
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,vnode,ofs);
+ if (seq_connect) {
+ undo_redo->add_do_method(script.ptr(),"sequence_connect",edited_func,port_action_node,port_action_output,new_id);
+ }
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+ undo_redo->add_do_method(this,"_update_graph",new_id);
+ undo_redo->add_undo_method(this,"_update_graph",new_id);
+ undo_redo->commit_action();
+
+ port_action_new_node=new_id;
+
+}
+
+void VisualScriptEditor::_selected_connect_node_method_or_setget(const String& p_text) {
+
+ Ref<VisualScriptNode> vsn = script->get_node(edited_func,port_action_new_node);
+
+ if (vsn->cast_to<VisualScriptFunctionCall>()) {
+
+ Ref<VisualScriptFunctionCall> vsfc = vsn;
+ vsfc->set_function(p_text);
+ script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0);
+ }
+
+ if (vsn->cast_to<VisualScriptPropertySet>()) {
+
+ Ref<VisualScriptPropertySet> vsp = vsn;
+ vsp->set_property(p_text);
+ script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0);
+ }
+
+ if (vsn->cast_to<VisualScriptPropertyGet>()) {
+
+ Ref<VisualScriptPropertyGet> vsp = vsn;
+ vsp->set_property(p_text);
+ script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0);
+ }
+
+ _update_graph(port_action_new_node);
+ _update_graph_connections();
+
+}
+
+
+void VisualScriptEditor::_default_value_changed() {
+
+
+ Ref<VisualScriptNode> vsn = script->get_node(edited_func,editing_id);
+ if (vsn.is_null())
+ return;
+
+ undo_redo->create_action("Change Input Value");
+ undo_redo->add_do_method(vsn.ptr(),"set_default_input_value",editing_input,default_value_edit->get_variant());
+ undo_redo->add_undo_method(vsn.ptr(),"set_default_input_value",editing_input,vsn->get_default_input_value(editing_input));
+
+ undo_redo->add_do_method(this,"_update_graph",editing_id);
+ undo_redo->add_undo_method(this,"_update_graph",editing_id);
+ undo_redo->commit_action();
+
+}
+
+void VisualScriptEditor::_default_value_edited(Node * p_button,int p_id,int p_input_port) {
+
+ Ref<VisualScriptNode> vsn = script->get_node(edited_func,p_id);
+ if (vsn.is_null())
+ return;
+
+ PropertyInfo pinfo = vsn->get_input_value_port_info(p_input_port);
+ Variant existing = vsn->get_default_input_value(p_input_port);
+ if (pinfo.type!=Variant::NIL && existing.get_type()!=pinfo.type) {
+
+ Variant::CallError ce;
+ const Variant *existingp=&existing;
+ existing = Variant::construct(pinfo.type,&existingp,1,ce,false);
+
+ }
+
+ default_value_edit->set_pos(p_button->cast_to<Control>()->get_global_pos()+Vector2(0,p_button->cast_to<Control>()->get_size().y));
+ default_value_edit->set_size(Size2(1,1));
+ if (default_value_edit->edit(NULL,pinfo.name,pinfo.type,existing,pinfo.hint,pinfo.hint_string)) {
+ if (pinfo.hint==PROPERTY_HINT_MULTILINE_TEXT)
+ default_value_edit->popup_centered_ratio();
+ else
+ default_value_edit->popup();
+ }
+
+ editing_id = p_id;
+ editing_input=p_input_port;
+
+}
+
+void VisualScriptEditor::_show_hint(const String& p_hint) {
+
+ hint_text->set_text(p_hint);
+ hint_text->show();
+ hint_text_timer->start();
+}
+
+void VisualScriptEditor::_hide_timer() {
+
+ hint_text->hide();
+}
+
+void VisualScriptEditor::_node_filter_changed(const String& p_text) {
+
+ _update_available_nodes();
+}
+
+void VisualScriptEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_READY) {
+ node_filter_icon->set_texture(Control::get_icon("Zoom","EditorIcons"));
+ }
+}
+
+void VisualScriptEditor::_graph_ofs_changed(const Vector2& p_ofs) {
+
+ if (updating_graph)
+ return;
+
+ updating_graph=true;
+
+ if (script->has_function(edited_func)) {
+ script->set_function_scroll(edited_func,graph->get_scroll_ofs()/EDSCALE);
+ script->set_edited(true);
+ }
+ updating_graph=false;
+}
+
+void VisualScriptEditor::_comment_node_resized(const Vector2& p_new_size,int p_node) {
+
+ if (updating_graph)
+ return;
+
+ Ref<VisualScriptComment> vsc = script->get_node(edited_func,p_node);
+ if (vsc.is_null())
+ return;
+
+ Node *node = graph->get_node(itos(p_node));
+ if (!node)
+ return;
+ GraphNode *gn = node->cast_to<GraphNode>();
+ if (!gn)
+ return;
+
+ updating_graph=true;
+
+ graph->set_block_minimum_size_adjust(true); //faster resize
+
+ undo_redo->create_action("Resize Comment",UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(vsc.ptr(),"set_size",p_new_size/EDSCALE);
+ undo_redo->add_undo_method(vsc.ptr(),"set_size",vsc->get_size());
+ undo_redo->commit_action();
+
+ gn->set_custom_minimum_size(p_new_size); //for this time since graph update is blocked
+ gn->set_size(Size2(1,1));
+ graph->set_block_minimum_size_adjust(false);
+ updating_graph=false;
+
+
+}
+
+void VisualScriptEditor::_menu_option(int p_what) {
+
+ switch(p_what) {
+ case EDIT_DELETE_NODES: {
+ _on_nodes_delete();
+ } break;
+ case EDIT_TOGGLE_BREAKPOINT: {
+
+ List<String> reselect;
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ if (gn->is_selected()) {
+ int id = String(gn->get_name()).to_int();
+ Ref<VisualScriptNode> vsn = script->get_node(edited_func,id);
+ if (vsn.is_valid()) {
+ vsn->set_breakpoint(!vsn->is_breakpoint());
+ reselect.push_back(gn->get_name());
+ }
+ }
+ }
+ }
+
+ _update_graph();
+
+ for(List<String>::Element *E=reselect.front();E;E=E->next()) {
+ GraphNode *gn = graph->get_node(E->get())->cast_to<GraphNode>();
+ gn->set_selected(true);
+ }
+
+ } break;
+ case EDIT_FIND_NODE_TYPE: {
+ //popup disappearing grabs focus to owner, so use call deferred
+ node_filter->call_deferred("grab_focus");
+ node_filter->call_deferred("select_all");
+ } break;
+ case EDIT_COPY_NODES:
+ case EDIT_CUT_NODES: {
+
+ if (!script->has_function(edited_func))
+ break;
+
+ clipboard->nodes.clear();
+ clipboard->data_connections.clear();
+ clipboard->sequence_connections.clear();
+
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ if (gn->is_selected()) {
+
+ int id = String(gn->get_name()).to_int();
+ Ref<VisualScriptNode> node = script->get_node(edited_func,id);
+ if (node->cast_to<VisualScriptFunction>()) {
+ EditorNode::get_singleton()->show_warning("Can't copy the function node.");
+ return;
+ }
+ if (node.is_valid()) {
+ clipboard->nodes[id]=node->duplicate();
+ clipboard->nodes_positions[id]=script->get_node_pos(edited_func,id);
+ }
+
+ }
+ }
+ }
+
+ if (clipboard->nodes.empty())
+ break;
+
+ List<VisualScript::SequenceConnection> sequence_connections;
+
+ script->get_sequence_connection_list(edited_func,&sequence_connections);
+
+ for (List<VisualScript::SequenceConnection>::Element *E=sequence_connections.front();E;E=E->next()) {
+
+ if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
+
+ clipboard->sequence_connections.insert(E->get());
+ }
+ }
+
+ List<VisualScript::DataConnection> data_connections;
+
+ script->get_data_connection_list(edited_func,&data_connections);
+
+ for (List<VisualScript::DataConnection>::Element *E=data_connections.front();E;E=E->next()) {
+
+ if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
+
+ clipboard->data_connections.insert(E->get());
+ }
+ }
+
+ if (p_what==EDIT_CUT_NODES) {
+ _on_nodes_delete(); // oh yeah, also delete on cut
+ }
+
+
+ } break;
+ case EDIT_PASTE_NODES: {
+ if (!script->has_function(edited_func))
+ break;
+
+ if (clipboard->nodes.empty()) {
+ EditorNode::get_singleton()->show_warning("Clipboard is empty!");
+ break;
+ }
+
+ Map<int,int> remap;
+
+ undo_redo->create_action("Paste VisualScript Nodes");
+ int idc=script->get_available_id()+1;
+
+ Set<int> to_select;
+
+ Set<Vector2> existing_positions;
+
+ {
+ List<int> nodes;
+ script->get_node_list(edited_func,&nodes);
+ for (List<int>::Element *E=nodes.front();E;E=E->next()) {
+ Vector2 pos = script->get_node_pos(edited_func,E->get()).snapped(Vector2(2,2));
+ existing_positions.insert(pos);
+ }
+ }
+
+ for (Map<int,Ref<VisualScriptNode> >::Element *E=clipboard->nodes.front();E;E=E->next()) {
+
+
+ Ref<VisualScriptNode> node = E->get()->duplicate();
+
+ int new_id = idc++;
+ to_select.insert(new_id);
+
+ remap[E->key()]=new_id;
+
+ Vector2 paste_pos = clipboard->nodes_positions[E->key()];
+
+ while(existing_positions.has(paste_pos.snapped(Vector2(2,2)))) {
+ paste_pos+=Vector2(20,20)*EDSCALE;
+ }
+
+
+ undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,node,paste_pos);
+ undo_redo->add_undo_method(script.ptr(),"remove_node",edited_func,new_id);
+
+ }
+
+ for (Set<VisualScript::SequenceConnection>::Element *E=clipboard->sequence_connections.front();E;E=E->next()) {
+
+
+ undo_redo->add_do_method(script.ptr(),"sequence_connect",edited_func,remap[E->get().from_node],E->get().from_output,remap[E->get().to_node]);
+ undo_redo->add_undo_method(script.ptr(),"sequence_disconnect",edited_func,remap[E->get().from_node],E->get().from_output,remap[E->get().to_node]);
+
+ }
+
+ for (Set<VisualScript::DataConnection>::Element *E=clipboard->data_connections.front();E;E=E->next()) {
+
+
+ undo_redo->add_do_method(script.ptr(),"data_connect",edited_func,remap[E->get().from_node],E->get().from_port,remap[E->get().to_node],E->get().to_port);
+ undo_redo->add_undo_method(script.ptr(),"data_disconnect",edited_func,remap[E->get().from_node],E->get().from_port,remap[E->get().to_node],E->get().to_port);
+
+ }
+
+ undo_redo->add_do_method(this,"_update_graph");
+ undo_redo->add_undo_method(this,"_update_graph");
+
+ undo_redo->commit_action();
+
+ for(int i=0;i<graph->get_child_count();i++) {
+ GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>();
+ if (gn) {
+ int id = gn->get_name().operator String().to_int();
+ gn->set_selected(to_select.has(id));
+
+ }
+ }
+ } break;
+
+
+ }
+}
+
+void VisualScriptEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_member_button",&VisualScriptEditor::_member_button);
+ ObjectTypeDB::bind_method("_member_edited",&VisualScriptEditor::_member_edited);
+ ObjectTypeDB::bind_method("_member_selected",&VisualScriptEditor::_member_selected);
+ ObjectTypeDB::bind_method("_update_members",&VisualScriptEditor::_update_members);
+ ObjectTypeDB::bind_method("_change_base_type",&VisualScriptEditor::_change_base_type);
+ ObjectTypeDB::bind_method("_change_base_type_callback",&VisualScriptEditor::_change_base_type_callback);
+ ObjectTypeDB::bind_method("_override_pressed",&VisualScriptEditor::_override_pressed);
+ ObjectTypeDB::bind_method("_node_selected",&VisualScriptEditor::_node_selected);
+ ObjectTypeDB::bind_method("_node_moved",&VisualScriptEditor::_node_moved);
+ ObjectTypeDB::bind_method("_move_node",&VisualScriptEditor::_move_node);
+ ObjectTypeDB::bind_method("_begin_node_move",&VisualScriptEditor::_begin_node_move);
+ ObjectTypeDB::bind_method("_end_node_move",&VisualScriptEditor::_end_node_move);
+ ObjectTypeDB::bind_method("_remove_node",&VisualScriptEditor::_remove_node);
+ ObjectTypeDB::bind_method("_update_graph",&VisualScriptEditor::_update_graph,DEFVAL(-1));
+ ObjectTypeDB::bind_method("_node_ports_changed",&VisualScriptEditor::_node_ports_changed);
+ ObjectTypeDB::bind_method("_available_node_doubleclicked",&VisualScriptEditor::_available_node_doubleclicked);
+ ObjectTypeDB::bind_method("_default_value_edited",&VisualScriptEditor::_default_value_edited);
+ ObjectTypeDB::bind_method("_default_value_changed",&VisualScriptEditor::_default_value_changed);
+ ObjectTypeDB::bind_method("_menu_option",&VisualScriptEditor::_menu_option);
+ ObjectTypeDB::bind_method("_graph_ofs_changed",&VisualScriptEditor::_graph_ofs_changed);
+ ObjectTypeDB::bind_method("_center_on_node",&VisualScriptEditor::_center_on_node);
+ ObjectTypeDB::bind_method("_comment_node_resized",&VisualScriptEditor::_comment_node_resized);
+ ObjectTypeDB::bind_method("_button_resource_previewed",&VisualScriptEditor::_button_resource_previewed);
+ ObjectTypeDB::bind_method("_port_action_menu",&VisualScriptEditor::_port_action_menu);
+ ObjectTypeDB::bind_method("_selected_connect_node_method_or_setget",&VisualScriptEditor::_selected_connect_node_method_or_setget);
+
+
+
+
+ ObjectTypeDB::bind_method("get_drag_data_fw",&VisualScriptEditor::get_drag_data_fw);
+ ObjectTypeDB::bind_method("can_drop_data_fw",&VisualScriptEditor::can_drop_data_fw);
+ ObjectTypeDB::bind_method("drop_data_fw",&VisualScriptEditor::drop_data_fw);
+
+ ObjectTypeDB::bind_method("_input",&VisualScriptEditor::_input);
+ ObjectTypeDB::bind_method("_on_nodes_delete",&VisualScriptEditor::_on_nodes_delete);
+ ObjectTypeDB::bind_method("_on_nodes_duplicate",&VisualScriptEditor::_on_nodes_duplicate);
+
+ ObjectTypeDB::bind_method("_hide_timer",&VisualScriptEditor::_hide_timer);
+
+ ObjectTypeDB::bind_method("_graph_connected",&VisualScriptEditor::_graph_connected);
+ ObjectTypeDB::bind_method("_graph_disconnected",&VisualScriptEditor::_graph_disconnected);
+ ObjectTypeDB::bind_method("_graph_connect_to_empty",&VisualScriptEditor::_graph_connect_to_empty);
+
+ ObjectTypeDB::bind_method("_update_graph_connections",&VisualScriptEditor::_update_graph_connections);
+ ObjectTypeDB::bind_method("_node_filter_changed",&VisualScriptEditor::_node_filter_changed);
+
+ ObjectTypeDB::bind_method("_selected_method",&VisualScriptEditor::_selected_method);
+ ObjectTypeDB::bind_method("_draw_color_over_button",&VisualScriptEditor::_draw_color_over_button);
+
+
+
+
+}
+
+
+
+VisualScriptEditor::VisualScriptEditor() {
+
+ if (!clipboard) {
+ clipboard = memnew( Clipboard );
+ }
+ updating_graph=false;
+
+ edit_menu = memnew( MenuButton );
+ edit_menu->set_text(TTR("Edit"));
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES);
+
+ edit_menu->get_popup()->connect("item_pressed",this,"_menu_option");
+
+ main_hsplit = memnew( HSplitContainer );
+ add_child(main_hsplit);
+ main_hsplit->set_area_as_parent_rect();
+
+ left_vsplit = memnew( VSplitContainer );
+ main_hsplit->add_child(left_vsplit);
+
+ VBoxContainer *left_vb = memnew( VBoxContainer );
+ left_vsplit->add_child(left_vb);
+ left_vb->set_v_size_flags(SIZE_EXPAND_FILL);
+ left_vb->set_custom_minimum_size(Size2(230,1)*EDSCALE);
+
+ base_type_select = memnew( Button );
+ left_vb->add_margin_child(TTR("Base Type:"),base_type_select);
+ base_type_select->connect("pressed",this,"_change_base_type");
+
+ members = memnew( Tree );
+ left_vb->add_margin_child(TTR("Members:"),members,true);
+ members->set_hide_root(true);
+ members->connect("button_pressed",this,"_member_button");
+ members->connect("item_edited",this,"_member_edited");
+ members->connect("cell_selected",this,"_member_selected",varray(),CONNECT_DEFERRED);
+ members->set_single_select_cell_editing_only_when_already_selected(true);
+ members->set_hide_folding(true);
+ members->set_drag_forwarding(this);
+
+
+ VBoxContainer *left_vb2 = memnew( VBoxContainer );
+ left_vsplit->add_child(left_vb2);
+ left_vb2->set_v_size_flags(SIZE_EXPAND_FILL);
+
+
+ VBoxContainer *vbc_nodes = memnew( VBoxContainer );
+ HBoxContainer *hbc_nodes = memnew( HBoxContainer );
+ node_filter = memnew (LineEdit);
+ node_filter->connect("text_changed",this,"_node_filter_changed");
+ hbc_nodes->add_child(node_filter);
+ node_filter->set_h_size_flags(SIZE_EXPAND_FILL);
+ node_filter_icon = memnew( TextureFrame );
+ node_filter_icon->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED);
+ hbc_nodes->add_child(node_filter_icon);
+ vbc_nodes->add_child(hbc_nodes);
+
+ nodes = memnew( Tree );
+ vbc_nodes->add_child(nodes);
+ nodes->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ left_vb2->add_margin_child(TTR("Available Nodes:"),vbc_nodes,true);
+
+ nodes->set_hide_root(true);
+ nodes->connect("item_activated",this,"_available_node_doubleclicked");
+ nodes->set_drag_forwarding(this);
+
+ graph = memnew( GraphEdit );
+ main_hsplit->add_child(graph);
+ graph->set_h_size_flags(SIZE_EXPAND_FILL);
+ graph->connect("node_selected",this,"_node_selected");
+ graph->connect("_begin_node_move",this,"_begin_node_move");
+ graph->connect("_end_node_move",this,"_end_node_move");
+ graph->connect("delete_nodes_request",this,"_on_nodes_delete");
+ graph->connect("duplicate_nodes_request",this,"_on_nodes_duplicate");
+ graph->set_drag_forwarding(this);
+ graph->hide();
+ graph->connect("scroll_offset_changed",this,"_graph_ofs_changed");
+
+ select_func_text = memnew( Label );
+ select_func_text->set_text(TTR("Select or create a function to edit graph"));
+ select_func_text->set_align(Label::ALIGN_CENTER);
+ select_func_text->set_valign(Label::VALIGN_CENTER);
+ select_func_text->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_hsplit->add_child(select_func_text);
+
+
+ hint_text = memnew( Label );
+ hint_text->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,100);
+ hint_text->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0);
+ hint_text->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0);
+ hint_text->set_align(Label::ALIGN_CENTER);
+ hint_text->set_valign(Label::VALIGN_CENTER);
+ graph->add_child(hint_text);
+
+ hint_text_timer = memnew( Timer );
+ hint_text_timer->set_wait_time(4);
+ hint_text_timer->connect("timeout",this,"_hide_timer");
+ add_child(hint_text_timer);
+
+ //allowed casts (connections)
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ graph->add_valid_connection_type(Variant::NIL,i);
+ graph->add_valid_connection_type(i,Variant::NIL);
+ for(int j=0;j<Variant::VARIANT_MAX;j++) {
+ if (Variant::can_convert(Variant::Type(i),Variant::Type(j))) {
+ graph->add_valid_connection_type(i,j);
+ }
+ }
+
+ graph->add_valid_right_disconnect_type(i);
+ }
+
+ graph->add_valid_left_disconnect_type(TYPE_SEQUENCE);
+
+ graph->connect("connection_request",this,"_graph_connected");
+ graph->connect("disconnection_request",this,"_graph_disconnected");
+ graph->connect("connection_to_empty",this,"_graph_connect_to_empty");
+
+ edit_signal_dialog = memnew( AcceptDialog );
+ edit_signal_dialog->get_ok()->set_text(TTR("Close"));
+ add_child(edit_signal_dialog);
+ edit_signal_dialog->set_title(TTR("Edit Signal Arguments:"));
+
+ signal_editor = memnew( VisualScriptEditorSignalEdit );
+ edit_signal_edit = memnew( PropertyEditor );
+ edit_signal_edit->hide_top_label();
+ edit_signal_dialog->add_child(edit_signal_edit);
+ edit_signal_dialog->set_child_rect(edit_signal_edit);
+ edit_signal_edit->edit(signal_editor);
+
+ edit_variable_dialog = memnew( AcceptDialog );
+ edit_variable_dialog->get_ok()->set_text(TTR("Close"));
+ add_child(edit_variable_dialog);
+ edit_variable_dialog->set_title(TTR("Edit Variable:"));
+
+ variable_editor = memnew( VisualScriptEditorVariableEdit );
+ edit_variable_edit = memnew( PropertyEditor );
+ edit_variable_edit->hide_top_label();
+ edit_variable_dialog->add_child(edit_variable_edit);
+ edit_variable_dialog->set_child_rect(edit_variable_edit);
+ edit_variable_edit->edit(variable_editor);
+
+ select_base_type=memnew(CreateDialog);
+ select_base_type->set_base_type("Object"); //anything goes
+ select_base_type->connect("create",this,"_change_base_type_callback");
+ select_base_type->get_ok()->set_text(TTR("Change"));
+ add_child(select_base_type);
+
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ new_function_menu = memnew( PopupMenu );
+ new_function_menu->connect("item_pressed",this,"_override_pressed");
+ add_child(new_function_menu);
+ updating_members=false;
+
+ set_process_input(true); //for revert on drag
+ set_process_unhandled_input(true); //for revert on drag
+
+ default_value_edit= memnew( CustomPropertyEditor);
+ add_child(default_value_edit);
+ default_value_edit->connect("variant_changed",this,"_default_value_changed");
+
+ method_select = memnew( PropertySelector );
+ add_child(method_select);
+ method_select->connect("selected",this,"_selected_method");
+ error_line=-1;
+
+ new_connect_node_select = memnew( PropertySelector );
+ add_child(new_connect_node_select);
+ new_connect_node_select->connect("selected",this,"_selected_connect_node_method_or_setget");
+
+ port_action_popup = memnew( PopupMenu );
+ add_child(port_action_popup);
+ port_action_popup->connect("item_pressed",this,"_port_action_menu");
+
+
+}
+
+VisualScriptEditor::~VisualScriptEditor() {
+
+ undo_redo->clear_history(); //avoid crashes
+ memdelete(signal_editor);
+ memdelete(variable_editor);
+}
+
+static ScriptEditorBase * create_editor(const Ref<Script>& p_script) {
+
+ if (p_script->cast_to<VisualScript>()) {
+ return memnew( VisualScriptEditor );
+ }
+
+ return NULL;
+}
+
+
+VisualScriptEditor::Clipboard *VisualScriptEditor::clipboard=NULL;
+
+void VisualScriptEditor::free_clipboard() {
+ if (clipboard)
+ memdelete(clipboard);
+}
+
+static void register_editor_callback() {
+
+ ScriptEditor::register_create_script_editor_function(create_editor);
+ EditorSettings::get_singleton()->set("visual_script_editor/color_functions",Color(1,0.9,0.9));
+ EditorSettings::get_singleton()->set("visual_script_editor/color_data",Color(0.9,1.0,0.9));
+ EditorSettings::get_singleton()->set("visual_script_editor/color_operators",Color(0.9,0.9,1.0));
+ EditorSettings::get_singleton()->set("visual_script_editor/color_flow_control",Color(1.0,1.0,0.8));
+ EditorSettings::get_singleton()->set("visual_script_editor/color_custom",Color(0.8,1.0,1.0));
+ EditorSettings::get_singleton()->set("visual_script_editor/color_constants",Color(1.0,0.8,1.0));
+
+
+ ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"));
+ ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
+ ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD+KEY_F);
+ ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD+KEY_C);
+ ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD+KEY_X);
+ ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD+KEY_V);
+
+}
+
+void VisualScriptEditor::register_editor() {
+
+ //too early to register stuff here, request a callback
+ EditorNode::add_plugin_init_callback(register_editor_callback);
+
+
+
+}
+
+#endif
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
new file mode 100644
index 0000000000..5191ed540a
--- /dev/null
+++ b/modules/visual_script/visual_script_editor.h
@@ -0,0 +1,243 @@
+#ifndef VisualSCRIPT_EDITOR_H
+#define VisualSCRIPT_EDITOR_H
+
+#include "tools/editor/plugins/script_editor_plugin.h"
+#include "visual_script.h"
+#include "tools/editor/property_editor.h"
+#include "scene/gui/graph_edit.h"
+#include "tools/editor/create_dialog.h"
+#include "tools/editor/property_selector.h"
+class VisualScriptEditorSignalEdit;
+class VisualScriptEditorVariableEdit;
+
+
+#ifdef TOOLS_ENABLED
+
+
+class VisualScriptEditor : public ScriptEditorBase {
+ OBJ_TYPE(VisualScriptEditor,ScriptEditorBase)
+
+ enum {
+ TYPE_SEQUENCE=1000,
+ INDEX_BASE_SEQUENCE=1024
+
+
+ };
+
+ enum {
+ EDIT_DELETE_NODES,
+ EDIT_TOGGLE_BREAKPOINT,
+ EDIT_FIND_NODE_TYPE,
+ EDIT_COPY_NODES,
+ EDIT_CUT_NODES,
+ EDIT_PASTE_NODES,
+ };
+
+ enum PortAction {
+
+ CREATE_CALL,
+ CREATE_SET,
+ CREATE_GET,
+ CREATE_COND,
+ CREATE_SEQUENCE,
+ CREATE_SWITCH,
+ CREATE_ITERATOR,
+ CREATE_WHILE,
+ CREATE_RETURN,
+ };
+
+ MenuButton *edit_menu;
+
+ Ref<VisualScript> script;
+
+ Button *base_type_select;
+
+ HSplitContainer *main_hsplit;
+ VSplitContainer *left_vsplit;
+
+ GraphEdit *graph;
+
+ LineEdit *node_filter;
+ TextureFrame *node_filter_icon;
+
+ VisualScriptEditorSignalEdit *signal_editor;
+
+ AcceptDialog *edit_signal_dialog;
+ PropertyEditor *edit_signal_edit;
+
+ PropertySelector *method_select;
+ PropertySelector *new_connect_node_select;
+
+ VisualScriptEditorVariableEdit *variable_editor;
+
+ AcceptDialog *edit_variable_dialog;
+ PropertyEditor *edit_variable_edit;
+
+ CustomPropertyEditor *default_value_edit;
+
+ UndoRedo *undo_redo;
+
+ Tree *members;
+ Tree *nodes;
+
+ Label *hint_text;
+ Timer *hint_text_timer;
+
+ Label *select_func_text;
+
+ bool updating_graph;
+
+ void _show_hint(const String& p_hint);
+ void _hide_timer();
+
+ CreateDialog *select_base_type;
+
+ struct VirtualInMenu {
+ String name;
+ Variant::Type ret;
+ bool ret_variant;
+ Vector< Pair<Variant::Type,String> > args;
+ };
+
+ Map<int,VirtualInMenu> virtuals_in_menu;
+
+ PopupMenu *new_function_menu;
+
+
+ StringName edited_func;
+
+ void _update_graph_connections();
+ void _update_graph(int p_only_id=-1);
+
+ bool updating_members;
+
+ void _update_members();
+
+ StringName selected;
+
+ String _validate_name(const String& p_name) const;
+
+ struct Clipboard {
+
+ Map<int,Ref<VisualScriptNode> > nodes;
+ Map<int,Vector2 > nodes_positions;
+
+ Set<VisualScript::SequenceConnection> sequence_connections;
+ Set<VisualScript::DataConnection> data_connections;
+ };
+
+ static Clipboard *clipboard;
+
+ PopupMenu *port_action_popup;
+
+ PortAction port_action;
+ int port_action_node;
+ int port_action_output;
+ Vector2 port_action_pos;
+ int port_action_new_node;
+ void _port_action_menu(int p_option);
+ void _selected_connect_node_method_or_setget(const String& p_text);
+
+
+ int error_line;
+
+ void _node_selected(Node* p_node);
+ void _center_on_node(int p_id);
+
+ void _node_filter_changed(const String& p_text);
+ void _change_base_type_callback();
+ void _change_base_type();
+ void _member_selected();
+ void _member_edited();
+ void _override_pressed(int p_id);
+
+ void _begin_node_move();
+ void _end_node_move();
+ void _move_node(String func,int p_id,const Vector2& p_to);
+
+ void _node_moved(Vector2 p_from,Vector2 p_to, int p_id);
+ void _remove_node(int p_id);
+ void _graph_connected(const String& p_from,int p_from_slot,const String& p_to,int p_to_slot);
+ void _graph_disconnected(const String& p_from,int p_from_slot,const String& p_to,int p_to_slot);
+ void _graph_connect_to_empty(const String& p_from,int p_from_slot,const Vector2& p_release_pos);
+
+ void _node_ports_changed(const String& p_func,int p_id);
+ void _available_node_doubleclicked();
+
+ void _update_available_nodes();
+
+ void _member_button(Object *p_item, int p_column, int p_button);
+
+
+
+ String revert_on_drag;
+
+ void _input(const InputEvent& p_event);
+ void _on_nodes_delete();
+ void _on_nodes_duplicate();
+
+ Variant get_drag_data_fw(const Point2& p_point,Control* p_from);
+ bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const;
+ void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from);
+
+
+ int editing_id;
+ int editing_input;
+
+ void _default_value_changed();
+ void _default_value_edited(Node * p_button,int p_id,int p_input_port);
+
+ void _menu_option(int p_what);
+
+ void _graph_ofs_changed(const Vector2& p_ofs);
+ void _comment_node_resized(const Vector2& p_new_size,int p_node);
+
+ int selecting_method_id;
+ void _selected_method(const String& p_method);
+
+ void _draw_color_over_button(Object* obj,Color p_color);
+ void _button_resource_previewed(const String& p_path,const Ref<Texture>& p_preview,Variant p_ud);
+
+ VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node,int p_port_action_output,Set<int> &visited_nodes);
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ virtual void apply_code();
+ virtual Ref<Script> get_edited_script() const;
+ virtual Vector<String> get_functions();
+ virtual void set_edited_script(const Ref<Script>& p_script);
+ virtual void reload_text();
+ virtual String get_name();
+ virtual Ref<Texture> get_icon();
+ virtual bool is_unsaved();
+ virtual Variant get_edit_state();
+ virtual void set_edit_state(const Variant& p_state);
+ virtual void goto_line(int p_line,bool p_with_error=false);
+ virtual void trim_trailing_whitespace();
+ virtual void ensure_focus();
+ virtual void tag_saved_version();
+ virtual void reload(bool p_soft);
+ virtual void get_breakpoints(List<int> *p_breakpoints);
+ virtual bool goto_method(const String& p_method);
+ virtual void add_callback(const String& p_function,StringArray p_args);
+ virtual void update_settings();
+ virtual void set_debugger_active(bool p_active);
+ virtual void set_tooltip_request_func(String p_method,Object* p_obj);
+ virtual Control *get_edit_menu();
+ virtual bool can_lose_focus_on_node_selection() { return false; }
+
+ static void register_editor();
+
+ static void free_clipboard();
+
+ VisualScriptEditor();
+ ~VisualScriptEditor();
+};
+#endif
+
+#endif // VisualSCRIPT_EDITOR_H
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
new file mode 100644
index 0000000000..b53e81f23f
--- /dev/null
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -0,0 +1,1440 @@
+#include "visual_script_expression.h"
+
+
+bool VisualScriptExpression::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (String(p_name)=="expression") {
+ expression=p_value;
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+
+ if (String(p_name)=="out_type") {
+ output_type=Variant::Type(int(p_value));
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+ if (String(p_name)=="sequenced") {
+ sequenced=p_value;
+ ports_changed_notify();
+ return true;
+ }
+
+ if (String(p_name)=="input_count") {
+
+ int from=inputs.size();
+ inputs.resize(int(p_value));
+ for(int i=from;i<inputs.size();i++) {
+ inputs[i].name=String::chr('a'+i);
+ if (from==0) {
+ inputs[i].type=output_type;
+ } else {
+ inputs[i].type=inputs[from-1].type;
+ }
+ }
+ expression_dirty=true;
+ ports_changed_notify();
+ _change_notify();
+ return true;
+ }
+
+ if (String(p_name).begins_with("input/")) {
+
+ int idx=String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,inputs.size(),false);
+
+ String what=String(p_name).get_slice("/",2);
+
+ if (what=="type") {
+
+ inputs[idx].type=Variant::Type(int(p_value));
+ } else if (what=="name") {
+
+ inputs[idx].name=p_value;
+ } else {
+ return false;
+ }
+
+ expression_dirty=true;
+ ports_changed_notify();
+ return true;
+ }
+
+
+ return false;
+
+}
+
+bool VisualScriptExpression::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (String(p_name)=="expression") {
+ r_ret=expression;
+ return true;
+ }
+
+ if (String(p_name)=="out_type") {
+ r_ret=output_type;
+ return true;
+ }
+
+ if (String(p_name)=="sequenced") {
+ r_ret=sequenced;
+ return true;
+ }
+
+ if (String(p_name)=="input_count") {
+ r_ret=inputs.size();
+ return true;
+ }
+
+ if (String(p_name).begins_with("input/")) {
+
+ int idx=String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,inputs.size(),false);
+
+ String what=String(p_name).get_slice("/",2);
+
+ if (what=="type") {
+
+ r_ret=inputs[idx].type;
+ } else if (what=="name") {
+
+ r_ret=inputs[idx].name;
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ return false;
+}
+void VisualScriptExpression::_get_property_list( List<PropertyInfo> *p_list) const {
+
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING,"expression"));
+ p_list->push_back(PropertyInfo(Variant::INT,"out_type",PROPERTY_HINT_ENUM,argt));
+ p_list->push_back(PropertyInfo(Variant::INT,"input_count",PROPERTY_HINT_RANGE,"0,64,1"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,"sequenced"));
+
+ for(int i=0;i<inputs.size();i++) {
+
+ p_list->push_back(PropertyInfo(Variant::INT,"input/"+itos(i)+"/type",PROPERTY_HINT_ENUM,argt));
+ p_list->push_back(PropertyInfo(Variant::STRING,"input/"+itos(i)+"/name"));
+ }
+}
+
+int VisualScriptExpression::get_output_sequence_port_count() const {
+
+ return sequenced?1:0;
+}
+bool VisualScriptExpression::has_input_sequence_port() const{
+
+ return sequenced;
+}
+
+
+String VisualScriptExpression::get_output_sequence_port_text(int p_port) const{
+
+ return String();
+}
+
+
+int VisualScriptExpression::get_input_value_port_count() const{
+
+ return inputs.size();
+
+}
+int VisualScriptExpression::get_output_value_port_count() const{
+
+ return 1;
+}
+
+
+PropertyInfo VisualScriptExpression::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(inputs[p_idx].type,inputs[p_idx].name);
+}
+PropertyInfo VisualScriptExpression::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(output_type,"result");
+}
+
+String VisualScriptExpression::get_caption() const{
+
+ return "Expression";
+}
+String VisualScriptExpression::get_text() const{
+
+ return expression;
+}
+
+
+Error VisualScriptExpression::_get_token(Token& r_token) {
+
+ while (true) {
+#define GET_CHAR() (str_ofs>=expression.length()?0:expression[str_ofs++])
+
+ CharType cchar = GET_CHAR();
+ if (cchar==0) {
+ r_token.type=TK_EOF;
+ return OK;
+ }
+
+
+ switch(cchar) {
+
+ case 0: {
+ r_token.type=TK_EOF;
+ return OK;
+ } break;
+ case '{': {
+
+ r_token.type=TK_CURLY_BRACKET_OPEN;
+ return OK;
+ };
+ case '}': {
+
+ r_token.type=TK_CURLY_BRACKET_CLOSE;
+ return OK;
+ };
+ case '[': {
+
+ r_token.type=TK_BRACKET_OPEN;
+ return OK;
+ };
+ case ']': {
+
+ r_token.type=TK_BRACKET_CLOSE;
+ return OK;
+ };
+ case '(': {
+
+ r_token.type=TK_PARENTHESIS_OPEN;
+ return OK;
+ };
+ case ')': {
+
+ r_token.type=TK_PARENTHESIS_CLOSE;
+ return OK;
+ };
+ case ',': {
+
+ r_token.type=TK_COMMA;
+ return OK;
+ };
+ case ':': {
+
+ r_token.type=TK_COLON;
+ return OK;
+ };
+ case '.': {
+
+ r_token.type=TK_PERIOD;
+ return OK;
+ };
+ case '=': {
+
+ cchar=GET_CHAR();
+ if (cchar=='=') {
+ r_token.type=TK_OP_EQUAL;
+ } else {
+ _set_error("Expected '='");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ return OK;
+ };
+ case '!': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_NOT_EQUAL;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_NOT;
+ }
+ return OK;
+ };
+ case '>': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_GREATER_EQUAL;
+ str_ofs++;
+ } else if (expression[str_ofs]=='>') {
+ r_token.type=TK_OP_SHIFT_RIGHT;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_GREATER;
+ }
+ return OK;
+ };
+ case '<': {
+
+ if (expression[str_ofs]=='=') {
+ r_token.type=TK_OP_LESS_EQUAL;
+ str_ofs++;
+ } else if (expression[str_ofs]=='<') {
+ r_token.type=TK_OP_SHIFT_LEFT;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_LESS;
+ }
+ return OK;
+ };
+ case '+': {
+ r_token.type=TK_OP_ADD;
+ return OK;
+ };
+ case '-': {
+ r_token.type=TK_OP_SUB;
+ return OK;
+ };
+ case '/': {
+ r_token.type=TK_OP_DIV;
+ return OK;
+ };
+ case '*': {
+ r_token.type=TK_OP_MUL;
+ return OK;
+ };
+ case '%': {
+ r_token.type=TK_OP_MOD;
+ return OK;
+ };
+ case '&': {
+
+ if (expression[str_ofs]=='&') {
+ r_token.type=TK_OP_AND;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_BIT_AND;
+ }
+ return OK;
+ };
+ case '|': {
+
+ if (expression[str_ofs]=='|') {
+ r_token.type=TK_OP_OR;
+ str_ofs++;
+ } else {
+ r_token.type=TK_OP_BIT_OR;
+ }
+ return OK;
+ };
+ case '^': {
+
+ r_token.type=TK_OP_BIT_XOR;
+
+ return OK;
+ };
+ case '~': {
+
+ r_token.type=TK_OP_BIT_INVERT;
+
+ return OK;
+ };
+ case '"': {
+
+
+ String str;
+ while(true) {
+
+ CharType ch=GET_CHAR();
+
+ if (ch==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else if (ch=='"') {
+ break;
+ } else if (ch=='\\') {
+ //escaped characters...
+
+ CharType next = GET_CHAR();
+ if (next==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ CharType res=0;
+
+ switch(next) {
+
+ case 'b': res=8; break;
+ case 't': res=9; break;
+ case 'n': res=10; break;
+ case 'f': res=12; break;
+ case 'r': res=13; break;
+ case 'u': {
+ //hexnumbarh - oct is deprecated
+
+
+ for(int j=0;j<4;j++) {
+ CharType c = GET_CHAR();
+
+ if (c==0) {
+ _set_error("Unterminated String");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) {
+
+ _set_error("Malformed hex constant in string");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ CharType v;
+ if (c>='0' && c<='9') {
+ v=c-'0';
+ } else if (c>='a' && c<='f') {
+ v=c-'a';
+ v+=10;
+ } else if (c>='A' && c<='F') {
+ v=c-'A';
+ v+=10;
+ } else {
+ ERR_PRINT("BUG");
+ v=0;
+ }
+
+ res<<=4;
+ res|=v;
+
+
+ }
+
+
+
+ } break;
+ //case '\"': res='\"'; break;
+ //case '\\': res='\\'; break;
+ //case '/': res='/'; break;
+ default: {
+ res = next;
+ //r_err_str="Invalid escape sequence";
+ //return ERR_PARSE_ERROR;
+ } break;
+ }
+
+ str+=res;
+
+ } else {
+ str+=ch;
+ }
+ }
+
+ r_token.type=TK_CONSTANT;
+ r_token.value=str;
+ return OK;
+
+ } break;
+ default: {
+
+ if (cchar<=32) {
+ break;
+ }
+
+ if (cchar=='-' || (cchar>='0' && cchar<='9')) {
+ //a number
+
+
+ String num;
+#define READING_SIGN 0
+#define READING_INT 1
+#define READING_DEC 2
+#define READING_EXP 3
+#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;
+ bool is_float=false;
+
+ while(true) {
+
+ switch(reading) {
+ case READING_INT: {
+
+ if (c>='0' && c<='9') {
+ //pass
+ } else if (c=='.') {
+ reading=READING_DEC;
+ is_float=true;
+ } else if (c=='e') {
+ reading=READING_EXP;
+ } else {
+ reading=READING_DONE;
+ }
+
+ } break;
+ case READING_DEC: {
+
+ if (c>='0' && c<='9') {
+
+ } else if (c=='e') {
+ reading=READING_EXP;
+
+ } else {
+ reading=READING_DONE;
+ }
+
+ } break;
+ case READING_EXP: {
+
+ if (c>='0' && c<='9') {
+ exp_beg=true;
+
+ } else if ((c=='-' || c=='+') && !exp_sign && !exp_beg) {
+ if (c=='-')
+ is_float=true;
+ exp_sign=true;
+
+ } else {
+ reading=READING_DONE;
+ }
+ } break;
+ }
+
+ if (reading==READING_DONE)
+ break;
+ num+=String::chr(c);
+ c = GET_CHAR();
+
+
+ }
+
+ str_ofs--;
+
+ r_token.type=TK_CONSTANT;
+
+ if (is_float)
+ r_token.value=num.to_double();
+ else
+ r_token.value=num.to_int();
+ return OK;
+
+ } else if ((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_') {
+
+ String id;
+ bool first=true;
+
+ while((cchar>='A' && cchar<='Z') || (cchar>='a' && cchar<='z') || cchar=='_' || (!first && cchar>='0' && cchar<='9')) {
+
+ id+=String::chr(cchar);
+ cchar=GET_CHAR();
+ first=false;
+ }
+
+ str_ofs--; //go back one
+
+ if (id=="in") {
+ r_token.type=TK_OP_IN;
+ } else if (id=="null") {
+ r_token.type=TK_CONSTANT;
+ r_token.value=Variant();
+ } else if (id=="true") {
+ r_token.type=TK_CONSTANT;
+ r_token.value=true;
+ } else if (id=="false") {
+ r_token.type=TK_CONSTANT;
+ r_token.value=false;
+ } else if (id=="PI") {
+ r_token.type=TK_CONSTANT;
+ r_token.value=Math_PI;
+ } else if (id=="not") {
+ r_token.type=TK_OP_NOT;
+ } else if (id=="or") {
+ r_token.type=TK_OP_OR;
+ } else if (id=="and") {
+ r_token.type=TK_OP_AND;
+ } else if (id=="self") {
+ r_token.type=TK_SELF;
+ } else {
+
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ if (id==Variant::get_type_name(Variant::Type(i))) {
+ r_token.type=TK_BASIC_TYPE;
+ r_token.value=i;
+ return OK;
+ break;
+ }
+ }
+
+ r_token.type=TK_IDENTIFIER;
+ r_token.value=id;
+ }
+
+ return OK;
+ } else {
+ _set_error("Unexpected character.");
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ }
+ }
+ }
+
+ r_token.type=TK_ERROR;
+ return ERR_PARSE_ERROR;
+}
+
+const char* VisualScriptExpression::token_name[TK_MAX]={
+"CURLY BRACKET OPEN",
+"CURLY BRACKET CLOSE",
+"BRACKET OPEN",
+"BRACKET CLOSE",
+"PARENTHESIS OPEN",
+"PARENTHESIS CLOSE",
+"IDENTIFIER",
+"SELF",
+"CONSTANT",
+"BASIC TYPE",
+"COLON",
+"COMMA",
+"PERIOD",
+"OP IN",
+"OP EQUAL",
+"OP NOT EQUAL",
+"OP LESS",
+"OP LESS EQUAL",
+"OP GREATER",
+"OP GREATER EQUAL",
+"OP AND",
+"OP OR",
+"OP NOT",
+"OP ADD",
+"OP SUB",
+"OP MUL",
+"OP DIV",
+"OP MOD",
+"OP SHIFT LEFT",
+"OP SHIFT RIGHT",
+"OP BIT AND",
+"OP BIT OR",
+"OP BIT XOR",
+"OP BIT INVERT",
+"EOF",
+"ERROR"
+};
+
+VisualScriptExpression::ENode* VisualScriptExpression::_parse_expression() {
+
+
+ Vector<Expression> expression;
+
+ while(true) {
+ //keep appending stuff to expression
+ ENode*expr=NULL;
+
+ Token tk;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+
+
+ switch(tk.type) {
+ case TK_CURLY_BRACKET_OPEN: {
+ //a dictionary
+ DictionaryNode *dn = alloc_node<DictionaryNode>();
+
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_CURLY_BRACKET_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+ dn->dict.push_back(expr);
+
+ _get_token(tk);
+ if (tk.type!=TK_COLON) {
+ _set_error("Expected ':'");
+ return NULL;
+ }
+
+ expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ dn->dict.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_CURLY_BRACKET_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or '}'");
+ }
+ }
+
+ expr=dn;
+ } break;
+ case TK_BRACKET_OPEN: {
+ //an array
+
+ ArrayNode *an = alloc_node<ArrayNode>();
+
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_BRACKET_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+ an->array.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_BRACKET_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ']'");
+ }
+ }
+
+ expr=an;
+ } break;
+ case TK_PARENTHESIS_OPEN: {
+ //a suexpression
+ ENode* e=_parse_expression();
+ if (error_set)
+ return NULL;
+ _get_token(tk);
+ if (tk.type!=TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')'");
+ return NULL;
+ }
+
+ expr=e;
+
+ } break;
+ case TK_IDENTIFIER: {
+
+ String what = tk.value;
+ int index=-1;
+ for(int i=0;i<inputs.size();i++) {
+ if (what==inputs[i].name) {
+ index=i;
+ break;
+ }
+ }
+
+ if (index!=-1) {
+ InputNode *input = alloc_node<InputNode>();
+ input->index=index;
+ expr=input;
+ } else {
+ _set_error("Invalid input identifier '"+what+"'. For script variables, use self (locals are for inputs)."+what);
+ return NULL;
+ }
+ } break;
+ case TK_SELF: {
+
+ SelfNode *self = alloc_node<SelfNode>();
+ expr=self;
+ } break;
+ case TK_CONSTANT: {
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value=tk.value;
+ expr=constant;
+ } break;
+ case TK_BASIC_TYPE: {
+ //constructor..
+
+ Variant::Type bt = Variant::Type(int(tk.value));
+ _get_token(tk);
+ if (tk.type!=TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '('");
+ return NULL;
+ }
+
+ ConstructorNode *constructor = alloc_node<ConstructorNode>();
+ constructor->data_type=bt;
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ constructor->arguments.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_PARENTHESIS_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ')'");
+ }
+ }
+
+ expr=constructor;
+
+ } break;
+ case TK_OP_SUB: {
+
+ Expression e;
+ e.is_op=true;
+ e.op=Variant::OP_NEGATE;
+ expression.push_back(e);
+ continue;
+ } break;
+ case TK_OP_NOT: {
+
+ Expression e;
+ e.is_op=true;
+ e.op=Variant::OP_NOT;
+ expression.push_back(e);
+ continue;
+ } break;
+
+ default: {
+ _set_error("Expected expression.");
+ return NULL;
+ } break;
+
+ }
+
+ //before going to operators, must check indexing!
+
+ while(true) {
+ int cofs2=str_ofs;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+ bool done=false;
+
+ switch(tk.type) {
+ case TK_BRACKET_OPEN: {
+ //value indexing
+
+ IndexNode *index = alloc_node<IndexNode>();
+ index->base=expr;
+
+ ENode* what=_parse_expression();
+ if (!what)
+ return NULL;
+
+ index->index=what;
+
+ _get_token(tk);
+ if (tk.type!=TK_BRACKET_CLOSE) {
+ _set_error("Expected ']' at end of index.");
+ return NULL;
+ }
+ expr=index;
+
+ } break;
+ case TK_PERIOD: {
+ //named indexing or function call
+ _get_token(tk);
+ if (tk.type!=TK_IDENTIFIER) {
+ _set_error("Expected identifier after '.'");
+ return NULL;
+ }
+
+ StringName identifier=tk.value;
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_OPEN) {
+ //function call
+ CallNode *func_call = alloc_node<CallNode>();
+ func_call->method=identifier;
+ func_call->base=expr;
+
+ while(true) {
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_PARENTHESIS_CLOSE) {
+ break;
+ }
+ str_ofs=cofs; //revert
+ //parse an expression
+ ENode* expr=_parse_expression();
+ if (!expr)
+ return NULL;
+
+ func_call->arguments.push_back(expr);
+
+ cofs=str_ofs;
+ _get_token(tk);
+ if (tk.type==TK_COMMA) {
+ //all good
+ } else if (tk.type==TK_PARENTHESIS_CLOSE) {
+ str_ofs=cofs;
+ } else {
+ _set_error("Expected ',' or ')'");
+ }
+ }
+
+ expr=func_call;
+ } else {
+ //named indexing
+ str_ofs=cofs;
+
+ NamedIndexNode *index = alloc_node<NamedIndexNode>();
+ index->base=expr;
+ index->name=identifier;
+ expr=index;
+
+ }
+
+ } break;
+ default: {
+ str_ofs=cofs2;
+ done=true;
+ } break;
+ }
+
+ if (done)
+ break;
+ }
+
+ //push expression
+ {
+ Expression e;
+ e.is_op=false;
+ e.node=expr;
+ expression.push_back(e);
+ }
+
+ //ok finally look for an operator
+
+
+ int cofs=str_ofs;
+ _get_token(tk);
+ if (error_set)
+ return NULL;
+
+
+ Variant::Operator op = Variant::OP_MAX;
+
+ switch(tk.type) {
+ case TK_OP_IN: op=Variant::OP_IN; break;
+ case TK_OP_EQUAL: op=Variant::OP_EQUAL; break;
+ case TK_OP_NOT_EQUAL: op=Variant::OP_NOT_EQUAL; break;
+ case TK_OP_LESS: op=Variant::OP_LESS; break;
+ case TK_OP_LESS_EQUAL: op=Variant::OP_LESS_EQUAL; break;
+ case TK_OP_GREATER: op=Variant::OP_GREATER; break;
+ case TK_OP_GREATER_EQUAL: op=Variant::OP_GREATER_EQUAL; break;
+ case TK_OP_AND: op=Variant::OP_AND; break;
+ case TK_OP_OR: op=Variant::OP_OR; break;
+ case TK_OP_NOT: op=Variant::OP_NOT; break;
+ case TK_OP_ADD: op=Variant::OP_ADD; break;
+ case TK_OP_SUB: op=Variant::OP_SUBSTRACT; break;
+ case TK_OP_MUL: op=Variant::OP_MULTIPLY; break;
+ case TK_OP_DIV: op=Variant::OP_DIVIDE; break;
+ case TK_OP_MOD: op=Variant::OP_MODULE; break;
+ case TK_OP_SHIFT_LEFT: op=Variant::OP_SHIFT_LEFT; break;
+ case TK_OP_SHIFT_RIGHT: op=Variant::OP_SHIFT_RIGHT; break;
+ case TK_OP_BIT_AND: op=Variant::OP_BIT_AND; break;
+ case TK_OP_BIT_OR: op=Variant::OP_BIT_OR; break;
+ case TK_OP_BIT_XOR: op=Variant::OP_BIT_XOR; break;
+ case TK_OP_BIT_INVERT: op=Variant::OP_BIT_NEGATE; break;
+ default: {};
+ }
+
+ if (op==Variant::OP_MAX) { //stop appending stuff
+ str_ofs=cofs;
+ break;
+ }
+
+ //push operator and go on
+ {
+ Expression e;
+ e.is_op=true;
+ e.op=op;
+ expression.push_back(e);
+ }
+ }
+
+
+ /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
+
+
+ while(expression.size()>1) {
+
+ int next_op=-1;
+ int min_priority=0xFFFFF;
+ bool is_unary=false;
+
+ for(int i=0;i<expression.size();i++) {
+
+
+
+ if (!expression[i].is_op) {
+
+ continue;
+ }
+
+ int priority;
+
+ bool unary=false;
+
+ switch(expression[i].op) {
+
+
+ case Variant::OP_BIT_NEGATE: priority=0; unary=true; break;
+ case Variant::OP_NEGATE: priority=1; unary=true; break;
+
+ case Variant::OP_MULTIPLY: priority=2; break;
+ case Variant::OP_DIVIDE: priority=2; break;
+ case Variant::OP_MODULE: priority=2; break;
+
+ case Variant::OP_ADD: priority=3; break;
+ case Variant::OP_SUBSTRACT: priority=3; break;
+
+ case Variant::OP_SHIFT_LEFT: priority=4; break;
+ case Variant::OP_SHIFT_RIGHT: priority=4; break;
+
+ case Variant::OP_BIT_AND: priority=5; break;
+ case Variant::OP_BIT_XOR: priority=6; break;
+ case Variant::OP_BIT_OR: priority=7; break;
+
+ case Variant::OP_LESS: priority=8; break;
+ case Variant::OP_LESS_EQUAL: priority=8; break;
+ case Variant::OP_GREATER: priority=8; break;
+ case Variant::OP_GREATER_EQUAL: priority=8; break;
+
+ case Variant::OP_EQUAL: priority=8; break;
+ case Variant::OP_NOT_EQUAL: priority=8; break;
+
+ case Variant::OP_IN: priority=10; break;
+
+ case Variant::OP_NOT: priority=11; unary=true; break;
+ case Variant::OP_AND: priority=12; break;
+ case Variant::OP_OR: priority=13; break;
+
+
+ default: {
+ _set_error("Parser bug, invalid operator in expression: "+itos(expression[i].op));
+ return NULL;
+ }
+
+ }
+
+ if (priority<min_priority) {
+ // < is used for left to right (default)
+ // <= is used for right to left
+
+ next_op=i;
+ min_priority=priority;
+ is_unary=unary;
+ }
+
+ }
+
+ if (next_op==-1) {
+
+
+ _set_error("Yet another parser bug....");
+ ERR_FAIL_COND_V(next_op==-1,NULL);
+ }
+
+
+ // OK! create operator..
+ if (is_unary) {
+
+ int expr_pos=next_op;
+ while(expression[expr_pos].is_op) {
+
+ expr_pos++;
+ if (expr_pos==expression.size()) {
+ //can happen..
+ _set_error("Unexpected end of expression..");
+ return NULL;
+ }
+ }
+
+ //consecutively do unary opeators
+ for(int i=expr_pos-1;i>=next_op;i--) {
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[i].op;
+ op->nodes[0]=expression[i+1].node;
+ op->nodes[1]=NULL;
+ expression[i].is_op=false;
+ expression[i].node=op;
+ expression.remove(i+1);
+ }
+
+
+ } else {
+
+ if (next_op <1 || next_op>=(expression.size()-1)) {
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators.");
+ return NULL;
+ }
+
+
+ op->nodes[0]=expression[next_op-1].node; //expression goes as left
+ op->nodes[1]=expression[next_op+1].node; //next expression goes as right
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ }
+ }
+
+ return expression[0].node;
+}
+
+bool VisualScriptExpression::_compile_expression() {
+
+ if (!expression_dirty)
+ return error_set;
+
+ if (nodes) {
+ memdelete(nodes);
+ nodes=NULL;
+ root=NULL;
+
+ }
+
+ error_str=String();
+ error_set=false;
+ str_ofs=0;
+
+ root=_parse_expression();
+
+ if (error_set) {
+ root=NULL;
+ if (nodes) {
+ memdelete(nodes);
+ }
+ nodes=NULL;
+ return true;
+ }
+
+ expression_dirty=false;
+ return false;
+}
+
+
+class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ VisualScriptExpression *expression;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //execute by parsing the tree directly
+ virtual bool _execute(const Variant** p_inputs,VisualScriptExpression::ENode *p_node,Variant& r_ret,String& r_error_str,Variant::CallError &ce) {
+
+ switch(p_node->type) {
+ case VisualScriptExpression::ENode::TYPE_INPUT: {
+
+ const VisualScriptExpression::InputNode *in = static_cast<const VisualScriptExpression::InputNode*>(p_node);
+ r_ret=*p_inputs[in->index];
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CONSTANT: {
+
+ const VisualScriptExpression::ConstantNode *c = static_cast<const VisualScriptExpression::ConstantNode*>(p_node);
+ r_ret=c->value;
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_SELF: {
+
+ r_ret=instance->get_owner_ptr();
+ } break;
+ case VisualScriptExpression::ENode::TYPE_OPERATOR: {
+
+
+ const VisualScriptExpression::OperatorNode *op = static_cast<const VisualScriptExpression::OperatorNode*>(p_node);
+
+ Variant a;
+ bool ret = _execute(p_inputs,op->nodes[0],a,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant b;
+
+ if (op->nodes[1]) {
+ ret = _execute(p_inputs,op->nodes[1],b,r_error_str,ce);
+ if (ret)
+ return true;
+ }
+
+ bool valid=true;
+ Variant::evaluate(op->op,a,b,r_ret,valid);
+ if (!valid) {
+ r_error_str="Invalid operands to operator "+Variant::get_operator_name(op->op)+": "+Variant::get_type_name(a.get_type())+" and "+Variant::get_type_name(b.get_type())+".";
+ return true;
+ }
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_INDEX: {
+
+ const VisualScriptExpression::IndexNode *index = static_cast<const VisualScriptExpression::IndexNode*>(p_node);
+
+ Variant base;
+ bool ret = _execute(p_inputs,index->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant idx;
+
+ ret = _execute(p_inputs,index->index,idx,r_error_str,ce);
+ if (ret)
+ return true;
+
+ bool valid;
+ r_ret=base.get(idx,&valid);
+ if (!valid) {
+ r_error_str="Invalid index of type "+Variant::get_type_name(idx.get_type())+" for base of type "+Variant::get_type_name(base.get_type())+".";
+ return true;
+ }
+
+
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_NAMED_INDEX: {
+
+ const VisualScriptExpression::NamedIndexNode *index = static_cast<const VisualScriptExpression::NamedIndexNode*>(p_node);
+
+ Variant base;
+ bool ret = _execute(p_inputs,index->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ bool valid;
+ r_ret=base.get_named(index->name,&valid);
+ if (!valid) {
+ r_error_str="Invalid index '"+String(index->name)+"' for base of type "+Variant::get_type_name(base.get_type())+".";
+ return true;
+ }
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_ARRAY: {
+ const VisualScriptExpression::ArrayNode *array = static_cast<const VisualScriptExpression::ArrayNode*>(p_node);
+
+ Array arr;
+ arr.resize(array->array.size());
+ for (int i=0;i<array->array.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,array->array[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ }
+
+ r_ret=arr;
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_DICTIONARY: {
+ const VisualScriptExpression::DictionaryNode *dictionary = static_cast<const VisualScriptExpression::DictionaryNode*>(p_node);
+
+ Dictionary d;
+ for (int i=0;i<dictionary->dict.size();i+=2) {
+
+ Variant key;
+ bool ret = _execute(p_inputs,dictionary->dict[i+0],key,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Variant value;
+ ret = _execute(p_inputs,dictionary->dict[i+1],value,r_error_str,ce);
+ if (ret)
+ return true;
+
+ d[key]=value;
+ }
+
+ r_ret=d;
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CONSTRUCTOR: {
+
+ const VisualScriptExpression::ConstructorNode *constructor = static_cast<const VisualScriptExpression::ConstructorNode*>(p_node);
+
+ Vector<Variant> arr;
+ Vector<const Variant*> argp;
+ arr.resize(constructor->arguments.size());
+ argp.resize(constructor->arguments.size());
+
+ for (int i=0;i<constructor->arguments.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,constructor->arguments[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ argp[i]=&arr[i];
+ }
+
+
+ r_ret=Variant::construct(constructor->data_type,argp.ptr(),argp.size(),ce);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="Invalid arguments to construct '"+Variant::get_type_name(constructor->data_type)+"'.";
+ return true;
+ }
+
+
+ } break;
+ case VisualScriptExpression::ENode::TYPE_CALL: {
+
+ const VisualScriptExpression::CallNode *call = static_cast<const VisualScriptExpression::CallNode*>(p_node);
+
+
+ Variant base;
+ bool ret = _execute(p_inputs,call->base,base,r_error_str,ce);
+ if (ret)
+ return true;
+
+ Vector<Variant> arr;
+ Vector<const Variant*> argp;
+ arr.resize(call->arguments.size());
+ argp.resize(call->arguments.size());
+
+ for (int i=0;i<call->arguments.size();i++) {
+
+ Variant value;
+ bool ret = _execute(p_inputs,call->arguments[i],value,r_error_str,ce);
+ if (ret)
+ return true;
+ arr[i]=value;
+ argp[i]=&arr[i];
+ }
+
+
+ r_ret=base.call(call->method,argp.ptr(),argp.size(),ce);
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="On call to '"+String(call->method)+"':";
+ return true;
+ }
+
+ } break;
+ }
+ return false;
+ }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (!expression->root || expression->error_set) {
+ r_error_str=expression->error_str;
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+
+ bool error = _execute(p_inputs,expression->root,*p_outputs[0],r_error_str,r_error);
+ if (error && r_error.error==Variant::CallError::CALL_OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (!error && expression->output_type!=Variant::NIL && !Variant::can_convert_strict(p_outputs[0]->get_type(),expression->output_type)) {
+
+ r_error_str+="Can't convert expression result from "+Variant::get_type_name(p_outputs[0]->get_type())+" to "+Variant::get_type_name(expression->output_type)+".";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+
+ }
+#endif
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptExpression::instance(VisualScriptInstance* p_instance){
+
+ _compile_expression();
+ VisualScriptNodeInstanceExpression *instance = memnew( VisualScriptNodeInstanceExpression );
+ instance->instance=p_instance;
+ instance->expression=this;
+ return instance;
+}
+
+
+VisualScriptExpression::VisualScriptExpression()
+{
+ output_type=Variant::NIL;
+ expression_dirty=true;
+ error_set=true;
+ root=NULL;
+ nodes=NULL;
+ sequenced=false;
+}
+
+VisualScriptExpression::~VisualScriptExpression() {
+
+ if (nodes) {
+ memdelete(nodes);
+ }
+}
+
+
+void register_visual_script_expression_node() {
+
+ VisualScriptLanguage::singleton->add_register_func("operators/expression",create_node_generic<VisualScriptExpression>);
+
+}
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
new file mode 100644
index 0000000000..90b955b5da
--- /dev/null
+++ b/modules/visual_script/visual_script_expression.h
@@ -0,0 +1,269 @@
+#ifndef VISUALSCRIPTEXPRESSION_H
+#define VISUALSCRIPTEXPRESSION_H
+
+#include "visual_script.h"
+
+class VisualScriptExpression : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptExpression,VisualScriptNode)
+friend class VisualScriptNodeInstanceExpression;
+
+ struct Input {
+
+ Variant::Type type;
+ String name;
+
+ Input() { type=Variant::NIL; }
+ };
+
+ Vector<Input> inputs;
+ Variant::Type output_type;
+
+ String expression;
+
+ bool sequenced;
+ int str_ofs;
+ bool expression_dirty;
+
+ bool _compile_expression();
+
+ enum TokenType {
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_PARENTHESIS_OPEN,
+ TK_PARENTHESIS_CLOSE,
+ TK_IDENTIFIER,
+ TK_SELF,
+ TK_CONSTANT,
+ TK_BASIC_TYPE,
+ TK_COLON,
+ TK_COMMA,
+ TK_PERIOD,
+ TK_OP_IN,
+ TK_OP_EQUAL,
+ TK_OP_NOT_EQUAL,
+ TK_OP_LESS,
+ TK_OP_LESS_EQUAL,
+ TK_OP_GREATER,
+ TK_OP_GREATER_EQUAL,
+ TK_OP_AND,
+ TK_OP_OR,
+ TK_OP_NOT,
+ TK_OP_ADD,
+ TK_OP_SUB,
+ TK_OP_MUL,
+ TK_OP_DIV,
+ TK_OP_MOD,
+ TK_OP_SHIFT_LEFT,
+ TK_OP_SHIFT_RIGHT,
+ TK_OP_BIT_AND,
+ TK_OP_BIT_OR,
+ TK_OP_BIT_XOR,
+ TK_OP_BIT_INVERT,
+ TK_EOF,
+ TK_ERROR,
+ TK_MAX
+ };
+
+ static const char* token_name[TK_MAX];
+ struct Token {
+
+ TokenType type;
+ Variant value;
+ };
+
+
+ void _set_error(const String& p_err) {
+ if (error_set)
+ return;
+ error_str=p_err;
+ error_set=true;
+ }
+
+ Error _get_token(Token& r_token);
+
+ String error_str;
+ bool error_set;
+
+
+
+ struct ENode {
+
+ enum Type {
+ TYPE_INPUT,
+ TYPE_CONSTANT,
+ TYPE_SELF,
+ TYPE_OPERATOR,
+ TYPE_INDEX,
+ TYPE_NAMED_INDEX,
+ TYPE_ARRAY,
+ TYPE_DICTIONARY,
+ TYPE_CONSTRUCTOR,
+ TYPE_CALL
+ };
+
+ ENode *next;
+
+ Type type;
+
+ ENode() { next=NULL; }
+ virtual ~ENode() { if (next) { memdelete(next); } }
+ };
+
+ struct Expression {
+
+ bool is_op;
+ union {
+ Variant::Operator op;
+ ENode *node;
+ };
+ };
+
+ ENode* _parse_expression();
+
+ struct InputNode : public ENode {
+
+ int index;
+ InputNode() {
+ type=TYPE_INPUT;
+ }
+ };
+
+
+ struct ConstantNode : public ENode {
+
+ Variant value;
+ ConstantNode() {
+ type=TYPE_CONSTANT;
+ }
+ };
+
+ struct OperatorNode : public ENode {
+
+ Variant::Operator op;
+
+ ENode* nodes[2];
+
+ OperatorNode() {
+ type=TYPE_OPERATOR;
+ }
+ };
+
+ struct SelfNode : public ENode {
+
+
+ SelfNode() {
+ type=TYPE_SELF;
+ }
+ };
+
+ struct IndexNode : public ENode {
+ ENode*base;
+ ENode*index;
+
+ IndexNode() {
+ type=TYPE_INDEX;
+ }
+ };
+
+ struct NamedIndexNode : public ENode {
+ ENode*base;
+ StringName name;
+
+ NamedIndexNode() {
+ type=TYPE_NAMED_INDEX;
+ }
+
+ };
+
+ struct ConstructorNode : public ENode {
+ Variant::Type data_type;
+ Vector<ENode*> arguments;
+
+ ConstructorNode() {
+ type=TYPE_CONSTRUCTOR;
+ }
+ };
+
+ struct CallNode : public ENode {
+ ENode*base;
+ StringName method;
+ Vector<ENode*> arguments;
+
+ CallNode() {
+ type=TYPE_CALL;
+ }
+
+ };
+
+ struct ArrayNode : public ENode {
+ Vector<ENode*> array;
+ ArrayNode() {
+ type=TYPE_ARRAY;
+ }
+
+ };
+
+ struct DictionaryNode : public ENode {
+ Vector<ENode*> dict;
+ DictionaryNode() {
+ type=TYPE_DICTIONARY;
+ }
+
+ };
+
+ template<class T>
+ T* alloc_node() {
+ T* node = memnew(T);
+ node->next=nodes;
+ nodes=node;
+ return node;
+ }
+
+ ENode *root;
+ ENode *nodes;
+
+
+
+
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+
+ VisualScriptExpression();
+ ~VisualScriptExpression();
+};
+
+
+void register_visual_script_expression_node();
+
+
+#endif // VISUALSCRIPTEXPRESSION_H
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
new file mode 100644
index 0000000000..97338da187
--- /dev/null
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -0,0 +1,1940 @@
+#include "visual_script_flow_control.h"
+#include "os/keyboard.h"
+#include "globals.h"
+
+
+//////////////////////////////////////////
+////////////////RETURN////////////////////
+//////////////////////////////////////////
+
+int VisualScriptReturn::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptReturn::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptReturn::get_input_value_port_count() const{
+
+ return with_value?1:0;
+}
+int VisualScriptReturn::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptReturn::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptReturn::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="result";
+ pinfo.type=type;
+ return pinfo;
+}
+PropertyInfo VisualScriptReturn::get_output_value_port_info(int p_idx) const{
+ return PropertyInfo();
+}
+
+String VisualScriptReturn::get_caption() const {
+
+ return "Return";
+}
+
+String VisualScriptReturn::get_text() const {
+
+ return get_name();
+}
+
+void VisualScriptReturn::set_return_type(Variant::Type p_type) {
+
+ if (type==p_type)
+ return;
+ type=p_type;
+ ports_changed_notify();
+
+}
+
+Variant::Type VisualScriptReturn::get_return_type() const{
+
+ return type;
+}
+
+void VisualScriptReturn::set_enable_return_value(bool p_enable) {
+ if (with_value==p_enable)
+ return;
+
+ with_value=p_enable;
+ ports_changed_notify();
+}
+
+bool VisualScriptReturn::is_return_value_enabled() const {
+
+ return with_value;
+}
+
+void VisualScriptReturn::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_return_type","type"),&VisualScriptReturn::set_return_type);
+ ObjectTypeDB::bind_method(_MD("get_return_type"),&VisualScriptReturn::get_return_type);
+ ObjectTypeDB::bind_method(_MD("set_enable_return_value","enable"),&VisualScriptReturn::set_enable_return_value);
+ ObjectTypeDB::bind_method(_MD("is_return_value_enabled"),&VisualScriptReturn::is_return_value_enabled);
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"return_value/enabled"),_SCS("set_enable_return_value"),_SCS("is_return_value_enabled"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"return_value/type",PROPERTY_HINT_ENUM,argt),_SCS("set_return_type"),_SCS("get_return_type"));
+
+}
+
+class VisualScriptNodeInstanceReturn : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptReturn *node;
+ VisualScriptInstance *instance;
+ bool with_value;
+
+ virtual int get_working_memory_size() const { return 1; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (with_value) {
+ *p_working_mem = *p_inputs[0];
+ } else {
+ *p_working_mem = Variant();
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptReturn::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceReturn * instance = memnew(VisualScriptNodeInstanceReturn );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->with_value=with_value;
+ return instance;
+}
+
+VisualScriptReturn::VisualScriptReturn() {
+
+ with_value=false;
+ type=Variant::NIL;
+}
+
+template<bool with_value>
+static Ref<VisualScriptNode> create_return_node(const String& p_name) {
+
+ Ref<VisualScriptReturn> node;
+ node.instance();
+ node->set_enable_return_value(with_value);
+ return node;
+}
+
+
+
+//////////////////////////////////////////
+////////////////CONDITION/////////////////
+//////////////////////////////////////////
+
+int VisualScriptCondition::get_output_sequence_port_count() const {
+
+ return 3;
+}
+
+bool VisualScriptCondition::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptCondition::get_input_value_port_count() const{
+
+ return 1;
+}
+int VisualScriptCondition::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptCondition::get_output_sequence_port_text(int p_port) const {
+
+ if (p_port==0)
+ return "true";
+ else if (p_port==1)
+ return "false";
+ else
+ return "done";
+}
+
+PropertyInfo VisualScriptCondition::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="cond";
+ pinfo.type=Variant::BOOL;
+ return pinfo;
+}
+PropertyInfo VisualScriptCondition::get_output_value_port_info(int p_idx) const{
+ return PropertyInfo();
+}
+
+String VisualScriptCondition::get_caption() const {
+
+ return "Condition";
+}
+
+String VisualScriptCondition::get_text() const {
+
+ return "if (cond) is: ";
+}
+
+
+void VisualScriptCondition::_bind_methods() {
+
+
+
+}
+
+class VisualScriptNodeInstanceCondition : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptCondition *node;
+ VisualScriptInstance *instance;
+
+ //virtual int get_working_memory_size() const { return 1; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_CONTINUE_SEQUENCE)
+ return 2;
+ else if (p_inputs[0]->operator bool())
+ return 0 | STEP_FLAG_PUSH_STACK_BIT;
+ else
+ return 1 | STEP_FLAG_PUSH_STACK_BIT;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptCondition::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceCondition * instance = memnew(VisualScriptNodeInstanceCondition );
+ instance->node=this;
+ instance->instance=p_instance;
+ return instance;
+}
+
+VisualScriptCondition::VisualScriptCondition() {
+
+}
+
+
+
+//////////////////////////////////////////
+////////////////WHILE/////////////////
+//////////////////////////////////////////
+
+int VisualScriptWhile::get_output_sequence_port_count() const {
+
+ return 2;
+}
+
+bool VisualScriptWhile::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptWhile::get_input_value_port_count() const{
+
+ return 1;
+}
+int VisualScriptWhile::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptWhile::get_output_sequence_port_text(int p_port) const {
+
+ if (p_port==0)
+ return "repeat";
+ else
+ return "exit";
+}
+
+PropertyInfo VisualScriptWhile::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="cond";
+ pinfo.type=Variant::BOOL;
+ return pinfo;
+}
+PropertyInfo VisualScriptWhile::get_output_value_port_info(int p_idx) const{
+ return PropertyInfo();
+}
+
+String VisualScriptWhile::get_caption() const {
+
+ return "While";
+}
+
+String VisualScriptWhile::get_text() const {
+
+ return "while (cond): ";
+}
+
+
+void VisualScriptWhile::_bind_methods() {
+
+
+
+}
+
+class VisualScriptNodeInstanceWhile : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptWhile *node;
+ VisualScriptInstance *instance;
+
+ //virtual int get_working_memory_size() const { return 1; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ bool keep_going = p_inputs[0]->operator bool();
+
+ if (keep_going)
+ return 0|STEP_FLAG_PUSH_STACK_BIT;
+ else
+ return 1;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptWhile::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceWhile * instance = memnew(VisualScriptNodeInstanceWhile );
+ instance->node=this;
+ instance->instance=p_instance;
+ return instance;
+}
+VisualScriptWhile::VisualScriptWhile() {
+
+}
+
+
+
+//////////////////////////////////////////
+////////////////ITERATOR/////////////////
+//////////////////////////////////////////
+
+int VisualScriptIterator::get_output_sequence_port_count() const {
+
+ return 2;
+}
+
+bool VisualScriptIterator::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptIterator::get_input_value_port_count() const{
+
+ return 1;
+}
+int VisualScriptIterator::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptIterator::get_output_sequence_port_text(int p_port) const {
+
+ if (p_port==0)
+ return "each";
+ else
+ return "exit";
+}
+
+PropertyInfo VisualScriptIterator::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="input";
+ pinfo.type=Variant::NIL;
+ return pinfo;
+}
+PropertyInfo VisualScriptIterator::get_output_value_port_info(int p_idx) const{
+ PropertyInfo pinfo;
+ pinfo.name="elem";
+ pinfo.type=Variant::NIL;
+ return pinfo;
+}
+String VisualScriptIterator::get_caption() const {
+
+ return "Iterator";
+}
+
+String VisualScriptIterator::get_text() const {
+
+ return "for (elem) in (input): ";
+}
+
+
+void VisualScriptIterator::_bind_methods() {
+
+
+
+}
+
+class VisualScriptNodeInstanceIterator : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptIterator *node;
+ VisualScriptInstance *instance;
+
+ virtual int get_working_memory_size() const { return 2; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_BEGIN_SEQUENCE) {
+ p_working_mem[0]=*p_inputs[0];
+ bool valid;
+ bool can_iter = p_inputs[0]->iter_init(p_working_mem[1],valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Input type not iterable: ")+Variant::get_type_name(p_inputs[0]->get_type());
+ return 0;
+ }
+
+ if (!can_iter)
+ return 1; //nothing to iterate
+
+
+ *p_outputs[0]=p_working_mem[0].iter_get( p_working_mem[1],valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Iterator became invalid");
+ return 0;
+ }
+
+
+ } else { //continue sequence
+
+ bool valid;
+ bool can_iter = p_working_mem[0].iter_next(p_working_mem[1],valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Iterator became invalid: ")+Variant::get_type_name(p_inputs[0]->get_type());
+ return 0;
+ }
+
+ if (!can_iter)
+ return 1; //nothing to iterate
+
+
+ *p_outputs[0]=p_working_mem[0].iter_get( p_working_mem[1],valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Iterator became invalid");
+ return 0;
+ }
+
+ }
+
+ return 0|STEP_FLAG_PUSH_STACK_BIT; //go around
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptIterator::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceIterator * instance = memnew(VisualScriptNodeInstanceIterator );
+ instance->node=this;
+ instance->instance=p_instance;
+ return instance;
+}
+
+VisualScriptIterator::VisualScriptIterator() {
+
+}
+
+
+
+//////////////////////////////////////////
+////////////////SEQUENCE/////////////////
+//////////////////////////////////////////
+
+int VisualScriptSequence::get_output_sequence_port_count() const {
+
+ return steps;
+}
+
+bool VisualScriptSequence::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptSequence::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptSequence::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptSequence::get_output_sequence_port_text(int p_port) const {
+
+ return itos(p_port+1);
+}
+
+PropertyInfo VisualScriptSequence::get_input_value_port_info(int p_idx) const{
+ return PropertyInfo();
+}
+PropertyInfo VisualScriptSequence::get_output_value_port_info(int p_idx) const{
+ return PropertyInfo(Variant::INT,"current");
+}
+String VisualScriptSequence::get_caption() const {
+
+ return "Sequence";
+}
+
+String VisualScriptSequence::get_text() const {
+
+ return "in order: ";
+}
+
+void VisualScriptSequence::set_steps(int p_steps) {
+
+ ERR_FAIL_COND(p_steps<1);
+ if (steps==p_steps)
+ return;
+
+ steps=p_steps;
+ ports_changed_notify();
+
+}
+
+int VisualScriptSequence::get_steps() const {
+
+ return steps;
+}
+
+void VisualScriptSequence::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_steps","steps"),&VisualScriptSequence::set_steps);
+ ObjectTypeDB::bind_method(_MD("get_steps"),&VisualScriptSequence::get_steps);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"steps",PROPERTY_HINT_RANGE,"1,64,1"),_SCS("set_steps"),_SCS("get_steps"));
+
+}
+
+class VisualScriptNodeInstanceSequence : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptSequence *node;
+ VisualScriptInstance *instance;
+ int steps;
+
+ virtual int get_working_memory_size() const { return 1; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_BEGIN_SEQUENCE) {
+
+ p_working_mem[0]=0;
+ }
+
+ int step = p_working_mem[0];
+
+ *p_outputs[0]=step;
+
+ if (step+1==steps)
+ return step;
+ else {
+ p_working_mem[0]=step+1;
+ return step|STEP_FLAG_PUSH_STACK_BIT;
+ }
+
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSequence::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSequence * instance = memnew(VisualScriptNodeInstanceSequence );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->steps=steps;
+ return instance;
+}
+VisualScriptSequence::VisualScriptSequence() {
+
+ steps=1;
+}
+
+
+//////////////////////////////////////////
+////////////////EVENT TYPE FILTER///////////
+//////////////////////////////////////////
+
+int VisualScriptSwitch::get_output_sequence_port_count() const {
+
+ return case_values.size()+1;
+}
+
+bool VisualScriptSwitch::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptSwitch::get_input_value_port_count() const{
+
+
+ return case_values.size()+1;
+}
+int VisualScriptSwitch::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptSwitch::get_output_sequence_port_text(int p_port) const {
+
+ if (p_port==case_values.size())
+ return "done";
+
+ return String();
+}
+
+PropertyInfo VisualScriptSwitch::get_input_value_port_info(int p_idx) const{
+
+ if (p_idx<case_values.size()) {
+ return PropertyInfo(case_values[p_idx].type," =");
+ } else
+ return PropertyInfo(Variant::NIL,"input");
+}
+
+PropertyInfo VisualScriptSwitch::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptSwitch::get_caption() const {
+
+ return "Switch";
+}
+
+String VisualScriptSwitch::get_text() const {
+
+ return "'input' is:";
+}
+
+
+class VisualScriptNodeInstanceSwitch : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ int case_count;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_CONTINUE_SEQUENCE) {
+ return case_count; //exit
+ }
+
+ for(int i=0;i<case_count;i++) {
+
+ if (*p_inputs[i]==*p_inputs[case_count]) {
+ return i|STEP_FLAG_PUSH_STACK_BIT;
+ }
+ }
+
+ return case_count;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSwitch::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSwitch * instance = memnew(VisualScriptNodeInstanceSwitch );
+ instance->instance=p_instance;
+ instance->case_count=case_values.size();
+ return instance;
+}
+
+bool VisualScriptSwitch::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (String(p_name)=="case_count") {
+ case_values.resize(p_value);
+ _change_notify();
+ ports_changed_notify();
+ return true;
+ }
+
+ if (String(p_name).begins_with("case/")) {
+
+ int idx = String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,case_values.size(),false);
+
+ case_values[idx].type=Variant::Type(int(p_value));
+ _change_notify();
+ ports_changed_notify();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool VisualScriptSwitch::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (String(p_name)=="case_count") {
+ r_ret=case_values.size();
+ return true;
+ }
+
+ if (String(p_name).begins_with("case/")) {
+
+ int idx = String(p_name).get_slice("/",1).to_int();
+ ERR_FAIL_INDEX_V(idx,case_values.size(),false);
+
+ r_ret=case_values[idx].type;
+ return true;
+ }
+
+ return false;
+
+}
+void VisualScriptSwitch::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back(PropertyInfo(Variant::INT,"case_count",PROPERTY_HINT_RANGE,"0,128"));
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ for(int i=0;i<case_values.size();i++) {
+ p_list->push_back(PropertyInfo(Variant::INT,"case/"+itos(i),PROPERTY_HINT_ENUM,argt));
+ }
+}
+
+
+void VisualScriptSwitch::_bind_methods() {
+
+
+}
+
+VisualScriptSwitch::VisualScriptSwitch() {
+
+
+}
+
+//////////////////////////////////////////
+////////////////EVENT ACTION FILTER///////////
+//////////////////////////////////////////
+
+
+int VisualScriptInputFilter::get_output_sequence_port_count() const {
+
+ return filters.size();
+}
+
+bool VisualScriptInputFilter::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptInputFilter::get_input_value_port_count() const{
+
+
+ return 1;
+}
+int VisualScriptInputFilter::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptInputFilter::get_output_sequence_port_text(int p_port) const {
+
+ String text;
+
+ switch(filters[p_port].type) {
+ case InputEvent::NONE: {
+ text="None";
+ } break;
+ case InputEvent::KEY: {
+
+ InputEventKey k = filters[p_port].key;
+
+ if (k.scancode==0 && k.unicode==0) {
+ text="No Key";
+ } else {
+ if (k.scancode!=0) {
+ text="KeyCode: "+keycode_get_string(k.scancode);
+ } else if (k.unicode!=0) {
+ text="Uniode: "+String::chr(k.unicode);
+ }
+
+ if (k.pressed)
+ text+=", Pressed";
+ else
+ text+=", Released";
+
+ if (k.echo)
+ text+=", Echo";
+ if (k.mod.alt)
+ text="Alt+"+text;
+ if (k.mod.shift)
+ text="Shift+"+text;
+ if (k.mod.control)
+ text="Ctrl+"+text;
+ if (k.mod.meta)
+ text="Meta+"+text;
+ }
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+ InputEventMouseMotion mm = filters[p_port].mouse_motion;
+ text="Mouse Motion";
+
+ String b = "Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight";
+
+ for(int i=0;i<7;i++) {
+ if (mm.button_mask&(1<<i)) {
+ text=b.get_slice(",",i)+"+"+text;
+ }
+ }
+ if (mm.mod.alt)
+ text="Alt+"+text;
+ if (mm.mod.shift)
+ text="Shift+"+text;
+ if (mm.mod.control)
+ text="Ctrl+"+text;
+ if (mm.mod.meta)
+ text="Meta+"+text;
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ InputEventMouseButton mb = filters[p_port].mouse_button;
+
+ String b = "Any,Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight";
+
+ text=b.get_slice(",",mb.button_index)+" Mouse Button";
+
+ if (mb.pressed)
+ text+=", Pressed";
+ else
+ text+=", Released";
+
+ if (mb.doubleclick)
+ text+=", DblClick";
+ if (mb.mod.alt)
+ text="Alt+"+text;
+ if (mb.mod.shift)
+ text="Shift+"+text;
+ if (mb.mod.control)
+ text="Ctrl+"+text;
+ if (mb.mod.meta)
+ text="Meta+"+text;
+
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ InputEventJoystickMotion jm = filters[p_port].joy_motion;
+
+ text="JoyMotion Axis "+itos(jm.axis>>1);
+ if (jm.axis&1)
+ text+=" > "+rtos(jm.axis_value);
+ else
+ text+=" < "+rtos(-jm.axis_value);
+
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+ InputEventJoystickButton jb = filters[p_port].joy_button;
+
+ text="JoyButton "+itos(jb.button_index);
+ if (jb.pressed)
+ text+=", Pressed";
+ else
+ text+=", Released";
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+ InputEventScreenTouch sd = filters[p_port].screen_touch;
+
+ text="Touch Finger "+itos(sd.index);
+ if (sd.pressed)
+ text+=", Pressed";
+ else
+ text+=", Released";
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ InputEventScreenDrag sd = filters[p_port].screen_drag;
+ text="Drag Finger "+itos(sd.index);
+ } break;
+ case InputEvent::ACTION: {
+
+
+ List<PropertyInfo> pinfo;
+ Globals::get_singleton()->get_property_list(&pinfo);
+ int index=1;
+
+ text="No Action";
+ for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ const PropertyInfo &pi=E->get();
+
+ if (!pi.name.begins_with("input/"))
+ continue;
+
+
+ if (filters[p_port].action.action==index) {
+ text="Action "+pi.name.substr(pi.name.find("/")+1,pi.name.length());
+ break;
+ }
+ index++;
+ }
+
+ if (filters[p_port].action.pressed)
+ text+=", Pressed";
+ else
+ text+=", Released";
+
+
+ } break;
+ }
+
+
+
+ return text+" - "+itos(p_port);
+}
+
+PropertyInfo VisualScriptInputFilter::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::INPUT_EVENT,"event");
+}
+
+PropertyInfo VisualScriptInputFilter::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::INPUT_EVENT,"");
+}
+
+
+String VisualScriptInputFilter::get_caption() const {
+
+ return "InputFilter";
+}
+
+String VisualScriptInputFilter::get_text() const {
+
+ return "";
+}
+
+
+
+bool VisualScriptInputFilter::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name=="filter_count") {
+ filters.resize(p_value);
+ _change_notify();
+ ports_changed_notify();
+ return true;
+ }
+
+
+ if (String(p_name).begins_with("filter_")) {
+
+ int idx = String(p_name).replace_first("filters_","").get_slice("/",0).to_int();
+
+ ERR_FAIL_INDEX_V(idx,filters.size(),false);
+
+ String what = String(p_name).get_slice("/",1);
+
+
+ if (what=="type") {
+ filters[idx]=InputEvent();
+ filters[idx].type=InputEvent::Type(int(p_value));
+ if (filters[idx].type==InputEvent::JOYSTICK_MOTION) {
+ filters[idx].joy_motion.axis_value=0.5; //for treshold
+ } else if (filters[idx].type==InputEvent::KEY) {
+ filters[idx].key.pressed=true; //put these as true to make it more user friendly
+ } else if (filters[idx].type==InputEvent::MOUSE_BUTTON) {
+ filters[idx].mouse_button.pressed=true;
+ } else if (filters[idx].type==InputEvent::JOYSTICK_BUTTON) {
+ filters[idx].joy_button.pressed=true;
+ } else if (filters[idx].type==InputEvent::SCREEN_TOUCH) {
+ filters[idx].screen_touch.pressed=true;
+ } else if (filters[idx].type==InputEvent::ACTION) {
+ filters[idx].action.pressed=true;
+ }
+ _change_notify();
+ ports_changed_notify();
+
+ return true;
+ }
+ if (what=="device") {
+ filters[idx].device=p_value;
+ ports_changed_notify();
+ return true;
+ }
+
+ switch(filters[idx].type) {
+
+ case InputEvent::KEY: {
+
+ if (what=="scancode") {
+ String sc = p_value;
+ if (sc==String()) {
+ filters[idx].key.scancode=0;
+ } else {
+ filters[idx].key.scancode=find_keycode(p_value);
+ }
+
+ } else if (what=="unicode") {
+
+ String uc = p_value;
+
+ if (uc==String()) {
+ filters[idx].key.unicode=0;
+ } else {
+ filters[idx].key.unicode=uc[0];
+ }
+
+ } else if (what=="pressed") {
+
+ filters[idx].key.pressed=p_value;
+ } else if (what=="echo") {
+
+ filters[idx].key.echo=p_value;
+
+ } else if (what=="mod_alt") {
+ filters[idx].key.mod.alt=p_value;
+
+ } else if (what=="mod_shift") {
+ filters[idx].key.mod.shift=p_value;
+
+ } else if (what=="mod_ctrl") {
+ filters[idx].key.mod.control=p_value;
+
+ } else if (what=="mod_meta") {
+ filters[idx].key.mod.meta=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+
+ return true;
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+
+ if (what=="button_mask") {
+ filters[idx].mouse_motion.button_mask=p_value;
+
+ } else if (what=="mod_alt") {
+ filters[idx].mouse_motion.mod.alt=p_value;
+
+ } else if (what=="mod_shift") {
+ filters[idx].mouse_motion.mod.shift=p_value;
+
+ } else if (what=="mod_ctrl") {
+ filters[idx].mouse_motion.mod.control=p_value;
+
+ } else if (what=="mod_meta") {
+ filters[idx].mouse_motion.mod.meta=p_value;
+ } else {
+ return false;
+ }
+
+ ports_changed_notify();
+ return true;
+
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ if (what=="button_index") {
+ filters[idx].mouse_button.button_index=p_value;
+ } else if (what=="pressed") {
+ filters[idx].mouse_button.pressed=p_value;
+ } else if (what=="doubleclicked") {
+ filters[idx].mouse_button.doubleclick=p_value;
+
+ } else if (what=="mod_alt") {
+ filters[idx].mouse_button.mod.alt=p_value;
+
+ } else if (what=="mod_shift") {
+ filters[idx].mouse_button.mod.shift=p_value;
+
+ } else if (what=="mod_ctrl") {
+ filters[idx].mouse_button.mod.control=p_value;
+
+ } else if (what=="mod_meta") {
+ filters[idx].mouse_button.mod.meta=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+ return true;
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ if (what=="axis") {
+ filters[idx].joy_motion.axis=int(p_value)<<1|filters[idx].joy_motion.axis;
+ } else if (what=="mode") {
+ filters[idx].joy_motion.axis|=int(p_value);
+ } else if (what=="treshold") {
+ filters[idx].joy_motion.axis_value=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+ return true;
+
+
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+
+ if (what=="button_index") {
+ filters[idx].joy_button.button_index=p_value;
+ } else if (what=="pressed") {
+ filters[idx].joy_button.pressed=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+ return true;
+
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+
+ if (what=="finger_index") {
+ filters[idx].screen_touch.index=p_value;
+ } else if (what=="pressed") {
+ filters[idx].screen_touch.pressed=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+ return true;
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ if (what=="finger_index") {
+ filters[idx].screen_drag.index=p_value;
+ } else {
+ return false;
+ }
+ ports_changed_notify();
+ return true;
+ } break;
+ case InputEvent::ACTION: {
+
+
+ if (what=="action_name") {
+
+ List<PropertyInfo> pinfo;
+ Globals::get_singleton()->get_property_list(&pinfo);
+ int index=1;
+
+ for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ const PropertyInfo &pi=E->get();
+
+ if (!pi.name.begins_with("input/"))
+ continue;
+
+ String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
+ if (name==String(p_value)) {
+
+ filters[idx].action.action=index;
+ ports_changed_notify();
+ return true;
+ }
+
+ index++;
+ }
+
+ filters[idx].action.action=0;
+ ports_changed_notify();
+
+ return false;
+
+ } else if (what=="pressed") {
+
+ filters[idx].action.pressed=p_value;
+ ports_changed_notify();
+ return true;
+ }
+
+
+ } break;
+
+ }
+ }
+ return false;
+}
+
+bool VisualScriptInputFilter::_get(const StringName& p_name,Variant &r_ret) const{
+
+ if (p_name=="filter_count") {
+ r_ret=filters.size();
+ return true;
+ }
+
+
+ if (String(p_name).begins_with("filter_")) {
+
+ int idx = String(p_name).replace_first("filters_","").get_slice("/",0).to_int();
+
+ ERR_FAIL_INDEX_V(idx,filters.size(),false);
+
+ String what = String(p_name).get_slice("/",1);
+
+
+ if (what=="type") {
+ r_ret=filters[idx].type;
+ return true;
+ }
+ if (what=="device") {
+ r_ret=filters[idx].device;
+ return true;
+ }
+
+ switch(filters[idx].type) {
+
+ case InputEvent::KEY: {
+
+ if (what=="scancode") {
+ if (filters[idx].key.scancode==0)
+ r_ret=String();
+ else {
+
+ r_ret=keycode_get_string(filters[idx].key.scancode);
+ }
+
+ } else if (what=="unicode") {
+
+
+ if (filters[idx].key.unicode==0) {
+ r_ret=String();
+ } else {
+ CharType str[2]={ (CharType)filters[idx].key.unicode, 0};
+ r_ret=String(str);
+ }
+
+ } else if (what=="pressed") {
+
+ r_ret=filters[idx].key.pressed;
+ } else if (what=="echo") {
+
+ r_ret=filters[idx].key.echo;
+
+ } else if (what=="mod_alt") {
+ r_ret=filters[idx].key.mod.alt;
+
+ } else if (what=="mod_shift") {
+ r_ret=filters[idx].key.mod.shift;
+
+ } else if (what=="mod_ctrl") {
+ r_ret=filters[idx].key.mod.control;
+
+ } else if (what=="mod_meta") {
+ r_ret=filters[idx].key.mod.meta;
+ } else {
+ return false;
+ }
+
+ return true;
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+
+ if (what=="button_mask") {
+ r_ret=filters[idx].mouse_motion.button_mask;
+
+ } else if (what=="mod_alt") {
+ r_ret=filters[idx].mouse_motion.mod.alt;
+
+ } else if (what=="mod_shift") {
+ r_ret=filters[idx].mouse_motion.mod.shift;
+
+ } else if (what=="mod_ctrl") {
+ r_ret=filters[idx].mouse_motion.mod.control;
+
+ } else if (what=="mod_meta") {
+ r_ret=filters[idx].mouse_motion.mod.meta;
+ } else {
+ return false;
+ }
+
+ return true;
+
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ if (what=="button_index") {
+ r_ret=filters[idx].mouse_button.button_index;
+ } else if (what=="pressed") {
+ r_ret=filters[idx].mouse_button.pressed;
+ } else if (what=="doubleclicked") {
+ r_ret=filters[idx].mouse_button.doubleclick;
+
+ } else if (what=="mod_alt") {
+ r_ret=filters[idx].mouse_button.mod.alt;
+
+ } else if (what=="mod_shift") {
+ r_ret=filters[idx].mouse_button.mod.shift;
+
+ } else if (what=="mod_ctrl") {
+ r_ret=filters[idx].mouse_button.mod.control;
+
+ } else if (what=="mod_meta") {
+ r_ret=filters[idx].mouse_button.mod.meta;
+ } else {
+ return false;
+ }
+ return true;
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ if (what=="axis_index") {
+ r_ret=filters[idx].joy_motion.axis>>1;
+ } else if (what=="mode") {
+ r_ret=filters[idx].joy_motion.axis&1;
+ } else if (what=="treshold") {
+ r_ret=filters[idx].joy_motion.axis_value;
+ } else {
+ return false;
+ }
+ return true;
+
+
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+
+ if (what=="button_index") {
+ r_ret=filters[idx].joy_button.button_index;
+ } else if (what=="pressed") {
+ r_ret=filters[idx].joy_button.pressed;
+ } else {
+ return false;
+ }
+ return true;
+
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+
+ if (what=="finger_index") {
+ r_ret=filters[idx].screen_touch.index;
+ } else if (what=="pressed") {
+ r_ret=filters[idx].screen_touch.pressed;
+ } else {
+ return false;
+ }
+ return true;
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ if (what=="finger_index") {
+ r_ret=filters[idx].screen_drag.index;
+ } else {
+ return false;
+ }
+ return true;
+ } break;
+ case InputEvent::ACTION: {
+
+
+ if (what=="action_name") {
+
+ List<PropertyInfo> pinfo;
+ Globals::get_singleton()->get_property_list(&pinfo);
+ int index=1;
+
+ for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ const PropertyInfo &pi=E->get();
+
+ if (!pi.name.begins_with("input/"))
+ continue;
+
+
+ if (filters[idx].action.action==index) {
+ r_ret=pi.name.substr(pi.name.find("/")+1,pi.name.length());
+ return true;
+ }
+ index++;
+ }
+
+ r_ret="None"; //no index
+ return false;
+
+ } else if (what=="pressed") {
+
+ r_ret=filters[idx].action.pressed;
+ return true;
+ }
+
+
+ } break;
+
+ }
+ }
+ return false;
+}
+
+static const char* event_type_names[InputEvent::TYPE_MAX]={
+ "None",
+ "Key",
+ "MouseMotion",
+ "MouseButton",
+ "JoystickMotion",
+ "JoystickButton",
+ "ScreenTouch",
+ "ScreenDrag",
+ "Action"
+};
+
+void VisualScriptInputFilter::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back(PropertyInfo(Variant::INT,"filter_count",PROPERTY_HINT_RANGE,"0,64"));
+
+ String et;
+ for(int i=0;i<InputEvent::TYPE_MAX;i++) {
+ if (i>0)
+ et+=",";
+
+ et+=event_type_names[i];
+ }
+
+ String kc;
+ String actions;
+
+
+
+ for(int i=0;i<filters.size();i++) {
+
+ String base = "filter_"+itos(i)+"/";
+ p_list->push_back(PropertyInfo(Variant::INT,base+"type",PROPERTY_HINT_ENUM,et));
+ p_list->push_back(PropertyInfo(Variant::INT,base+"device"));
+ switch(filters[i].type) {
+
+ case InputEvent::NONE: {
+
+ } break;
+ case InputEvent::KEY: {
+ if (kc==String()) {
+ int kcc = keycode_get_count();
+ kc="None";
+ for(int i=0;i<kcc;i++) {
+ kc+=",";
+ kc+=String(keycode_get_name_by_index(i));
+ }
+ }
+ p_list->push_back(PropertyInfo(Variant::STRING,base+"scancode",PROPERTY_HINT_ENUM,kc));
+ p_list->push_back(PropertyInfo(Variant::STRING,base+"unicode"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"echo"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+ p_list->push_back(PropertyInfo(Variant::INT,base+"button_mask",PROPERTY_HINT_FLAGS,"Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));
+
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+ p_list->push_back(PropertyInfo(Variant::INT,base+"button_index",PROPERTY_HINT_ENUM,"Any,Left,Right,Middle,WheelUp,WheelDown,WheelLeft,WheelRight"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"doubleclicked"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_alt"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_shift"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_ctrl"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"mod_meta"));
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ p_list->push_back(PropertyInfo(Variant::INT,base+"axis_index"));
+ p_list->push_back(PropertyInfo(Variant::INT,base+"mode",PROPERTY_HINT_ENUM,"Min,Max"));
+ p_list->push_back(PropertyInfo(Variant::REAL,base+"treshold",PROPERTY_HINT_RANGE,"0,1,0.01"));
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+ p_list->push_back(PropertyInfo(Variant::INT,base+"button_index"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
+
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+ p_list->push_back(PropertyInfo(Variant::INT,base+"finger_index"));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
+
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ p_list->push_back(PropertyInfo(Variant::INT,base+"finger_index"));
+ } break;
+ case InputEvent::ACTION: {
+
+
+
+ if (actions==String()) {
+
+ actions="None";
+
+ List<PropertyInfo> pinfo;
+ Globals::get_singleton()->get_property_list(&pinfo);
+ Vector<String> al;
+
+ for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ const PropertyInfo &pi=E->get();
+
+ if (!pi.name.begins_with("input/"))
+ continue;
+
+ String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
+
+
+ al.push_back(name);
+ }
+
+ for(int i=0;i<al.size();i++) {
+ actions+=",";
+ actions+=al[i];
+ }
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING,base+"action_name",PROPERTY_HINT_ENUM,actions));
+ p_list->push_back(PropertyInfo(Variant::BOOL,base+"pressed"));
+
+ } break;
+
+ }
+ }
+}
+
+class VisualScriptNodeInstanceInputFilter : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ Vector<InputEvent> filters;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_inputs[0]->get_type()!=Variant::INPUT_EVENT) {
+ r_error_str="Input value not of type event";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+ InputEvent event = *p_inputs[0];
+
+
+ for(int i=0;i<filters.size();i++) {
+
+ const InputEvent &ie = filters[i];
+ if (ie.type!=event.type)
+ continue;
+
+ bool match=false;
+
+ switch(ie.type) {
+
+ case InputEvent::NONE: {
+
+ match=true;
+ } break;
+ case InputEvent::KEY: {
+
+ InputEventKey k = ie.key;
+ InputEventKey k2 = event.key;
+
+ if (k.scancode==0 && k.unicode==0 && k2.scancode==0 && k2.unicode==0) {
+ match=true;
+
+ } else {
+
+ if ( (k.scancode!=0 && k.scancode==k2.scancode) || (k.unicode!=0 && k.unicode==k2.unicode)) {
+ //key valid
+
+ if (
+ k.pressed==k2.pressed &&
+ k.echo==k2.echo &&
+ k.mod == k2.mod
+ ) {
+ match=true;
+ }
+ }
+
+ }
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+ InputEventMouseMotion mm = ie.mouse_motion;
+ InputEventMouseMotion mm2 = event.mouse_motion;
+
+ if ( mm.button_mask==mm2.button_mask &&
+ mm.mod==mm2.mod
+ ) {
+ match=true;
+ }
+
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ InputEventMouseButton mb = ie.mouse_button;
+ InputEventMouseButton mb2 = event.mouse_button;
+
+ if ( mb.button_index==mb2.button_index &&
+ mb.pressed==mb2.pressed &&
+ mb.doubleclick==mb2.doubleclick &&
+ mb.mod==mb2.mod) {
+ match=true;
+ }
+
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ InputEventJoystickMotion jm = ie.joy_motion;
+ InputEventJoystickMotion jm2 = event.joy_motion;
+
+ int axis = jm.axis>>1;
+
+ if (axis==jm2.axis) {
+
+ if (jm.axis&1) {
+ //greater
+ if (jm2.axis_value > jm.axis_value) {
+ match=true;
+ }
+ } else {
+ //less
+ if (jm2.axis_value < -jm.axis_value) {
+ match=true;
+ }
+ }
+ }
+
+
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+ InputEventJoystickButton jb = ie.joy_button;
+ InputEventJoystickButton jb2 = event.joy_button;
+
+ if ( jb.button_index==jb2.button_index &&
+ jb.pressed == jb2.pressed
+ ) {
+ match=true;
+ }
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+ InputEventScreenTouch st = ie.screen_touch;
+ InputEventScreenTouch st2 = event.screen_touch;
+
+ if ( st.index==st2.index &&
+ st.pressed==st2.pressed) {
+ match=true;
+ }
+
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ InputEventScreenDrag sd = ie.screen_drag;
+ InputEventScreenDrag sd2 = event.screen_drag;
+
+ if (sd.index==sd2.index) {
+ match=true;
+ }
+ } break;
+ case InputEvent::ACTION: {
+
+ InputEventAction ia = ie.action;
+ InputEventAction ia2 = event.action;
+
+ if ( ia.action==ia2.action &&
+ ia.pressed==ia2.pressed) {
+ match=true;
+ }
+ } break;
+
+ }
+
+ *p_outputs[0] = event;
+
+ if (match)
+ return i; //go through match output
+
+ }
+
+ return STEP_NO_ADVANCE_BIT; //none found, don't advance
+
+
+ }
+
+
+};
+
+
+VisualScriptNodeInstance* VisualScriptInputFilter::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceInputFilter * instance = memnew(VisualScriptNodeInstanceInputFilter );
+ instance->instance=p_instance;
+ instance->filters=filters;
+ return instance;
+}
+
+
+
+
+VisualScriptInputFilter::VisualScriptInputFilter() {
+
+
+}
+
+
+//////////////////////////////////////////
+////////////////TYPE CAST///////////
+//////////////////////////////////////////
+
+
+int VisualScriptTypeCast::get_output_sequence_port_count() const {
+
+ return 2;
+}
+
+bool VisualScriptTypeCast::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptTypeCast::get_input_value_port_count() const{
+
+
+ return 1;
+}
+int VisualScriptTypeCast::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptTypeCast::get_output_sequence_port_text(int p_port) const {
+
+ return p_port==0 ? "yes" : "no";
+}
+
+PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"instance");
+}
+
+PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"");
+}
+
+
+String VisualScriptTypeCast::get_caption() const {
+
+ return "TypeCast";
+}
+
+String VisualScriptTypeCast::get_text() const {
+
+ if (script!=String())
+ return "Is "+script.get_file()+"?";
+ else
+ return "Is "+base_type+"?";
+}
+
+void VisualScriptTypeCast::set_base_type(const StringName& p_type) {
+
+ if (base_type==p_type)
+ return;
+
+ base_type=p_type;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptTypeCast::get_base_type() const{
+
+ return base_type;
+}
+
+void VisualScriptTypeCast::set_base_script(const String& p_path){
+
+ if (script==p_path)
+ return;
+
+ script=p_path;
+ _change_notify();
+ ports_changed_notify();
+
+}
+String VisualScriptTypeCast::get_base_script() const{
+
+ return script;
+}
+
+
+class VisualScriptNodeInstanceTypeCast : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ StringName base_type;
+ String script;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ Object *obj = *p_inputs[0];
+
+ *p_outputs[0]=Variant();
+
+ if (!obj) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Instance is null";
+ return 0;
+ }
+
+ if (script!=String()) {
+
+ Ref<Script> obj_script = obj->get_script();
+ if (!obj_script.is_valid()) {
+ return 1; //well, definitely not the script because object we got has no script.
+ }
+
+ if (!ResourceCache::has(script)) {
+ //if the script is not in use by anyone, we can safely assume whathever we got is not casting to it.
+ return 1;
+ }
+ Ref<Script> cast_script = Ref<Resource>(ResourceCache::get(script));
+ if (!cast_script.is_valid()) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Script path is not a script: "+script;
+ return 1;
+ }
+
+ while(obj_script.is_valid()) {
+
+ if (cast_script==obj_script) {
+ *p_outputs[0]=*p_inputs[0]; //copy
+ return 0; // it is the script, yey
+ }
+
+ obj_script=obj_script->get_base_script();
+ }
+
+ return 1; //not found sorry
+ }
+
+ if (ObjectTypeDB::is_type(obj->get_type_name(),base_type)) {
+ *p_outputs[0]=*p_inputs[0]; //copy
+ return 0;
+ } else
+ return 1;
+
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptTypeCast::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceTypeCast * instance = memnew(VisualScriptNodeInstanceTypeCast );
+ instance->instance=p_instance;
+ instance->base_type=base_type;
+ instance->script=script;
+ return instance;
+}
+
+
+
+void VisualScriptTypeCast::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","type"),&VisualScriptTypeCast::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptTypeCast::get_base_type);
+
+ ObjectTypeDB::bind_method(_MD("set_base_script","path"),&VisualScriptTypeCast::set_base_script);
+ ObjectTypeDB::bind_method(_MD("get_base_script"),&VisualScriptTypeCast::get_base_script);
+
+
+ List<String> script_extensions;
+ for(int i=0;i>ScriptServer::get_language_count();i++) {
+ ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
+ }
+
+ String script_ext_hint;
+ for (List<String>::Element *E=script_extensions.front();E;E=E->next()) {
+ if (script_ext_hint!=String())
+ script_ext_hint+=",";
+ script_ext_hint+="*."+E->get();
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_script",PROPERTY_HINT_FILE,script_ext_hint),_SCS("set_base_script"),_SCS("get_base_script"));
+
+}
+
+VisualScriptTypeCast::VisualScriptTypeCast() {
+
+ base_type="Object";
+}
+
+
+void register_visual_script_flow_control_nodes() {
+
+ VisualScriptLanguage::singleton->add_register_func("flow_control/return",create_return_node<false>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/return_with_value",create_return_node<true>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/condition",create_node_generic<VisualScriptCondition>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/while",create_node_generic<VisualScriptWhile>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/iterator",create_node_generic<VisualScriptIterator>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/sequence",create_node_generic<VisualScriptSequence>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/switch",create_node_generic<VisualScriptSwitch>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/input_filter",create_node_generic<VisualScriptInputFilter>);
+ VisualScriptLanguage::singleton->add_register_func("flow_control/type_cast",create_node_generic<VisualScriptTypeCast>);
+
+
+
+}
diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h
new file mode 100644
index 0000000000..e0da84a534
--- /dev/null
+++ b/modules/visual_script/visual_script_flow_control.h
@@ -0,0 +1,341 @@
+#ifndef VISUAL_SCRIPT_FLOW_CONTROL_H
+#define VISUAL_SCRIPT_FLOW_CONTROL_H
+
+#include "visual_script.h"
+
+class VisualScriptReturn : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptReturn,VisualScriptNode)
+
+
+ Variant::Type type;
+ bool with_value;
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ void set_return_type(Variant::Type);
+ Variant::Type get_return_type() const;
+
+ void set_enable_return_value(bool p_enable);
+ bool is_return_value_enabled() const;
+
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptReturn();
+};
+
+
+class VisualScriptCondition : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptCondition,VisualScriptNode)
+
+
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptCondition();
+};
+
+
+class VisualScriptWhile : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptWhile,VisualScriptNode)
+
+
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptWhile();
+};
+
+
+
+class VisualScriptIterator : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptIterator,VisualScriptNode)
+
+
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptIterator();
+};
+
+
+
+class VisualScriptSequence : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSequence,VisualScriptNode)
+
+
+ int steps;
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ void set_steps(int p_steps);
+ int get_steps() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptSequence();
+};
+
+
+
+
+class VisualScriptSwitch : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSwitch,VisualScriptNode)
+
+ struct Case {
+ Variant::Type type;
+ Case() { type=Variant::NIL; }
+ };
+
+ Vector<Case> case_values;
+
+friend class VisualScriptNodeInstanceSwitch;
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+ virtual bool has_mixed_input_and_sequence_ports() const { return true; }
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+
+ VisualScriptSwitch();
+};
+
+
+
+
+class VisualScriptInputFilter : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptInputFilter,VisualScriptNode)
+
+ Vector<InputEvent> filters;
+
+
+protected:
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+
+ VisualScriptInputFilter();
+};
+
+
+
+
+
+class VisualScriptTypeCast : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptTypeCast,VisualScriptNode)
+
+
+ StringName base_type;
+ String script;
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ void set_base_type(const StringName& p_type);
+ StringName get_base_type() const;
+
+ void set_base_script(const String& p_path);
+ String get_base_script() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+
+ VisualScriptTypeCast();
+};
+
+
+
+
+void register_visual_script_flow_control_nodes();
+
+
+
+#endif // VISUAL_SCRIPT_FLOW_CONTROL_H
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
new file mode 100644
index 0000000000..de99beacaf
--- /dev/null
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -0,0 +1,2520 @@
+#include "visual_script_func_nodes.h"
+#include "scene/main/scene_main_loop.h"
+#include "os/os.h"
+#include "scene/main/node.h"
+#include "visual_script_nodes.h"
+#include "io/resource_loader.h"
+#include "globals.h"
+
+//////////////////////////////////////////
+////////////////CALL//////////////////////
+//////////////////////////////////////////
+
+int VisualScriptFunctionCall::get_output_sequence_port_count() const {
+
+ if (method_cache.flags&METHOD_FLAG_CONST || call_mode==CALL_MODE_BASIC_TYPE)
+ return 0;
+ else
+ return 1;
+}
+
+bool VisualScriptFunctionCall::has_input_sequence_port() const{
+
+ if (method_cache.flags&METHOD_FLAG_CONST || call_mode==CALL_MODE_BASIC_TYPE)
+ return false;
+ else
+ return true;
+}
+#ifdef TOOLS_ENABLED
+
+static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) {
+
+ if (p_edited_scene!=p_current_node && p_current_node->get_owner()!=p_edited_scene)
+ return NULL;
+
+ Ref<Script> scr = p_current_node->get_script();
+
+ if (scr.is_valid() && scr==script)
+ return p_current_node;
+
+ for(int i=0;i<p_current_node->get_child_count();i++) {
+ Node *n = _find_script_node(p_edited_scene,p_current_node->get_child(i),script);
+ if (n)
+ return n;
+ }
+
+ return NULL;
+}
+
+#endif
+Node *VisualScriptFunctionCall::_get_base_node() const {
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return NULL;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return NULL;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return NULL;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return NULL;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return NULL;
+
+ if (!script_node->has_node(base_path))
+ return NULL;
+
+ Node *path_to = script_node->get_node(base_path);
+
+ return path_to;
+#else
+
+ return NULL;
+#endif
+}
+
+StringName VisualScriptFunctionCall::_get_base_type() const {
+
+ if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid())
+ return get_visual_script()->get_instance_base_type();
+ else if (call_mode==CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
+ Node *path = _get_base_node();
+ if (path)
+ return path->get_type();
+
+ }
+
+ return base_type;
+}
+
+
+int VisualScriptFunctionCall::get_input_value_port_count() const{
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+
+ Vector<StringName> names = Variant::get_method_argument_names(basic_type,function);
+ return names.size() + (rpc_call_mode>=RPC_RELIABLE_TO_ID?1:0) + 1;
+
+ } else {
+
+ MethodBind *mb = ObjectTypeDB::get_method(_get_base_type(),function);
+ if (mb) {
+ return mb->get_argument_count() + (call_mode==CALL_MODE_INSTANCE?1:0) + (rpc_call_mode>=RPC_RELIABLE_TO_ID?1:0) - use_default_args;
+ }
+
+ return method_cache.arguments.size() + (call_mode==CALL_MODE_INSTANCE?1:0) + (rpc_call_mode>=RPC_RELIABLE_TO_ID?1:0) - use_default_args;
+ }
+
+}
+int VisualScriptFunctionCall::get_output_value_port_count() const{
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ bool returns=false;
+ Variant::get_method_return_type(basic_type,function,&returns);
+ return returns?1:0;
+
+ } else {
+ int ret;
+ MethodBind *mb = ObjectTypeDB::get_method(_get_base_type(),function);
+ if (mb) {
+ ret = mb->has_return() ? 1 : 0;
+ } else
+ ret = 1; //it is assumed that script always returns something
+
+ if (call_mode==CALL_MODE_INSTANCE) {
+ ret++;
+ }
+
+ return ret;
+ }
+}
+
+String VisualScriptFunctionCall::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptFunctionCall::get_input_value_port_info(int p_idx) const{
+
+ if (call_mode==CALL_MODE_INSTANCE || call_mode==CALL_MODE_BASIC_TYPE) {
+ if (p_idx==0) {
+ PropertyInfo pi;
+ pi.type=(call_mode==CALL_MODE_INSTANCE?Variant::OBJECT:basic_type);
+ pi.name=(call_mode==CALL_MODE_INSTANCE?String("instance"):Variant::get_type_name(basic_type).to_lower());
+ return pi;
+ } else {
+ p_idx--;
+ }
+ }
+
+ if (rpc_call_mode>=RPC_RELIABLE_TO_ID) {
+
+ if (p_idx==0) {
+ return PropertyInfo(Variant::INT,"peer_id");
+ } else {
+ p_idx--;
+ }
+
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+
+ Vector<StringName> names = Variant::get_method_argument_names(basic_type,function);
+ Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type,function);
+ return PropertyInfo(types[p_idx],names[p_idx]);
+
+ } else {
+
+ MethodBind *mb = ObjectTypeDB::get_method(_get_base_type(),function);
+ if (mb) {
+ return mb->get_argument_info(p_idx);
+ }
+
+ if (p_idx>=0 && p_idx < method_cache.arguments.size()) {
+ return method_cache.arguments[p_idx];
+ }
+
+ return PropertyInfo();
+ }
+#else
+ return PropertyInfo();
+#endif
+
+}
+
+PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) const{
+
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+
+ return PropertyInfo(Variant::get_method_return_type(basic_type,function),"");
+ } else {
+
+ if (call_mode==CALL_MODE_INSTANCE) {
+ if (p_idx==0) {
+ return PropertyInfo(Variant::OBJECT,"pass");
+ } else {
+ p_idx--;
+ }
+ }
+
+ PropertyInfo ret;
+
+ /*MethodBind *mb = ObjectTypeDB::get_method(_get_base_type(),function);
+ if (mb) {
+
+ ret = mb->get_argument_info(-1);
+ } else {*/
+
+ ret = method_cache.return_val;
+
+ //}
+
+ if (call_mode==CALL_MODE_INSTANCE) {
+ ret.name="return";
+ } else {
+ ret.name="";
+ }
+ return ret;
+
+
+ }
+#else
+ return PropertyInfo();
+#endif
+}
+
+
+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)
+ return String(singleton)+":"+String(function)+"()";
+ else if (call_mode==CALL_MODE_BASIC_TYPE)
+ return Variant::get_type_name(basic_type)+"."+String(function)+"()";
+ else if (call_mode==CALL_MODE_NODE_PATH)
+ return " ["+String(base_path.simplified())+"]."+String(function)+"()";
+ else
+ return " "+base_type+"."+String(function)+"()";
+
+}
+
+
+void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
+
+ if (basic_type==p_type)
+ return;
+ basic_type=p_type;
+
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptFunctionCall::get_basic_type() const{
+
+ return basic_type;
+}
+
+void VisualScriptFunctionCall::set_base_type(const StringName& p_type) {
+
+ if (base_type==p_type)
+ return;
+
+ base_type=p_type;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptFunctionCall::get_base_type() const{
+
+ return base_type;
+}
+
+void VisualScriptFunctionCall::set_base_script(const String& p_path) {
+
+ if (base_script==p_path)
+ return;
+
+ base_script=p_path;
+ _change_notify();
+ ports_changed_notify();
+}
+
+String VisualScriptFunctionCall::get_base_script() const {
+
+ return base_script;
+}
+
+void VisualScriptFunctionCall::set_singleton(const StringName& p_path) {
+
+ if (singleton==p_path)
+ return;
+
+ singleton=p_path;
+ Object *obj = Globals::get_singleton()->get_singleton_object(singleton);
+ if (obj) {
+ base_type=obj->get_type();
+ }
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptFunctionCall::get_singleton() const {
+
+ return singleton;
+}
+
+
+
+void VisualScriptFunctionCall::_update_method_cache() {
+ StringName type;
+ Ref<Script> script;
+
+ if (call_mode==CALL_MODE_NODE_PATH) {
+
+ Node* node=_get_base_node();
+ if (node) {
+ type=node->get_type();
+ base_type=type; //cache, too
+ script = node->get_script();
+ }
+ } else if (call_mode==CALL_MODE_SELF) {
+
+ if (get_visual_script().is_valid()) {
+ type=get_visual_script()->get_instance_base_type();
+ base_type=type; //cache, too
+ script=get_visual_script();
+ }
+
+ } else if (call_mode==CALL_MODE_SINGLETON) {
+
+ Object *obj = Globals::get_singleton()->get_singleton_object(singleton);
+ if (obj) {
+ type=obj->get_type();
+ script=obj->get_script();
+ }
+
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+
+ type=base_type;
+ if (base_script!=String()) {
+
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ script = Ref<Resource>( ResourceCache::get(base_script) );
+ } else {
+ return;
+ }
+ }
+ }
+
+
+// print_line("BASE: "+String(type)+" FUNC: "+String(function));
+ MethodBind *mb = ObjectTypeDB::get_method(type,function);
+ if (mb) {
+ use_default_args=mb->get_default_argument_count();
+ method_cache = MethodInfo();
+ for(int i=0;i<mb->get_argument_count();i++) {
+#ifdef DEBUG_METHODS_ENABLED
+ method_cache.arguments.push_back(mb->get_argument_info(i));
+#else
+ method_cache.arguments.push_back(PropertyInfo());
+#endif
+ }
+
+ if (mb->is_const()) {
+ method_cache.flags|=METHOD_FLAG_CONST;
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ method_cache.return_val = mb->get_argument_info(-1);
+#endif
+ } else if (script.is_valid() && script->has_method(function)) {
+
+ method_cache = script->get_method_info(function);
+ use_default_args=method_cache.default_arguments.size();
+ }
+}
+
+void VisualScriptFunctionCall::set_function(const StringName& p_type){
+
+ if (function==p_type)
+ return;
+
+ function=p_type;
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+ use_default_args = Variant::get_method_default_arguments(basic_type,function).size();
+ } else {
+ //update all caches
+
+ _update_method_cache();
+
+ }
+
+
+ _change_notify();
+ ports_changed_notify();
+}
+StringName VisualScriptFunctionCall::get_function() const {
+
+
+ return function;
+}
+
+void VisualScriptFunctionCall::set_base_path(const NodePath& p_type) {
+
+ if (base_path==p_type)
+ return;
+
+ base_path=p_type;
+ _change_notify();
+ ports_changed_notify();
+}
+
+NodePath VisualScriptFunctionCall::get_base_path() const {
+
+ return base_path;
+}
+
+
+void VisualScriptFunctionCall::set_call_mode(CallMode p_mode) {
+
+ if (call_mode==p_mode)
+ return;
+
+ call_mode=p_mode;
+ _change_notify();
+ ports_changed_notify();
+
+}
+VisualScriptFunctionCall::CallMode VisualScriptFunctionCall::get_call_mode() const {
+
+ return call_mode;
+}
+
+void VisualScriptFunctionCall::set_use_default_args(int p_amount) {
+
+ if (use_default_args==p_amount)
+ return;
+
+ use_default_args=p_amount;
+ ports_changed_notify();
+
+
+}
+
+void VisualScriptFunctionCall::set_rpc_call_mode(VisualScriptFunctionCall::RPCCallMode p_mode) {
+
+ if (rpc_call_mode==p_mode)
+ return;
+ rpc_call_mode=p_mode;
+ ports_changed_notify();
+ _change_notify();
+}
+
+VisualScriptFunctionCall::RPCCallMode VisualScriptFunctionCall::get_rpc_call_mode() const{
+
+ return rpc_call_mode;
+}
+
+
+int VisualScriptFunctionCall::get_use_default_args() const{
+
+ return use_default_args;
+}
+
+
+void VisualScriptFunctionCall::set_validate(bool p_amount) {
+
+ validate=p_amount;
+}
+
+bool VisualScriptFunctionCall::get_validate() const {
+
+ return validate;
+}
+
+
+void VisualScriptFunctionCall::_set_argument_cache(const Dictionary& p_cache) {
+ //so everything works in case all else fails
+ method_cache=MethodInfo::from_dict(p_cache);
+
+}
+
+Dictionary VisualScriptFunctionCall::_get_argument_cache() const {
+
+ return method_cache;
+}
+
+void VisualScriptFunctionCall::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="function/base_type") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
+ if (property.name=="function/base_script") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="function/basic_type") {
+ if (call_mode!=CALL_MODE_BASIC_TYPE) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="function/singleton") {
+ if (call_mode!=CALL_MODE_SINGLETON) {
+ property.usage=0;
+ } else {
+ List<Globals::Singleton> names;
+ Globals::get_singleton()->get_singletons(&names);
+ property.hint=PROPERTY_HINT_ENUM;
+ String sl;
+ for (List<Globals::Singleton>::Element *E=names.front();E;E=E->next()) {
+ if (sl!=String())
+ sl+=",";
+ sl+=E->get().name;
+ }
+ property.hint_string=sl;
+
+ }
+ }
+
+ if (property.name=="function/node_path") {
+ if (call_mode!=CALL_MODE_NODE_PATH) {
+ property.usage=0;
+ } else {
+
+ Node *bnode = _get_base_node();
+ if (bnode) {
+ property.hint_string=bnode->get_path(); //convert to loong string
+ } else {
+
+ }
+ }
+ }
+
+ if (property.name=="function/function") {
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ property.hint=PROPERTY_HINT_METHOD_OF_VARIANT_TYPE;
+ property.hint_string=Variant::get_type_name(basic_type);
+
+ } else if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid()) {
+ property.hint=PROPERTY_HINT_METHOD_OF_SCRIPT;
+ property.hint_string=itos(get_visual_script()->get_instance_ID());
+ } else if (call_mode==CALL_MODE_SINGLETON) {
+
+ Object *obj = Globals::get_singleton()->get_singleton_object(singleton);
+ if (obj) {
+ property.hint=PROPERTY_HINT_METHOD_OF_INSTANCE;
+ property.hint_string=itos(obj->get_instance_ID());
+ } else {
+
+ property.hint=PROPERTY_HINT_METHOD_OF_BASE_TYPE;
+ property.hint_string=base_type;//should be cached
+ }
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+ property.hint=PROPERTY_HINT_METHOD_OF_BASE_TYPE;
+ property.hint_string=base_type;
+
+ if (base_script!=String()) {
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ Ref<Script> script = Ref<Resource>( ResourceCache::get(base_script) );
+ if (script.is_valid()) {
+
+ property.hint=PROPERTY_HINT_METHOD_OF_SCRIPT;
+ property.hint_string=itos(script->get_instance_ID());
+ }
+ }
+ }
+
+ } else if (call_mode==CALL_MODE_NODE_PATH) {
+ Node *node = _get_base_node();
+ if (node) {
+ property.hint=PROPERTY_HINT_METHOD_OF_INSTANCE;
+ property.hint_string=itos(node->get_instance_ID());
+ } else {
+ property.hint=PROPERTY_HINT_METHOD_OF_BASE_TYPE;
+ property.hint_string=get_base_type();
+ }
+
+ }
+
+ }
+
+ if (property.name=="function/use_default_args") {
+
+ property.hint=PROPERTY_HINT_RANGE;
+
+ int mc=0;
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ mc = Variant::get_method_default_arguments(basic_type,function).size();
+ } else {
+ MethodBind *mb = ObjectTypeDB::get_method(_get_base_type(),function);
+ if (mb) {
+
+ mc=mb->get_default_argument_count();
+ }
+ }
+
+ if (mc==0) {
+ property.usage=0; //do not show
+ } else {
+
+ property.hint_string="0,"+itos(mc)+",1";
+ }
+ }
+
+ if (property.name=="rpc/call_mode") {
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+ property.usage=0;
+ }
+ }
+
+}
+
+
+void VisualScriptFunctionCall::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","base_type"),&VisualScriptFunctionCall::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptFunctionCall::get_base_type);
+
+ ObjectTypeDB::bind_method(_MD("set_base_script","base_script"),&VisualScriptFunctionCall::set_base_script);
+ ObjectTypeDB::bind_method(_MD("get_base_script"),&VisualScriptFunctionCall::get_base_script);
+
+ ObjectTypeDB::bind_method(_MD("set_basic_type","basic_type"),&VisualScriptFunctionCall::set_basic_type);
+ ObjectTypeDB::bind_method(_MD("get_basic_type"),&VisualScriptFunctionCall::get_basic_type);
+
+ ObjectTypeDB::bind_method(_MD("set_singleton","singleton"),&VisualScriptFunctionCall::set_singleton);
+ ObjectTypeDB::bind_method(_MD("get_singleton"),&VisualScriptFunctionCall::get_singleton);
+
+ ObjectTypeDB::bind_method(_MD("set_function","function"),&VisualScriptFunctionCall::set_function);
+ ObjectTypeDB::bind_method(_MD("get_function"),&VisualScriptFunctionCall::get_function);
+
+ ObjectTypeDB::bind_method(_MD("set_call_mode","mode"),&VisualScriptFunctionCall::set_call_mode);
+ ObjectTypeDB::bind_method(_MD("get_call_mode"),&VisualScriptFunctionCall::get_call_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_base_path","base_path"),&VisualScriptFunctionCall::set_base_path);
+ ObjectTypeDB::bind_method(_MD("get_base_path"),&VisualScriptFunctionCall::get_base_path);
+
+ ObjectTypeDB::bind_method(_MD("set_use_default_args","amount"),&VisualScriptFunctionCall::set_use_default_args);
+ ObjectTypeDB::bind_method(_MD("get_use_default_args"),&VisualScriptFunctionCall::get_use_default_args);
+
+ ObjectTypeDB::bind_method(_MD("_set_argument_cache","argument_cache"),&VisualScriptFunctionCall::_set_argument_cache);
+ ObjectTypeDB::bind_method(_MD("_get_argument_cache"),&VisualScriptFunctionCall::_get_argument_cache);
+
+ ObjectTypeDB::bind_method(_MD("set_rpc_call_mode","mode"),&VisualScriptFunctionCall::set_rpc_call_mode);
+ ObjectTypeDB::bind_method(_MD("get_rpc_call_mode"),&VisualScriptFunctionCall::get_rpc_call_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_validate","enable"),&VisualScriptFunctionCall::set_validate);
+ ObjectTypeDB::bind_method(_MD("get_validate"),&VisualScriptFunctionCall::get_validate);
+
+ String bt;
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ if (i>0)
+ bt+=",";
+
+ bt+=Variant::get_type_name(Variant::Type(i));
+ }
+
+
+ List<String> script_extensions;
+ for(int i=0;i<ScriptServer::get_language_count();i++) {
+ ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
+ }
+
+ String script_ext_hint;
+ for (List<String>::Element *E=script_extensions.front();E;E=E->next()) {
+ if (script_ext_hint!=String())
+ script_ext_hint+=",";
+ script_ext_hint+="*."+E->get();
+ }
+
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"function/call_mode",PROPERTY_HINT_ENUM,"Self,Node Path,Instance,Basic Type,Singleton"),_SCS("set_call_mode"),_SCS("get_call_mode"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/base_script",PROPERTY_HINT_FILE,script_ext_hint),_SCS("set_base_script"),_SCS("get_base_script"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/singleton"),_SCS("set_singleton"),_SCS("get_singleton"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"function/basic_type",PROPERTY_HINT_ENUM,bt),_SCS("set_basic_type"),_SCS("get_basic_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"function/node_path",PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE),_SCS("set_base_path"),_SCS("get_base_path"));
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY,"function/argument_cache",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_argument_cache"),_SCS("_get_argument_cache"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"function/function"),_SCS("set_function"),_SCS("get_function")); //when set, if loaded properly, will override argument count.
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"function/use_default_args"),_SCS("set_use_default_args"),_SCS("get_use_default_args"));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"function/validate"),_SCS("set_validate"),_SCS("get_validate"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"rpc/call_mode",PROPERTY_HINT_ENUM,"Disabled,Reliable,Unreliable,ReliableToID,UnreliableToID"),_SCS("set_rpc_call_mode"),_SCS("get_rpc_call_mode")); //when set, if loaded properly, will override argument count.
+
+ BIND_CONSTANT( CALL_MODE_SELF );
+ BIND_CONSTANT( CALL_MODE_NODE_PATH);
+ BIND_CONSTANT( CALL_MODE_INSTANCE);
+ BIND_CONSTANT( CALL_MODE_BASIC_TYPE );
+}
+
+class VisualScriptNodeInstanceFunctionCall : public VisualScriptNodeInstance {
+public:
+
+
+ VisualScriptFunctionCall::CallMode call_mode;
+ NodePath node_path;
+ int input_args;
+ bool validate;
+ bool returns;
+ VisualScriptFunctionCall::RPCCallMode rpc_mode;
+ StringName function;
+ StringName singleton;
+
+ VisualScriptFunctionCall *node;
+ VisualScriptInstance *instance;
+
+
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+
+ _FORCE_INLINE_ bool call_rpc(Object* p_base,const Variant** p_args,int p_argcount) {
+
+ if (!p_base)
+ return false;
+
+ Node * node = p_base->cast_to<Node>();
+ if (!node)
+ return false;
+
+ int to_id=0;
+ bool reliable=true;
+
+ if (rpc_mode>=VisualScriptFunctionCall::RPC_RELIABLE_TO_ID) {
+ to_id = *p_args[0];
+ p_args+=1;
+ p_argcount-=1;
+ if (rpc_mode==VisualScriptFunctionCall::RPC_UNRELIABLE_TO_ID) {
+ reliable=false;
+ }
+ } else if (rpc_mode==VisualScriptFunctionCall::RPC_UNRELIABLE) {
+ reliable=false;
+ }
+
+ node->rpcp(to_id,!reliable,function,p_args,p_argcount);
+
+ return true;
+ }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+ switch(call_mode) {
+
+ case VisualScriptFunctionCall::CALL_MODE_SELF: {
+
+ Object *object=instance->get_owner_ptr();
+
+ if (rpc_mode) {
+ call_rpc(object,p_inputs,input_args);
+ } else if (returns) {
+ *p_outputs[0] = object->call(function,p_inputs,input_args,r_error);
+ } else {
+ object->call(function,p_inputs,input_args,r_error);
+ }
+ } break;
+ case VisualScriptFunctionCall::CALL_MODE_NODE_PATH: {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Base object is not a Node!";
+ return 0;
+ }
+
+ Node* another = node->get_node(node_path);
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Path does not lead Node!";
+ return 0;
+ }
+
+ if (rpc_mode) {
+ call_rpc(node,p_inputs,input_args);
+ } else if (returns) {
+ *p_outputs[0] = another->call(function,p_inputs,input_args,r_error);
+ } else {
+ another->call(function,p_inputs,input_args,r_error);
+ }
+
+ } break;
+ case VisualScriptFunctionCall::CALL_MODE_INSTANCE:
+ case VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE: {
+
+ Variant v = *p_inputs[0];
+
+ if (rpc_mode) {
+ Object *obj = v;
+ if (obj) {
+ call_rpc(obj,p_inputs+1,input_args-1);
+ }
+ } else if (returns) {
+ if (call_mode==VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
+ *p_outputs[1] = v.call(function,p_inputs+1,input_args,r_error);
+ } else {
+ *p_outputs[0] = v.call(function,p_inputs+1,input_args,r_error);
+ }
+ } else {
+ v.call(function,p_inputs+1,input_args,r_error);
+ }
+
+ if (call_mode==VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
+ *p_outputs[0]=*p_inputs[0];
+ }
+
+ } break;
+ case VisualScriptFunctionCall::CALL_MODE_SINGLETON: {
+
+ Object *object=Globals::get_singleton()->get_singleton_object(singleton);
+ if (!object) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid singleton name: '"+String(singleton)+"'";
+ return 0;
+ }
+
+ if (rpc_mode) {
+ call_rpc(object,p_inputs,input_args);
+ } else if (returns) {
+ *p_outputs[0] = object->call(function,p_inputs,input_args,r_error);
+ } else {
+ object->call(function,p_inputs,input_args,r_error);
+ }
+ } break;
+
+ }
+
+ if (!validate) {
+
+ //ignore call errors if validation is disabled
+ r_error.error=Variant::CallError::CALL_OK;
+ r_error_str=String();
+ }
+
+ return 0;
+
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptFunctionCall::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceFunctionCall * instance = memnew(VisualScriptNodeInstanceFunctionCall );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->singleton=singleton;
+ instance->function=function;
+ instance->call_mode=call_mode;
+ instance->returns=get_output_value_port_count();
+ instance->node_path=base_path;
+ instance->input_args = get_input_value_port_count() - ( (call_mode==CALL_MODE_BASIC_TYPE || call_mode==CALL_MODE_INSTANCE) ? 1: 0 );
+ instance->rpc_mode=rpc_call_mode;
+ instance->validate=validate;
+ return instance;
+}
+
+
+VisualScriptFunctionCall::TypeGuess VisualScriptFunctionCall::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+ if (p_output==0 && call_mode==CALL_MODE_INSTANCE) {
+ return p_inputs[0];
+ }
+
+ return VisualScriptNode::guess_output_type(p_inputs,p_output);
+
+}
+
+VisualScriptFunctionCall::VisualScriptFunctionCall() {
+
+ validate=true;
+ call_mode=CALL_MODE_SELF;
+ basic_type=Variant::NIL;
+ use_default_args=0;
+ base_type="Object";
+ rpc_call_mode=RPC_DISABLED;
+
+
+}
+
+template<VisualScriptFunctionCall::CallMode cmode>
+static Ref<VisualScriptNode> create_function_call_node(const String& p_name) {
+
+ Ref<VisualScriptFunctionCall> node;
+ node.instance();
+ node->set_call_mode(cmode);
+ return node;
+}
+
+
+//////////////////////////////////////////
+////////////////SET//////////////////////
+//////////////////////////////////////////
+
+static const char* event_type_names[InputEvent::TYPE_MAX]={
+ "None",
+ "Key",
+ "MouseMotion",
+ "MouseButton",
+ "JoystickMotion",
+ "JoystickButton",
+ "ScreenTouch",
+ "ScreenDrag",
+ "Action"
+};
+
+
+int VisualScriptPropertySet::get_output_sequence_port_count() const {
+
+ return call_mode!=CALL_MODE_BASIC_TYPE ? 1 : 0;
+}
+
+bool VisualScriptPropertySet::has_input_sequence_port() const{
+
+ return call_mode!=CALL_MODE_BASIC_TYPE ? true : false;
+}
+
+Node *VisualScriptPropertySet::_get_base_node() const {
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return NULL;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return NULL;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return NULL;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return NULL;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return NULL;
+
+ if (!script_node->has_node(base_path))
+ return NULL;
+
+ Node *path_to = script_node->get_node(base_path);
+
+ return path_to;
+#else
+
+ return NULL;
+#endif
+}
+
+StringName VisualScriptPropertySet::_get_base_type() const {
+
+ if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid())
+ return get_visual_script()->get_instance_base_type();
+ else if (call_mode==CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
+ Node *path = _get_base_node();
+ if (path)
+ return path->get_type();
+
+ }
+
+ return base_type;
+}
+
+int VisualScriptPropertySet::get_input_value_port_count() const{
+
+ int pc = (call_mode==CALL_MODE_BASIC_TYPE || call_mode==CALL_MODE_INSTANCE)?2:1;
+
+ return pc;
+}
+int VisualScriptPropertySet::get_output_value_port_count() const{
+
+ return (call_mode==CALL_MODE_BASIC_TYPE || call_mode==CALL_MODE_INSTANCE) ? 1 : 0;
+}
+
+String VisualScriptPropertySet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const{
+
+ if (call_mode==CALL_MODE_INSTANCE || call_mode==CALL_MODE_BASIC_TYPE) {
+ if (p_idx==0) {
+ PropertyInfo pi;
+ pi.type=(call_mode==CALL_MODE_INSTANCE?Variant::OBJECT:basic_type);
+ pi.name=(call_mode==CALL_MODE_INSTANCE?String("instance"):Variant::get_type_name(basic_type).to_lower());
+ return pi;
+ } else {
+ p_idx--;
+ }
+ }
+
+ PropertyInfo pinfo=type_cache;
+ pinfo.name="value";
+ return pinfo;
+}
+
+PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) const{
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+ return PropertyInfo(basic_type,"out");
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+ return PropertyInfo(Variant::OBJECT,"pass");
+ } else {
+ return PropertyInfo();
+ }
+
+}
+
+
+String VisualScriptPropertySet::get_caption() const {
+
+ static const char*cname[4]= {
+ "SelfSet",
+ "NodeSet",
+ "InstanceSet",
+ "BasicSet"
+ };
+
+ return cname[call_mode];
+}
+
+String VisualScriptPropertySet::get_text() const {
+
+ String prop;
+
+ 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;
+
+ return prop;
+
+}
+
+void VisualScriptPropertySet::_update_base_type() {
+ //cache it because this information may not be available on load
+ if (call_mode==CALL_MODE_NODE_PATH) {
+
+ Node* node=_get_base_node();
+ if (node) {
+ base_type=node->get_type();
+ }
+ } else if (call_mode==CALL_MODE_SELF) {
+
+ if (get_visual_script().is_valid()) {
+ base_type=get_visual_script()->get_instance_base_type();
+ }
+ }
+
+}
+void VisualScriptPropertySet::set_basic_type(Variant::Type p_type) {
+
+ if (basic_type==p_type)
+ return;
+ basic_type=p_type;
+
+
+ _change_notify();
+ _update_base_type();
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptPropertySet::get_basic_type() const{
+
+ return basic_type;
+}
+
+void VisualScriptPropertySet::set_event_type(InputEvent::Type p_type) {
+
+ if (event_type==p_type)
+ return;
+ event_type=p_type;
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+ _update_cache();
+ }
+ _change_notify();
+ _update_base_type();
+ ports_changed_notify();
+}
+
+InputEvent::Type VisualScriptPropertySet::get_event_type() const{
+
+ return event_type;
+}
+
+
+void VisualScriptPropertySet::set_base_type(const StringName& p_type) {
+
+ if (base_type==p_type)
+ return;
+
+ base_type=p_type;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptPropertySet::get_base_type() const{
+
+ return base_type;
+}
+
+
+void VisualScriptPropertySet::set_base_script(const String& p_path) {
+
+ if (base_script==p_path)
+ return;
+
+ base_script=p_path;
+ _change_notify();
+ ports_changed_notify();
+}
+
+String VisualScriptPropertySet::get_base_script() const {
+
+ return base_script;
+}
+
+
+void VisualScriptPropertySet::_update_cache() {
+
+
+ if (!OS::get_singleton()->get_main_loop())
+ return;
+ if (!OS::get_singleton()->get_main_loop()->cast_to<SceneTree>())
+ return;
+
+ if (!OS::get_singleton()->get_main_loop()->cast_to<SceneTree>()->is_editor_hint()) //only update cache if editor exists, it's pointless otherwise
+ return;
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ //not super efficient..
+
+ Variant v;
+ if (basic_type==Variant::INPUT_EVENT) {
+ InputEvent ev;
+ ev.type=event_type;
+ v=ev;
+ } else {
+ Variant::CallError ce;
+ v = Variant::construct(basic_type,NULL,0,ce);
+ }
+
+ List<PropertyInfo> pinfo;
+ v.get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+
+ if (E->get().name==property) {
+
+ type_cache=E->get();
+ }
+ }
+
+ } else {
+
+
+ StringName type;
+ Ref<Script> script;
+ Node *node=NULL;
+
+ if (call_mode==CALL_MODE_NODE_PATH) {
+
+ node=_get_base_node();
+ if (node) {
+ type=node->get_type();
+ base_type=type; //cache, too
+ script = node->get_script();
+ }
+ } else if (call_mode==CALL_MODE_SELF) {
+
+ if (get_visual_script().is_valid()) {
+ type=get_visual_script()->get_instance_base_type();
+ base_type=type; //cache, too
+ script=get_visual_script();
+ }
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+
+ type=base_type;
+ if (base_script!=String()) {
+
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ script = Ref<Resource>( ResourceCache::get(base_script) );
+ } else {
+ return;
+ }
+ }
+ }
+
+ List<PropertyInfo> pinfo;
+
+
+ if (node) {
+
+ node->get_property_list(&pinfo);
+ } else {
+ ObjectTypeDB::get_property_list(type,&pinfo);
+ }
+
+ if (script.is_valid()) {
+
+ script->get_script_property_list(&pinfo);
+ }
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+
+ if (E->get().name==property) {
+ type_cache=E->get();
+ return;
+ }
+ }
+
+ }
+}
+
+void VisualScriptPropertySet::set_property(const StringName& p_type){
+
+ if (property==p_type)
+ return;
+
+ property=p_type;
+ _update_cache();
+ _change_notify();
+ ports_changed_notify();
+}
+StringName VisualScriptPropertySet::get_property() const {
+
+
+ return property;
+}
+
+void VisualScriptPropertySet::set_base_path(const NodePath& p_type) {
+
+ if (base_path==p_type)
+ return;
+
+ base_path=p_type;
+ _update_base_type();
+ _change_notify();
+ ports_changed_notify();
+}
+
+NodePath VisualScriptPropertySet::get_base_path() const {
+
+ return base_path;
+}
+
+
+void VisualScriptPropertySet::set_call_mode(CallMode p_mode) {
+
+ if (call_mode==p_mode)
+ return;
+
+ call_mode=p_mode;
+ _update_base_type();
+ _change_notify();
+ ports_changed_notify();
+
+}
+VisualScriptPropertySet::CallMode VisualScriptPropertySet::get_call_mode() const {
+
+ return call_mode;
+}
+
+
+
+
+void VisualScriptPropertySet::_set_type_cache(const Dictionary &p_type) {
+ type_cache=PropertyInfo::from_dict(p_type);
+}
+
+Dictionary VisualScriptPropertySet::_get_type_cache() const {
+
+ return type_cache;
+}
+
+void VisualScriptPropertySet::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="property/base_type") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
+ if (property.name=="property/base_script") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="property/basic_type") {
+ if (call_mode!=CALL_MODE_BASIC_TYPE) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="property/event_type") {
+ if (call_mode!=CALL_MODE_BASIC_TYPE || basic_type!=Variant::INPUT_EVENT) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="property/node_path") {
+ if (call_mode!=CALL_MODE_NODE_PATH) {
+ property.usage=0;
+ } else {
+
+ Node *bnode = _get_base_node();
+ if (bnode) {
+ property.hint_string=bnode->get_path(); //convert to loong string
+ } else {
+
+ }
+ }
+ }
+
+ if (property.name=="property/property") {
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ property.hint=PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE;
+ property.hint_string=Variant::get_type_name(basic_type);
+
+ } else if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid()) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_SCRIPT;
+ property.hint_string=itos(get_visual_script()->get_instance_ID());
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
+ property.hint_string=base_type;
+
+ if (base_script!=String()) {
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ Ref<Script> script = Ref<Resource>( ResourceCache::get(base_script) );
+ if (script.is_valid()) {
+
+ property.hint=PROPERTY_HINT_PROPERTY_OF_SCRIPT;
+ property.hint_string=itos(script->get_instance_ID());
+ }
+ }
+ }
+
+ } else if (call_mode==CALL_MODE_NODE_PATH) {
+ Node *node = _get_base_node();
+ if (node) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_INSTANCE;
+ property.hint_string=itos(node->get_instance_ID());
+ } else {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
+ property.hint_string=get_base_type();
+ }
+
+ }
+
+ }
+
+}
+
+void VisualScriptPropertySet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","base_type"),&VisualScriptPropertySet::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptPropertySet::get_base_type);
+
+ ObjectTypeDB::bind_method(_MD("set_base_script","base_script"),&VisualScriptPropertySet::set_base_script);
+ ObjectTypeDB::bind_method(_MD("get_base_script"),&VisualScriptPropertySet::get_base_script);
+
+ ObjectTypeDB::bind_method(_MD("set_basic_type","basic_type"),&VisualScriptPropertySet::set_basic_type);
+ ObjectTypeDB::bind_method(_MD("get_basic_type"),&VisualScriptPropertySet::get_basic_type);
+
+ ObjectTypeDB::bind_method(_MD("_set_type_cache","type_cache"),&VisualScriptPropertySet::_set_type_cache);
+ ObjectTypeDB::bind_method(_MD("_get_type_cache"),&VisualScriptPropertySet::_get_type_cache);
+
+ ObjectTypeDB::bind_method(_MD("set_event_type","event_type"),&VisualScriptPropertySet::set_event_type);
+ ObjectTypeDB::bind_method(_MD("get_event_type"),&VisualScriptPropertySet::get_event_type);
+
+ ObjectTypeDB::bind_method(_MD("set_property","property"),&VisualScriptPropertySet::set_property);
+ ObjectTypeDB::bind_method(_MD("get_property"),&VisualScriptPropertySet::get_property);
+
+ ObjectTypeDB::bind_method(_MD("set_call_mode","mode"),&VisualScriptPropertySet::set_call_mode);
+ ObjectTypeDB::bind_method(_MD("get_call_mode"),&VisualScriptPropertySet::get_call_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_base_path","base_path"),&VisualScriptPropertySet::set_base_path);
+ ObjectTypeDB::bind_method(_MD("get_base_path"),&VisualScriptPropertySet::get_base_path);
+
+
+
+ String bt;
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ if (i>0)
+ bt+=",";
+
+ bt+=Variant::get_type_name(Variant::Type(i));
+ }
+
+ String et;
+ for(int i=0;i<InputEvent::TYPE_MAX;i++) {
+ if (i>0)
+ et+=",";
+
+ et+=event_type_names[i];
+ }
+
+ List<String> script_extensions;
+ for(int i=0;i<ScriptServer::get_language_count();i++) {
+ ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
+ }
+
+ String script_ext_hint;
+ for (List<String>::Element *E=script_extensions.front();E;E=E->next()) {
+ if (script_ext_hint!=String())
+ script_ext_hint+=",";
+ script_ext_hint+="*."+E->get();
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/set_mode",PROPERTY_HINT_ENUM,"Self,Node Path,Instance,Basic Type"),_SCS("set_call_mode"),_SCS("get_call_mode"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_script",PROPERTY_HINT_FILE,script_ext_hint),_SCS("set_base_script"),_SCS("get_base_script"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/type_cache",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_type_cache"),_SCS("_get_type_cache"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/basic_type",PROPERTY_HINT_ENUM,bt),_SCS("set_basic_type"),_SCS("get_basic_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/event_type",PROPERTY_HINT_ENUM,et),_SCS("set_event_type"),_SCS("get_event_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"property/node_path",PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE),_SCS("set_base_path"),_SCS("get_base_path"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/property"),_SCS("set_property"),_SCS("get_property"));
+
+ BIND_CONSTANT( CALL_MODE_SELF );
+ BIND_CONSTANT( CALL_MODE_NODE_PATH);
+ BIND_CONSTANT( CALL_MODE_INSTANCE);
+
+}
+
+class VisualScriptNodeInstancePropertySet : public VisualScriptNodeInstance {
+public:
+
+
+ VisualScriptPropertySet::CallMode call_mode;
+ NodePath node_path;
+ StringName property;
+
+ VisualScriptPropertySet *node;
+ VisualScriptInstance *instance;
+
+
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+ switch(call_mode) {
+
+ case VisualScriptPropertySet::CALL_MODE_SELF: {
+
+ Object *object=instance->get_owner_ptr();
+
+ bool valid;
+
+ object->set(property,*p_inputs[0],&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid set value '"+String(*p_inputs[0])+"' on property '"+String(property)+"' of type "+object->get_type();
+ }
+ } break;
+ case VisualScriptPropertySet::CALL_MODE_NODE_PATH: {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Base object is not a Node!";
+ return 0;
+ }
+
+ Node* another = node->get_node(node_path);
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Path does not lead Node!";
+ return 0;
+ }
+
+ bool valid;
+
+ another->set(property,*p_inputs[0],&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid set value '"+String(*p_inputs[0])+"' on property '"+String(property)+"' of type "+another->get_type();
+ }
+
+ } break;
+ case VisualScriptPropertySet::CALL_MODE_INSTANCE:
+ case VisualScriptPropertySet::CALL_MODE_BASIC_TYPE: {
+
+ Variant v = *p_inputs[0];
+
+ bool valid;
+
+ v.set(property,*p_inputs[1],&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid set value '"+String(*p_inputs[1])+"' ("+Variant::get_type_name(p_inputs[1]->get_type())+") on property '"+String(property)+"' of type "+Variant::get_type_name(v.get_type());
+ }
+
+ *p_outputs[0]=v;
+
+ } break;
+
+ }
+ return 0;
+
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptPropertySet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstancePropertySet * instance = memnew(VisualScriptNodeInstancePropertySet );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->property=property;
+ instance->call_mode=call_mode;
+ instance->node_path=base_path;
+ return instance;
+}
+
+
+
+VisualScriptPropertySet::TypeGuess VisualScriptPropertySet::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+ if (p_output==0 && call_mode==CALL_MODE_INSTANCE) {
+ return p_inputs[0];
+ }
+
+ return VisualScriptNode::guess_output_type(p_inputs,p_output);
+
+}
+VisualScriptPropertySet::VisualScriptPropertySet() {
+
+ call_mode=CALL_MODE_SELF;
+ base_type="Object";
+ basic_type=Variant::NIL;
+ event_type=InputEvent::NONE;
+
+}
+
+template<VisualScriptPropertySet::CallMode cmode>
+static Ref<VisualScriptNode> create_property_set_node(const String& p_name) {
+
+ Ref<VisualScriptPropertySet> node;
+ node.instance();
+ node->set_call_mode(cmode);
+ return node;
+}
+
+
+//////////////////////////////////////////
+////////////////GET//////////////////////
+//////////////////////////////////////////
+
+int VisualScriptPropertyGet::get_output_sequence_port_count() const {
+
+ return 0;// (call_mode==CALL_MODE_SELF || call_mode==CALL_MODE_NODE_PATH)?0:1;
+}
+
+bool VisualScriptPropertyGet::has_input_sequence_port() const{
+
+ return false;//(call_mode==CALL_MODE_SELF || call_mode==CALL_MODE_NODE_PATH)?false:true;
+}
+void VisualScriptPropertyGet::_update_base_type() {
+ //cache it because this information may not be available on load
+ if (call_mode==CALL_MODE_NODE_PATH) {
+
+ Node* node=_get_base_node();
+ if (node) {
+ base_type=node->get_type();
+ }
+ } else if (call_mode==CALL_MODE_SELF) {
+
+ if (get_visual_script().is_valid()) {
+ base_type=get_visual_script()->get_instance_base_type();
+ }
+ }
+
+}
+Node *VisualScriptPropertyGet::_get_base_node() const {
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return NULL;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return NULL;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return NULL;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return NULL;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return NULL;
+
+ if (!script_node->has_node(base_path))
+ return NULL;
+
+ Node *path_to = script_node->get_node(base_path);
+
+ return path_to;
+#else
+
+ return NULL;
+#endif
+}
+
+StringName VisualScriptPropertyGet::_get_base_type() const {
+
+ if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid())
+ return get_visual_script()->get_instance_base_type();
+ else if (call_mode==CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
+ Node *path = _get_base_node();
+ if (path)
+ return path->get_type();
+
+ }
+
+ return base_type;
+}
+
+
+int VisualScriptPropertyGet::get_input_value_port_count() const{
+
+ return (call_mode==CALL_MODE_BASIC_TYPE || call_mode==CALL_MODE_INSTANCE)?1:0;
+
+}
+int VisualScriptPropertyGet::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptPropertyGet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const{
+
+ if (call_mode==CALL_MODE_INSTANCE || call_mode==CALL_MODE_BASIC_TYPE) {
+ if (p_idx==0) {
+ PropertyInfo pi;
+ pi.type=(call_mode==CALL_MODE_INSTANCE?Variant::OBJECT:basic_type);
+ pi.name=(call_mode==CALL_MODE_INSTANCE?String("instance"):Variant::get_type_name(basic_type).to_lower());
+ return pi;
+ } else {
+ p_idx--;
+ }
+ }
+ return PropertyInfo();
+
+}
+
+PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type_cache,"value");
+}
+
+
+String VisualScriptPropertyGet::get_caption() const {
+
+ static const char*cname[4]= {
+ "SelfGet",
+ "NodeGet",
+ "InstanceGet",
+ "BasicGet"
+ };
+
+ return cname[call_mode];
+}
+
+String VisualScriptPropertyGet::get_text() const {
+
+ String prop;
+
+ 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;
+
+ return prop;
+}
+
+void VisualScriptPropertyGet::set_base_type(const StringName& p_type) {
+
+ if (base_type==p_type)
+ return;
+
+ base_type=p_type;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptPropertyGet::get_base_type() const{
+
+ return base_type;
+}
+
+void VisualScriptPropertyGet::set_base_script(const String& p_path) {
+
+ if (base_script==p_path)
+ return;
+
+ base_script=p_path;
+ _change_notify();
+ ports_changed_notify();
+}
+
+String VisualScriptPropertyGet::get_base_script() const {
+
+ return base_script;
+}
+
+
+void VisualScriptPropertyGet::_update_cache() {
+
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ //not super efficient..
+
+ Variant v;
+ if (basic_type==Variant::INPUT_EVENT) {
+ InputEvent ev;
+ ev.type=event_type;
+ v=ev;
+ } else {
+ Variant::CallError ce;
+ v = Variant::construct(basic_type,NULL,0,ce);
+ }
+
+ List<PropertyInfo> pinfo;
+ v.get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+
+ if (E->get().name==property) {
+
+ type_cache=E->get().type;
+ return;
+ }
+ }
+
+ } else {
+
+
+ StringName type;
+ Ref<Script> script;
+ Node *node=NULL;
+
+ if (call_mode==CALL_MODE_NODE_PATH) {
+
+ node=_get_base_node();
+ if (node) {
+ type=node->get_type();
+ base_type=type; //cache, too
+ script = node->get_script();
+ }
+ } else if (call_mode==CALL_MODE_SELF) {
+
+ if (get_visual_script().is_valid()) {
+ type=get_visual_script()->get_instance_base_type();
+ base_type=type; //cache, too
+ script=get_visual_script();
+ }
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+
+ type=base_type;
+ if (base_script!=String()) {
+
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ script = Ref<Resource>( ResourceCache::get(base_script) );
+ } else {
+ return;
+ }
+ }
+ }
+
+
+ bool valid=false;
+
+ Variant::Type type_ret;
+
+ type_ret=ObjectTypeDB::get_property_type(base_type,property,&valid);
+
+ if (valid) {
+ type_cache=type_ret;
+ return; //all dandy
+ }
+
+ if (node) {
+
+ Variant prop = node->get(property,&valid);
+ if (valid) {
+ type_cache=prop.get_type();
+ return; //all dandy again
+ }
+ }
+
+ if (script.is_valid()) {
+
+ type_ret=script->get_static_property_type(property,&valid);
+
+ if (valid) {
+ type_cache=type_ret;
+ return; //all dandy
+ }
+ }
+ }
+}
+
+void VisualScriptPropertyGet::set_property(const StringName& p_type){
+
+ if (property==p_type)
+ return;
+
+ property=p_type;
+
+
+ _update_cache();
+ _change_notify();
+ ports_changed_notify();
+}
+StringName VisualScriptPropertyGet::get_property() const {
+
+
+ return property;
+}
+
+void VisualScriptPropertyGet::set_base_path(const NodePath& p_type) {
+
+ if (base_path==p_type)
+ return;
+
+ base_path=p_type;
+ _change_notify();
+ _update_base_type();
+ ports_changed_notify();
+}
+
+NodePath VisualScriptPropertyGet::get_base_path() const {
+
+ return base_path;
+}
+
+
+void VisualScriptPropertyGet::set_call_mode(CallMode p_mode) {
+
+ if (call_mode==p_mode)
+ return;
+
+ call_mode=p_mode;
+ _change_notify();
+ _update_base_type();
+ ports_changed_notify();
+
+}
+VisualScriptPropertyGet::CallMode VisualScriptPropertyGet::get_call_mode() const {
+
+ return call_mode;
+}
+
+
+
+void VisualScriptPropertyGet::set_basic_type(Variant::Type p_type) {
+
+ if (basic_type==p_type)
+ return;
+ basic_type=p_type;
+
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptPropertyGet::get_basic_type() const{
+
+ return basic_type;
+}
+
+
+void VisualScriptPropertyGet::set_event_type(InputEvent::Type p_type) {
+
+ if (event_type==p_type)
+ return;
+ event_type=p_type;
+ if(call_mode==CALL_MODE_BASIC_TYPE) {
+ _update_cache();
+ }
+ _change_notify();
+ _update_base_type();
+ ports_changed_notify();
+}
+
+InputEvent::Type VisualScriptPropertyGet::get_event_type() const{
+
+ return event_type;
+}
+
+
+void VisualScriptPropertyGet::_set_type_cache(Variant::Type p_type) {
+ type_cache=p_type;
+}
+
+Variant::Type VisualScriptPropertyGet::_get_type_cache() const {
+
+ return type_cache;
+}
+
+
+void VisualScriptPropertyGet::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="property/base_type") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
+ if (property.name=="property/base_script") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="property/basic_type") {
+ if (call_mode!=CALL_MODE_BASIC_TYPE) {
+ property.usage=0;
+ }
+ }
+ if (property.name=="property/event_type") {
+ if (call_mode!=CALL_MODE_BASIC_TYPE || basic_type!=Variant::INPUT_EVENT) {
+ property.usage=0;
+ }
+ }
+
+ if (property.name=="property/node_path") {
+ if (call_mode!=CALL_MODE_NODE_PATH) {
+ property.usage=0;
+ } else {
+
+ Node *bnode = _get_base_node();
+ if (bnode) {
+ property.hint_string=bnode->get_path(); //convert to loong string
+ } else {
+
+ }
+ }
+ }
+
+ if (property.name=="property/property") {
+
+ if (call_mode==CALL_MODE_BASIC_TYPE) {
+
+ property.hint=PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE;
+ property.hint_string=Variant::get_type_name(basic_type);
+
+ } else if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid()) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_SCRIPT;
+ property.hint_string=itos(get_visual_script()->get_instance_ID());
+ } else if (call_mode==CALL_MODE_INSTANCE) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
+ property.hint_string=base_type;
+
+ if (base_script!=String()) {
+ if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
+
+ ScriptServer::edit_request_func(base_script); //make sure it's loaded
+ }
+
+ if (ResourceCache::has(base_script)) {
+
+ Ref<Script> script = Ref<Resource>( ResourceCache::get(base_script) );
+ if (script.is_valid()) {
+
+ property.hint=PROPERTY_HINT_PROPERTY_OF_SCRIPT;
+ property.hint_string=itos(script->get_instance_ID());
+ }
+ }
+ }
+ } else if (call_mode==CALL_MODE_NODE_PATH) {
+ Node *node = _get_base_node();
+ if (node) {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_INSTANCE;
+ property.hint_string=itos(node->get_instance_ID());
+ } else {
+ property.hint=PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
+ property.hint_string=get_base_type();
+ }
+
+ }
+
+ }
+
+}
+
+void VisualScriptPropertyGet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","base_type"),&VisualScriptPropertyGet::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptPropertyGet::get_base_type);
+
+ ObjectTypeDB::bind_method(_MD("set_base_script","base_script"),&VisualScriptPropertyGet::set_base_script);
+ ObjectTypeDB::bind_method(_MD("get_base_script"),&VisualScriptPropertyGet::get_base_script);
+
+ ObjectTypeDB::bind_method(_MD("set_basic_type","basic_type"),&VisualScriptPropertyGet::set_basic_type);
+ ObjectTypeDB::bind_method(_MD("get_basic_type"),&VisualScriptPropertyGet::get_basic_type);
+
+ ObjectTypeDB::bind_method(_MD("_set_type_cache","type_cache"),&VisualScriptPropertyGet::_set_type_cache);
+ ObjectTypeDB::bind_method(_MD("_get_type_cache"),&VisualScriptPropertyGet::_get_type_cache);
+
+ ObjectTypeDB::bind_method(_MD("set_event_type","event_type"),&VisualScriptPropertyGet::set_event_type);
+ ObjectTypeDB::bind_method(_MD("get_event_type"),&VisualScriptPropertyGet::get_event_type);
+
+
+ ObjectTypeDB::bind_method(_MD("set_property","property"),&VisualScriptPropertyGet::set_property);
+ ObjectTypeDB::bind_method(_MD("get_property"),&VisualScriptPropertyGet::get_property);
+
+ ObjectTypeDB::bind_method(_MD("set_call_mode","mode"),&VisualScriptPropertyGet::set_call_mode);
+ ObjectTypeDB::bind_method(_MD("get_call_mode"),&VisualScriptPropertyGet::get_call_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_base_path","base_path"),&VisualScriptPropertyGet::set_base_path);
+ ObjectTypeDB::bind_method(_MD("get_base_path"),&VisualScriptPropertyGet::get_base_path);
+
+ String bt;
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ if (i>0)
+ bt+=",";
+
+ bt+=Variant::get_type_name(Variant::Type(i));
+ }
+
+ String et;
+ for(int i=0;i<InputEvent::TYPE_MAX;i++) {
+ if (i>0)
+ et+=",";
+
+ et+=event_type_names[i];
+ }
+
+ List<String> script_extensions;
+ for(int i=0;i<ScriptServer::get_language_count();i++) {
+ ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
+ }
+
+ String script_ext_hint;
+ for (List<String>::Element *E=script_extensions.front();E;E=E->next()) {
+ if (script_ext_hint!=String())
+ script_ext_hint+=",";
+ script_ext_hint+="."+E->get();
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/set_mode",PROPERTY_HINT_ENUM,"Self,Node Path,Instance,Basic Type"),_SCS("set_call_mode"),_SCS("get_call_mode"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/base_script",PROPERTY_HINT_FILE,script_ext_hint),_SCS("set_base_script"),_SCS("get_base_script"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/type_cache",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_type_cache"),_SCS("_get_type_cache"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/basic_type",PROPERTY_HINT_ENUM,bt),_SCS("set_basic_type"),_SCS("get_basic_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"property/event_type",PROPERTY_HINT_ENUM,et),_SCS("set_event_type"),_SCS("get_event_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"property/node_path",PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE),_SCS("set_base_path"),_SCS("get_base_path"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"property/property"),_SCS("set_property"),_SCS("get_property"));
+
+ BIND_CONSTANT( CALL_MODE_SELF );
+ BIND_CONSTANT( CALL_MODE_NODE_PATH);
+ BIND_CONSTANT( CALL_MODE_INSTANCE);
+}
+
+class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance {
+public:
+
+
+ VisualScriptPropertyGet::CallMode call_mode;
+ NodePath node_path;
+ StringName property;
+
+ VisualScriptPropertyGet *node;
+ VisualScriptInstance *instance;
+
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+
+ switch(call_mode) {
+
+ case VisualScriptPropertyGet::CALL_MODE_SELF: {
+
+ Object *object=instance->get_owner_ptr();
+
+ bool valid;
+
+ *p_outputs[0] = object->get(property,&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Invalid index property name.");
+ return 0;
+ }
+ } break;
+ case VisualScriptPropertyGet::CALL_MODE_NODE_PATH: {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Base object is not a Node!");
+ return 0;
+ }
+
+ Node* another = node->get_node(node_path);
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Path does not lead Node!");
+ return 0;
+ }
+
+ bool valid;
+
+
+ *p_outputs[0] = another->get(property,&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=vformat(RTR("Invalid index property name '%s' in node %s."),String(property),another->get_name());
+ return 0;
+ }
+
+ } break;
+ default: {
+
+ bool valid;
+ Variant v = *p_inputs[0];
+
+ *p_outputs[0] = v.get(property,&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("Invalid index property name.");
+
+ }
+ };
+ }
+
+ return 0;
+ }
+
+
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptPropertyGet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstancePropertyGet * instance = memnew(VisualScriptNodeInstancePropertyGet );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->property=property;
+ instance->call_mode=call_mode;
+ instance->node_path=base_path;
+
+ return instance;
+}
+
+VisualScriptPropertyGet::VisualScriptPropertyGet() {
+
+ call_mode=CALL_MODE_SELF;
+ base_type="Object";
+ basic_type=Variant::NIL;
+ event_type=InputEvent::NONE;
+ type_cache=Variant::NIL;
+
+}
+
+template<VisualScriptPropertyGet::CallMode cmode>
+static Ref<VisualScriptNode> create_property_get_node(const String& p_name) {
+
+ Ref<VisualScriptPropertyGet> node;
+ node.instance();
+ node->set_call_mode(cmode);
+ return node;
+}
+
+
+//////////////////////////////////////////
+////////////////EMIT//////////////////////
+//////////////////////////////////////////
+
+int VisualScriptEmitSignal::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptEmitSignal::has_input_sequence_port() const{
+
+ return true;
+}
+
+
+int VisualScriptEmitSignal::get_input_value_port_count() const{
+
+ Ref<VisualScript> vs = get_visual_script();
+ if (vs.is_valid()) {
+
+ if (!vs->has_custom_signal(name))
+ return 0;
+
+ return vs->custom_signal_get_argument_count(name);
+ }
+
+ return 0;
+
+}
+int VisualScriptEmitSignal::get_output_value_port_count() const{
+ return 0;
+}
+
+String VisualScriptEmitSignal::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptEmitSignal::get_input_value_port_info(int p_idx) const{
+
+ Ref<VisualScript> vs = get_visual_script();
+ if (vs.is_valid()) {
+
+ if (!vs->has_custom_signal(name))
+ return PropertyInfo();
+
+ return PropertyInfo(vs->custom_signal_get_argument_type(name,p_idx),vs->custom_signal_get_argument_name(name,p_idx));
+ }
+
+ return PropertyInfo();
+
+}
+
+PropertyInfo VisualScriptEmitSignal::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptEmitSignal::get_caption() const {
+
+ return "EmitSignal";
+}
+
+String VisualScriptEmitSignal::get_text() const {
+
+ return "emit "+String(name);
+}
+
+
+
+void VisualScriptEmitSignal::set_signal(const StringName& p_type){
+
+ if (name==p_type)
+ return;
+
+ name=p_type;
+
+ _change_notify();
+ ports_changed_notify();
+}
+StringName VisualScriptEmitSignal::get_signal() const {
+
+
+ return name;
+}
+
+
+void VisualScriptEmitSignal::_validate_property(PropertyInfo& property) const {
+
+
+
+ if (property.name=="signal/signal") {
+ property.hint=PROPERTY_HINT_ENUM;
+
+
+ List<StringName> sigs;
+
+ Ref<VisualScript> vs = get_visual_script();
+ if (vs.is_valid()) {
+
+ vs->get_custom_signal_list(&sigs);
+
+ }
+
+ String ml;
+ for (List<StringName>::Element *E=sigs.front();E;E=E->next()) {
+
+ if (ml!=String())
+ ml+=",";
+ ml+=E->get();
+ }
+
+ property.hint_string=ml;
+ }
+
+}
+
+
+void VisualScriptEmitSignal::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_signal","name"),&VisualScriptEmitSignal::set_signal);
+ ObjectTypeDB::bind_method(_MD("get_signal"),&VisualScriptEmitSignal::get_signal);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"signal/signal"),_SCS("set_signal"),_SCS("get_signal"));
+
+
+}
+
+class VisualScriptNodeInstanceEmitSignal : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptEmitSignal *node;
+ VisualScriptInstance *instance;
+ int argcount;
+ StringName name;
+
+ //virtual int get_working_memory_size() const { return 0; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+ Object *obj = instance->get_owner_ptr();
+
+ obj->emit_signal(name,p_inputs,argcount);
+
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptEmitSignal::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceEmitSignal * instance = memnew(VisualScriptNodeInstanceEmitSignal );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->name=name;
+ instance->argcount=get_input_value_port_count();
+ return instance;
+}
+
+VisualScriptEmitSignal::VisualScriptEmitSignal() {
+}
+
+
+
+static Ref<VisualScriptNode> create_basic_type_call_node(const String& p_name) {
+
+ Vector<String> path = p_name.split("/");
+ ERR_FAIL_COND_V(path.size()<4,Ref<VisualScriptNode>());
+ String base_type = path[2];
+ String method = path[3];
+
+ Ref<VisualScriptFunctionCall> node;
+ node.instance();
+
+ Variant::Type type=Variant::VARIANT_MAX;
+
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+
+ if (Variant::get_type_name(Variant::Type(i))==base_type) {
+ type=Variant::Type(i);
+ break;
+ }
+ }
+
+ ERR_FAIL_COND_V(type==Variant::VARIANT_MAX,Ref<VisualScriptNode>());
+
+
+ node->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
+ node->set_basic_type(type);
+ node->set_function(method);
+
+ return node;
+}
+
+
+void register_visual_script_func_nodes() {
+
+ VisualScriptLanguage::singleton->add_register_func("functions/call",create_node_generic<VisualScriptFunctionCall>);
+ VisualScriptLanguage::singleton->add_register_func("functions/set",create_node_generic<VisualScriptPropertySet>);
+ VisualScriptLanguage::singleton->add_register_func("functions/get",create_node_generic<VisualScriptPropertyGet>);
+
+ //VisualScriptLanguage::singleton->add_register_func("functions/call_script/call_self",create_script_call_node<VisualScriptScriptCall::CALL_MODE_SELF>);
+// VisualScriptLanguage::singleton->add_register_func("functions/call_script/call_node",create_script_call_node<VisualScriptScriptCall::CALL_MODE_NODE_PATH>);
+ VisualScriptLanguage::singleton->add_register_func("functions/emit_signal",create_node_generic<VisualScriptEmitSignal>);
+
+
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+
+ Variant::Type t = Variant::Type(i);
+ String type_name = Variant::get_type_name(t);
+ Variant::CallError ce;
+ Variant vt = Variant::construct(t,NULL,0,ce);
+ List<MethodInfo> ml;
+ vt.get_method_list(&ml);
+
+ for (List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
+ VisualScriptLanguage::singleton->add_register_func("functions/by_type/"+type_name+"/"+E->get().name,create_basic_type_call_node);
+ }
+ }
+}
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
new file mode 100644
index 0000000000..43ef276cf4
--- /dev/null
+++ b/modules/visual_script/visual_script_func_nodes.h
@@ -0,0 +1,349 @@
+#ifndef VISUAL_SCRIPT_FUNC_NODES_H
+#define VISUAL_SCRIPT_FUNC_NODES_H
+
+#include "visual_script.h"
+
+
+class VisualScriptFunctionCall : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptFunctionCall,VisualScriptNode)
+public:
+ enum CallMode {
+ CALL_MODE_SELF,
+ CALL_MODE_NODE_PATH,
+ CALL_MODE_INSTANCE,
+ CALL_MODE_BASIC_TYPE,
+ CALL_MODE_SINGLETON,
+ };
+
+ enum RPCCallMode {
+ RPC_DISABLED,
+ RPC_RELIABLE,
+ RPC_UNRELIABLE,
+ RPC_RELIABLE_TO_ID,
+ RPC_UNRELIABLE_TO_ID
+ };
+
+private:
+
+ CallMode call_mode;
+ StringName base_type;
+ String base_script;
+ Variant::Type basic_type;
+ NodePath base_path;
+ StringName function;
+ int use_default_args;
+ RPCCallMode rpc_call_mode;
+ StringName singleton;
+ bool validate;
+
+
+ Node *_get_base_node() const;
+ StringName _get_base_type() const;
+
+ MethodInfo method_cache;
+ void _update_method_cache();
+
+ void _set_argument_cache(const Dictionary& p_args);
+ Dictionary _get_argument_cache() const;
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_basic_type(Variant::Type p_type);
+ Variant::Type get_basic_type() const;
+
+ void set_base_type(const StringName& p_type);
+ StringName get_base_type() const;
+
+ void set_base_script(const String& p_path);
+ String get_base_script() const;
+
+ void set_singleton(const StringName& p_type);
+ StringName get_singleton() const;
+
+ void set_function(const StringName& p_type);
+ StringName get_function() const;
+
+ void set_base_path(const NodePath& p_type);
+ NodePath get_base_path() const;
+
+
+ void set_call_mode(CallMode p_mode);
+ CallMode get_call_mode() const;
+
+ void set_use_default_args(int p_amount);
+ int get_use_default_args() const;
+
+ void set_validate(bool p_amount);
+ bool get_validate() const;
+
+ void set_rpc_call_mode(RPCCallMode p_mode);
+ RPCCallMode get_rpc_call_mode() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+
+ VisualScriptFunctionCall();
+};
+
+VARIANT_ENUM_CAST(VisualScriptFunctionCall::CallMode );
+VARIANT_ENUM_CAST(VisualScriptFunctionCall::RPCCallMode );
+
+
+class VisualScriptPropertySet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptPropertySet,VisualScriptNode)
+public:
+ enum CallMode {
+ CALL_MODE_SELF,
+ CALL_MODE_NODE_PATH,
+ CALL_MODE_INSTANCE,
+ CALL_MODE_BASIC_TYPE,
+
+
+ };
+private:
+
+ PropertyInfo type_cache;
+
+ CallMode call_mode;
+ Variant::Type basic_type;
+ StringName base_type;
+ String base_script;
+ NodePath base_path;
+ StringName property;
+ InputEvent::Type event_type;
+
+ Node *_get_base_node() const;
+ StringName _get_base_type() const;
+
+ void _update_base_type();
+
+ void _update_cache();
+
+ void _set_type_cache(const Dictionary& p_type);
+ Dictionary _get_type_cache() const;
+
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_base_type(const StringName& p_type);
+ StringName get_base_type() const;
+
+ void set_base_script(const String& p_path);
+ String get_base_script() const;
+
+ void set_basic_type(Variant::Type p_type);
+ Variant::Type get_basic_type() const;
+
+ void set_event_type(InputEvent::Type p_type);
+ InputEvent::Type get_event_type() const;
+
+ void set_property(const StringName& p_type);
+ StringName get_property() const;
+
+ void set_base_path(const NodePath& p_type);
+ NodePath get_base_path() const;
+
+ void set_call_mode(CallMode p_mode);
+ CallMode get_call_mode() const;
+
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+ VisualScriptPropertySet();
+};
+
+VARIANT_ENUM_CAST(VisualScriptPropertySet::CallMode );
+
+
+class VisualScriptPropertyGet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptPropertyGet,VisualScriptNode)
+public:
+ enum CallMode {
+ CALL_MODE_SELF,
+ CALL_MODE_NODE_PATH,
+ CALL_MODE_INSTANCE,
+ CALL_MODE_BASIC_TYPE,
+
+ };
+private:
+
+ Variant::Type type_cache;
+
+ CallMode call_mode;
+ Variant::Type basic_type;
+ StringName base_type;
+ String base_script;
+ NodePath base_path;
+ StringName property;
+ InputEvent::Type event_type;
+
+ void _update_base_type();
+ Node *_get_base_node() const;
+ StringName _get_base_type() const;
+
+ void _update_cache();
+
+ void _set_type_cache(Variant::Type p_type);
+ Variant::Type _get_type_cache() const;
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_base_type(const StringName& p_type);
+ StringName get_base_type() const;
+
+ void set_base_script(const String& p_path);
+ String get_base_script() const;
+
+ void set_basic_type(Variant::Type p_type);
+ Variant::Type get_basic_type() const;
+
+ void set_event_type(InputEvent::Type p_type);
+ InputEvent::Type get_event_type() const;
+
+ void set_property(const StringName& p_type);
+ StringName get_property() const;
+
+ void set_base_path(const NodePath& p_type);
+ NodePath get_base_path() const;
+
+ void set_call_mode(CallMode p_mode);
+ CallMode get_call_mode() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptPropertyGet();
+};
+
+
+
+
+
+VARIANT_ENUM_CAST(VisualScriptPropertyGet::CallMode );
+
+
+
+class VisualScriptEmitSignal : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptEmitSignal,VisualScriptNode)
+
+private:
+
+ StringName name;
+
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_signal(const StringName& p_type);
+ StringName get_signal() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+
+
+
+ VisualScriptEmitSignal();
+};
+
+
+
+void register_visual_script_func_nodes();
+
+#endif // VISUAL_SCRIPT_FUNC_NODES_H
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
new file mode 100644
index 0000000000..7ada292b13
--- /dev/null
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -0,0 +1,3958 @@
+#include "visual_script_nodes.h"
+#include "global_constants.h"
+#include "globals.h"
+#include "scene/main/scene_main_loop.h"
+#include "os/os.h"
+#include "scene/main/node.h"
+#include "os/input.h"
+
+//////////////////////////////////////////
+////////////////FUNCTION//////////////////
+//////////////////////////////////////////
+
+
+bool VisualScriptFunction::_set(const StringName& p_name, const Variant& p_value) {
+
+
+ if (p_name=="argument_count") {
+
+ int new_argc=p_value;
+ int argc = arguments.size();
+ if (argc==new_argc)
+ return true;
+
+ arguments.resize(new_argc);
+
+ for(int i=argc;i<new_argc;i++) {
+ arguments[i].name="arg"+itos(i+1);
+ arguments[i].type=Variant::NIL;
+ }
+ ports_changed_notify();
+ _change_notify();
+ return true;
+ }
+ if (String(p_name).begins_with("argument/")) {
+ int idx = String(p_name).get_slice("/",1).to_int()-1;
+ ERR_FAIL_INDEX_V(idx,arguments.size(),false);
+ String what = String(p_name).get_slice("/",2);
+ if (what=="type") {
+
+ Variant::Type new_type = Variant::Type(int(p_value));
+ arguments[idx].type=new_type;
+ ports_changed_notify();
+
+ return true;
+ }
+
+ if (what=="name") {
+
+ arguments[idx].name=p_value;
+ ports_changed_notify();
+ return true;
+ }
+
+
+ }
+
+ if (p_name=="stack/stackless") {
+ set_stack_less(p_value);
+ return true;
+ }
+
+ if (p_name=="stack/size") {
+ stack_size=p_value;
+ return true;
+ }
+
+ if (p_name=="rpc/mode") {
+ rpc_mode=ScriptInstance::RPCMode(int(p_value));
+ return true;
+ }
+
+ return false;
+}
+
+bool VisualScriptFunction::_get(const StringName& p_name,Variant &r_ret) const {
+
+
+ if (p_name=="argument_count") {
+ r_ret = arguments.size();
+ return true;
+ }
+ if (String(p_name).begins_with("argument/")) {
+ int idx = String(p_name).get_slice("/",1).to_int()-1;
+ ERR_FAIL_INDEX_V(idx,arguments.size(),false);
+ String what = String(p_name).get_slice("/",2);
+ if (what=="type") {
+ r_ret = arguments[idx].type;
+ return true;
+ }
+ if (what=="name") {
+ r_ret = arguments[idx].name;
+ return true;
+ }
+
+
+
+ }
+
+ if (p_name=="stack/stackless") {
+ r_ret=stack_less;
+ return true;
+ }
+
+ if (p_name=="stack/size") {
+ r_ret=stack_size;
+ return true;
+ }
+
+ if (p_name=="rpc/mode") {
+ r_ret=rpc_mode;
+ return true;
+ }
+
+ return false;
+}
+void VisualScriptFunction::_get_property_list( List<PropertyInfo> *p_list) const {
+
+
+ p_list->push_back(PropertyInfo(Variant::INT,"argument_count",PROPERTY_HINT_RANGE,"0,256"));
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ for(int i=0;i<arguments.size();i++) {
+ p_list->push_back(PropertyInfo(Variant::INT,"argument/"+itos(i+1)+"/type",PROPERTY_HINT_ENUM,argt));
+ p_list->push_back(PropertyInfo(Variant::STRING,"argument/"+itos(i+1)+"/name"));
+ }
+ if (!stack_less) {
+ p_list->push_back(PropertyInfo(Variant::INT,"stack/size",PROPERTY_HINT_RANGE,"1,100000"));
+ }
+ p_list->push_back(PropertyInfo(Variant::BOOL,"stack/stackless"));
+ p_list->push_back(PropertyInfo(Variant::INT,"rpc/mode",PROPERTY_HINT_ENUM,"Disabled,Remote,Sync,Master,Slave"));
+
+}
+
+
+int VisualScriptFunction::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptFunction::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptFunction::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptFunction::get_output_value_port_count() const{
+
+ return arguments.size();
+}
+
+String VisualScriptFunction::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptFunction::get_input_value_port_info(int p_idx) const{
+
+ ERR_FAIL_V(PropertyInfo());
+ return PropertyInfo();
+}
+PropertyInfo VisualScriptFunction::get_output_value_port_info(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,arguments.size(),PropertyInfo());
+ PropertyInfo out;
+ out.type=arguments[p_idx].type;
+ out.name=arguments[p_idx].name;
+ return out;
+}
+
+String VisualScriptFunction::get_caption() const {
+
+ return "Function";
+}
+
+String VisualScriptFunction::get_text() const {
+
+ return get_name(); //use name as function name I guess
+}
+
+void VisualScriptFunction::add_argument(Variant::Type p_type,const String& p_name,int p_index){
+
+ Argument arg;
+ arg.name=p_name;
+ arg.type=p_type;
+ if (p_index>=0)
+ arguments.insert(p_index,arg);
+ else
+ arguments.push_back(arg);
+
+ ports_changed_notify();
+
+}
+void VisualScriptFunction::set_argument_type(int p_argidx,Variant::Type p_type){
+
+ ERR_FAIL_INDEX(p_argidx,arguments.size());
+
+ arguments[p_argidx].type=p_type;
+ ports_changed_notify();
+}
+Variant::Type VisualScriptFunction::get_argument_type(int p_argidx) const {
+
+ ERR_FAIL_INDEX_V(p_argidx,arguments.size(),Variant::NIL);
+ return arguments[p_argidx].type;
+
+}
+void VisualScriptFunction::set_argument_name(int p_argidx,const String& p_name) {
+
+ ERR_FAIL_INDEX(p_argidx,arguments.size());
+
+ arguments[p_argidx].name=p_name;
+ ports_changed_notify();
+
+}
+String VisualScriptFunction::get_argument_name(int p_argidx) const {
+
+ ERR_FAIL_INDEX_V(p_argidx,arguments.size(),String());
+ return arguments[p_argidx].name;
+
+}
+void VisualScriptFunction::remove_argument(int p_argidx) {
+
+ ERR_FAIL_INDEX(p_argidx,arguments.size());
+
+ arguments.remove(p_argidx);
+ ports_changed_notify();
+
+}
+
+int VisualScriptFunction::get_argument_count() const {
+
+ return arguments.size();
+}
+
+
+void VisualScriptFunction::set_rpc_mode(ScriptInstance::RPCMode p_mode) {
+ rpc_mode=p_mode;
+}
+
+ScriptInstance::RPCMode VisualScriptFunction::get_rpc_mode() const {
+ return rpc_mode;
+}
+
+
+class VisualScriptNodeInstanceFunction : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptFunction *node;
+ VisualScriptInstance *instance;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ int ac = node->get_argument_count();
+
+ for(int i=0;i<ac;i++) {
+#ifdef DEBUG_ENABLED
+ Variant::Type expected = node->get_argument_type(i);
+ if (expected!=Variant::NIL) {
+ if (!Variant::can_convert_strict(p_inputs[i]->get_type(),expected)) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.expected=expected;
+ r_error.argument=i;
+ return 0;
+ }
+ }
+#endif
+
+ *p_outputs[i]=*p_inputs[i];
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptFunction::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceFunction * instance = memnew(VisualScriptNodeInstanceFunction );
+ instance->node=this;
+ instance->instance=p_instance;
+ return instance;
+}
+
+VisualScriptFunction::VisualScriptFunction() {
+
+ stack_size=256;
+ stack_less=false;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+}
+
+
+void VisualScriptFunction::set_stack_less(bool p_enable) {
+ stack_less=p_enable;
+ _change_notify();
+}
+
+bool VisualScriptFunction::is_stack_less() const {
+ return stack_less;
+}
+
+void VisualScriptFunction::set_stack_size(int p_size) {
+
+ ERR_FAIL_COND(p_size <1 || p_size>100000);
+ stack_size=p_size;
+}
+
+int VisualScriptFunction::get_stack_size() const {
+
+ return stack_size;
+}
+
+
+//////////////////////////////////////////
+////////////////OPERATOR//////////////////
+//////////////////////////////////////////
+
+int VisualScriptOperator::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptOperator::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptOperator::get_input_value_port_count() const{
+
+ return (op==Variant::OP_BIT_NEGATE || op==Variant::OP_NOT || op==Variant::OP_NEGATE) ? 1 : 2;
+}
+int VisualScriptOperator::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptOperator::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const{
+
+ static const Variant::Type port_types[Variant::OP_MAX][2]={
+ {Variant::NIL,Variant::NIL}, //OP_EQUAL,
+ {Variant::NIL,Variant::NIL}, //OP_NOT_EQUAL,
+ {Variant::NIL,Variant::NIL}, //OP_LESS,
+ {Variant::NIL,Variant::NIL}, //OP_LESS_EQUAL,
+ {Variant::NIL,Variant::NIL}, //OP_GREATER,
+ {Variant::NIL,Variant::NIL}, //OP_GREATER_EQUAL,
+ //mathematic
+ {Variant::NIL,Variant::NIL}, //OP_ADD,
+ {Variant::NIL,Variant::NIL}, //OP_SUBSTRACT,
+ {Variant::NIL,Variant::NIL}, //OP_MULTIPLY,
+ {Variant::NIL,Variant::NIL}, //OP_DIVIDE,
+ {Variant::NIL,Variant::NIL}, //OP_NEGATE,
+ {Variant::INT,Variant::INT}, //OP_MODULE,
+ {Variant::STRING,Variant::STRING}, //OP_STRING_CONCAT,
+ //bitwise
+ {Variant::INT,Variant::INT}, //OP_SHIFT_LEFT,
+ {Variant::INT,Variant::INT}, //OP_SHIFT_RIGHT,
+ {Variant::INT,Variant::INT}, //OP_BIT_AND,
+ {Variant::INT,Variant::INT}, //OP_BIT_OR,
+ {Variant::INT,Variant::INT}, //OP_BIT_XOR,
+ {Variant::INT,Variant::INT}, //OP_BIT_NEGATE,
+ //logic
+ {Variant::BOOL,Variant::BOOL}, //OP_AND,
+ {Variant::BOOL,Variant::BOOL}, //OP_OR,
+ {Variant::BOOL,Variant::BOOL}, //OP_XOR,
+ {Variant::BOOL,Variant::BOOL}, //OP_NOT,
+ //containment
+ {Variant::NIL,Variant::NIL} //OP_IN,
+ };
+
+ ERR_FAIL_INDEX_V(p_idx,Variant::OP_MAX,PropertyInfo());
+
+ PropertyInfo pinfo;
+ pinfo.name=p_idx==0?"A":"B";
+ pinfo.type=port_types[op][p_idx];
+ if (pinfo.type==Variant::NIL)
+ pinfo.type=typed;
+ return pinfo;
+}
+PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const{
+ static const Variant::Type port_types[Variant::OP_MAX]={
+ //comparation
+ Variant::BOOL, //OP_EQUAL,
+ Variant::BOOL, //OP_NOT_EQUAL,
+ Variant::BOOL, //OP_LESS,
+ Variant::BOOL, //OP_LESS_EQUAL,
+ Variant::BOOL, //OP_GREATER,
+ Variant::BOOL, //OP_GREATER_EQUAL,
+ //mathematic
+ Variant::NIL, //OP_ADD,
+ Variant::NIL, //OP_SUBSTRACT,
+ Variant::NIL, //OP_MULTIPLY,
+ Variant::NIL, //OP_DIVIDE,
+ Variant::NIL, //OP_NEGATE,
+ Variant::INT, //OP_MODULE,
+ Variant::STRING, //OP_STRING_CONCAT,
+ //bitwise
+ Variant::INT, //OP_SHIFT_LEFT,
+ Variant::INT, //OP_SHIFT_RIGHT,
+ Variant::INT, //OP_BIT_AND,
+ Variant::INT, //OP_BIT_OR,
+ Variant::INT, //OP_BIT_XOR,
+ Variant::INT, //OP_BIT_NEGATE,
+ //logic
+ Variant::BOOL, //OP_AND,
+ Variant::BOOL, //OP_OR,
+ Variant::BOOL, //OP_XOR,
+ Variant::BOOL, //OP_NOT,
+ //containment
+ Variant::BOOL //OP_IN,
+ };
+
+ PropertyInfo pinfo;
+ pinfo.name="";
+ pinfo.type=port_types[op];
+ if (pinfo.type==Variant::NIL)
+ pinfo.type=typed;
+ return pinfo;
+
+}
+
+static const char* op_names[]={
+ //comparation
+ "Equal", //OP_EQUAL,
+ "NotEqual", //OP_NOT_EQUAL,
+ "Less", //OP_LESS,
+ "LessEqual", //OP_LESS_EQUAL,
+ "Greater", //OP_GREATER,
+ "GreaterEq", //OP_GREATER_EQUAL,
+ //mathematic
+ "Add", //OP_ADD,
+ "Subtract", //OP_SUBSTRACT,
+ "Multiply", //OP_MULTIPLY,
+ "Divide", //OP_DIVIDE,
+ "Negate", //OP_NEGATE,
+ "Remainder", //OP_MODULE,
+ "Concat", //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,
+ //logic
+ "And", //OP_AND,
+ "Or", //OP_OR,
+ "Xor", //OP_XOR,
+ "Not", //OP_NOT,
+ //containment
+ "In", //OP_IN,
+};
+
+String VisualScriptOperator::get_caption() const {
+
+
+
+ return op_names[op];
+}
+
+String VisualScriptOperator::get_text() const {
+
+ static const wchar_t* op_names[]={
+ //comparation
+ L"A = B", //OP_EQUAL,
+ L"A \u2260 B", //OP_NOT_EQUAL,
+ L"A < B", //OP_LESS,
+ L"A \u2264 B", //OP_LESS_EQUAL,
+ L"A > B", //OP_GREATER,
+ L"A \u2265 B", //OP_GREATER_EQUAL,
+ //mathematic
+ L"A + B", //OP_ADD,
+ L"A - B", //OP_SUBSTRACT,
+ L"A x B", //OP_MULTIPLY,
+ L"A \u00F7 B", //OP_DIVIDE,
+ L"\u00AC A", //OP_NEGATE,
+ L"A mod B", //OP_MODULE,
+ L"A .. B", //OP_STRING_CONCAT,
+ //bitwise
+ L"A << B", //OP_SHIFT_LEFT,
+ L"A >> B", //OP_SHIFT_RIGHT,
+ L"A & B", //OP_BIT_AND,
+ L"A | B", //OP_BIT_OR,
+ L"A ^ B", //OP_BIT_XOR,
+ L"~A", //OP_BIT_NEGATE,
+ //logic
+ L"A and B", //OP_AND,
+ L"A or B", //OP_OR,
+ L"A xor B", //OP_XOR,
+ L"not A", //OP_NOT,
+
+ };
+ return op_names[op];
+}
+
+void VisualScriptOperator::set_operator(Variant::Operator p_op) {
+
+ if (op==p_op)
+ return;
+ op=p_op;
+ ports_changed_notify();
+
+}
+
+Variant::Operator VisualScriptOperator::get_operator() const{
+
+ return op;
+}
+
+void VisualScriptOperator::set_typed(Variant::Type p_op) {
+
+ if (typed==p_op)
+ return;
+
+ typed=p_op;
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptOperator::get_typed() const {
+
+ return typed;
+}
+
+
+void VisualScriptOperator::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_operator","op"),&VisualScriptOperator::set_operator);
+ ObjectTypeDB::bind_method(_MD("get_operator"),&VisualScriptOperator::get_operator);
+
+ ObjectTypeDB::bind_method(_MD("set_typed","type"),&VisualScriptOperator::set_typed);
+ ObjectTypeDB::bind_method(_MD("get_typed"),&VisualScriptOperator::get_typed);
+
+ String types;
+ for(int i=0;i<Variant::OP_MAX;i++) {
+ if (i>0)
+ types+=",";
+ types+=op_names[i];
+ }
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"operator_value/type",PROPERTY_HINT_ENUM,types),_SCS("set_operator"),_SCS("get_operator"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"typed_value/typed",PROPERTY_HINT_ENUM,argt),_SCS("set_typed"),_SCS("get_typed"));
+
+}
+
+class VisualScriptNodeInstanceOperator : public VisualScriptNodeInstance {
+public:
+
+ bool unary;
+ Variant::Operator op;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ bool valid;
+ if (unary) {
+
+ Variant::evaluate(op,*p_inputs[0],Variant(),*p_outputs[0],valid);
+ } else {
+ Variant::evaluate(op,*p_inputs[0],*p_inputs[1],*p_outputs[0],valid);
+ }
+
+ if (!valid) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ if (p_outputs[0]->get_type()==Variant::STRING) {
+ r_error_str=*p_outputs[0];
+ } else {
+ if (unary)
+ r_error_str=String(op_names[op])+RTR(": Invalid argument of type: ")+Variant::get_type_name(p_inputs[0]->get_type());
+ else
+ r_error_str=String(op_names[op])+RTR(": Invalid arguments: ")+"A: "+Variant::get_type_name(p_inputs[0]->get_type())+" B: "+Variant::get_type_name(p_inputs[1]->get_type());
+ }
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptOperator::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceOperator * instance = memnew(VisualScriptNodeInstanceOperator );
+ instance->unary=get_input_value_port_count()==1;
+ instance->op=op;
+ return instance;
+}
+
+VisualScriptOperator::VisualScriptOperator() {
+
+ op=Variant::OP_ADD;
+ typed=Variant::NIL;
+}
+
+
+
+template<Variant::Operator OP>
+static Ref<VisualScriptNode> create_op_node(const String& p_name) {
+
+ Ref<VisualScriptOperator> node;
+ node.instance();
+ node->set_operator(OP);
+ return node;
+}
+
+//////////////////////////////////////////
+////////////////VARIABLE GET//////////////////
+//////////////////////////////////////////
+
+int VisualScriptVariableGet::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptVariableGet::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptVariableGet::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptVariableGet::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptVariableGet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptVariableGet::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptVariableGet::get_output_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="value";
+ if (get_visual_script().is_valid() && get_visual_script()->has_variable(variable)) {
+ PropertyInfo vinfo = get_visual_script()->get_variable_info(variable);
+ pinfo.type=vinfo.type;
+ pinfo.hint=vinfo.hint;
+ pinfo.hint_string=vinfo.hint_string;
+ }
+ return pinfo;
+}
+
+
+String VisualScriptVariableGet::get_caption() const {
+
+ return "Variable";
+}
+
+String VisualScriptVariableGet::get_text() const {
+
+ return variable;
+}
+
+void VisualScriptVariableGet::set_variable(StringName p_variable) {
+
+ if (variable==p_variable)
+ return;
+ variable=p_variable;
+ ports_changed_notify();
+
+}
+
+StringName VisualScriptVariableGet::get_variable() const{
+
+ return variable;
+}
+
+void VisualScriptVariableGet::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="variable/name" && get_visual_script().is_valid()) {
+ Ref<VisualScript> vs = get_visual_script();
+ List<StringName> vars;
+ vs->get_variable_list(&vars);
+
+ String vhint;
+ for (List<StringName>::Element *E=vars.front();E;E=E->next()) {
+ if (vhint!=String())
+ vhint+=",";
+
+ vhint+=E->get().operator String();
+ }
+
+ property.hint=PROPERTY_HINT_ENUM;
+ property.hint_string=vhint;
+ }
+}
+
+void VisualScriptVariableGet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_variable","name"),&VisualScriptVariableGet::set_variable);
+ ObjectTypeDB::bind_method(_MD("get_variable"),&VisualScriptVariableGet::get_variable);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"variable/name"),_SCS("set_variable"),_SCS("get_variable"));
+
+}
+
+class VisualScriptNodeInstanceVariableGet : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptVariableGet *node;
+ VisualScriptInstance *instance;
+ StringName variable;
+
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (instance->get_variable(variable,p_outputs[0])==false) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("VariableGet not found in script: ")+"'"+String(variable)+"'";
+ return false;
+ }
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptVariableGet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceVariableGet * instance = memnew(VisualScriptNodeInstanceVariableGet );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->variable=variable;
+ return instance;
+}
+VisualScriptVariableGet::VisualScriptVariableGet() {
+
+
+}
+
+
+//////////////////////////////////////////
+////////////////VARIABLE SET//////////////////
+//////////////////////////////////////////
+
+int VisualScriptVariableSet::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptVariableSet::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptVariableSet::get_input_value_port_count() const{
+
+ return 1;
+}
+int VisualScriptVariableSet::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptVariableSet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptVariableSet::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="set";
+ if (get_visual_script().is_valid() && get_visual_script()->has_variable(variable)) {
+ PropertyInfo vinfo = get_visual_script()->get_variable_info(variable);
+ pinfo.type=vinfo.type;
+ pinfo.hint=vinfo.hint;
+ pinfo.hint_string=vinfo.hint_string;
+ }
+ return pinfo;
+}
+
+PropertyInfo VisualScriptVariableSet::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptVariableSet::get_caption() const {
+
+ return "VariableSet";
+}
+
+String VisualScriptVariableSet::get_text() const {
+
+ return variable;
+}
+
+void VisualScriptVariableSet::set_variable(StringName p_variable) {
+
+ if (variable==p_variable)
+ return;
+ variable=p_variable;
+ ports_changed_notify();
+
+}
+
+StringName VisualScriptVariableSet::get_variable() const{
+
+ return variable;
+}
+
+void VisualScriptVariableSet::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="variable/name" && get_visual_script().is_valid()) {
+ Ref<VisualScript> vs = get_visual_script();
+ List<StringName> vars;
+ vs->get_variable_list(&vars);
+
+ String vhint;
+ for (List<StringName>::Element *E=vars.front();E;E=E->next()) {
+ if (vhint!=String())
+ vhint+=",";
+
+ vhint+=E->get().operator String();
+ }
+
+ property.hint=PROPERTY_HINT_ENUM;
+ property.hint_string=vhint;
+ }
+}
+
+void VisualScriptVariableSet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_variable","name"),&VisualScriptVariableSet::set_variable);
+ ObjectTypeDB::bind_method(_MD("get_variable"),&VisualScriptVariableSet::get_variable);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"variable/name"),_SCS("set_variable"),_SCS("get_variable"));
+
+}
+
+class VisualScriptNodeInstanceVariableSet : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptVariableSet *node;
+ VisualScriptInstance *instance;
+ StringName variable;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (instance->set_variable(variable,*p_inputs[0])==false) {
+
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str=RTR("VariableSet not found in script: ")+"'"+String(variable)+"'";
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptVariableSet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceVariableSet * instance = memnew(VisualScriptNodeInstanceVariableSet );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->variable=variable;
+ return instance;
+}
+VisualScriptVariableSet::VisualScriptVariableSet() {
+
+
+}
+
+
+
+//////////////////////////////////////////
+////////////////CONSTANT//////////////////
+//////////////////////////////////////////
+
+int VisualScriptConstant::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptConstant::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptConstant::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptConstant::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptConstant::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptConstant::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptConstant::get_output_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo;
+ pinfo.name="get";
+ pinfo.type=type;
+ return pinfo;
+}
+
+
+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)
+ return;
+
+ type=p_type;
+ ports_changed_notify();
+ Variant::CallError ce;
+ value=Variant::construct(type,NULL,0,ce);
+ _change_notify();
+
+}
+
+Variant::Type VisualScriptConstant::get_constant_type() const{
+
+ return type;
+}
+
+void VisualScriptConstant::set_constant_value(Variant p_value){
+
+ if (value==p_value)
+ return;
+
+ value=p_value;
+ ports_changed_notify();
+}
+Variant VisualScriptConstant::get_constant_value() const{
+
+ return value;
+}
+
+void VisualScriptConstant::_validate_property(PropertyInfo& property) const {
+
+
+ if (property.name=="constant/value") {
+ property.type=type;
+ if (type==Variant::NIL)
+ property.usage=0; //do not save if nil
+ }
+}
+
+void VisualScriptConstant::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_constant_type","type"),&VisualScriptConstant::set_constant_type);
+ ObjectTypeDB::bind_method(_MD("get_constant_type"),&VisualScriptConstant::get_constant_type);
+
+ ObjectTypeDB::bind_method(_MD("set_constant_value","value"),&VisualScriptConstant::set_constant_value);
+ ObjectTypeDB::bind_method(_MD("get_constant_value"),&VisualScriptConstant::get_constant_value);
+
+ String argt="Null";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"constant/type",PROPERTY_HINT_ENUM,argt),_SCS("set_constant_type"),_SCS("get_constant_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::NIL,"constant/value"),_SCS("set_constant_value"),_SCS("get_constant_value"));
+
+}
+
+class VisualScriptNodeInstanceConstant : public VisualScriptNodeInstance {
+public:
+
+ Variant constant;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0]=constant;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptConstant::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceConstant * instance = memnew(VisualScriptNodeInstanceConstant );
+ instance->constant=value;
+ return instance;
+}
+
+VisualScriptConstant::VisualScriptConstant() {
+
+ type=Variant::NIL;
+
+}
+
+//////////////////////////////////////////
+////////////////PRELOAD//////////////////
+//////////////////////////////////////////
+
+int VisualScriptPreload::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptPreload::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptPreload::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptPreload::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptPreload::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptPreload::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const{
+
+ PropertyInfo pinfo=PropertyInfo(Variant::OBJECT,"res");
+ if (preload.is_valid()) {
+ pinfo.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ pinfo.hint_string=preload->get_type();
+ }
+
+ return pinfo;
+}
+
+
+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_type();
+ }
+ } else {
+ return "<empty>";
+ }
+}
+
+
+void VisualScriptPreload::set_preload(const Ref<Resource>& p_preload){
+
+ if (preload==p_preload)
+ return;
+
+ preload=p_preload;
+ ports_changed_notify();
+}
+Ref<Resource> VisualScriptPreload::get_preload() const{
+
+ return preload;
+}
+
+
+void VisualScriptPreload::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_preload","resource"),&VisualScriptPreload::set_preload);
+ ObjectTypeDB::bind_method(_MD("get_preload"),&VisualScriptPreload::get_preload);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"resource",PROPERTY_HINT_RESOURCE_TYPE,"Resource"),_SCS("set_preload"),_SCS("get_preload"));
+
+}
+
+class VisualScriptNodeInstancePreload : public VisualScriptNodeInstance {
+public:
+
+ Ref<Resource> preload;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0]=preload;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptPreload::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstancePreload * instance = memnew(VisualScriptNodeInstancePreload );
+ instance->preload=preload;
+ return instance;
+}
+
+VisualScriptPreload::VisualScriptPreload() {
+
+}
+
+
+
+
+//////////////////////////////////////////
+////////////////INDEX////////////////////
+//////////////////////////////////////////
+
+int VisualScriptIndexGet::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptIndexGet::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptIndexGet::get_input_value_port_count() const{
+
+ return 2;
+}
+int VisualScriptIndexGet::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptIndexGet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptIndexGet::get_input_value_port_info(int p_idx) const{
+
+ if (p_idx==0) {
+ return PropertyInfo(Variant::NIL,"base");
+ } else {
+ return PropertyInfo(Variant::NIL,"index");
+
+ }
+}
+
+PropertyInfo VisualScriptIndexGet::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptIndexGet::get_caption() const {
+
+ return "IndexGet";
+}
+
+String VisualScriptIndexGet::get_text() const {
+
+ return String("get");
+}
+
+
+class VisualScriptNodeInstanceIndexGet : public VisualScriptNodeInstance {
+public:
+
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ bool valid;
+ *p_outputs[0] = p_inputs[0]->get(*p_inputs[1],&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid get: "+p_inputs[0]->get_construct_string();
+ }
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptIndexGet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceIndexGet * instance = memnew(VisualScriptNodeInstanceIndexGet );
+ return instance;
+}
+VisualScriptIndexGet::VisualScriptIndexGet() {
+
+
+
+}
+
+//////////////////////////////////////////
+////////////////INDEXSET//////////////////
+//////////////////////////////////////////
+
+int VisualScriptIndexSet::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptIndexSet::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptIndexSet::get_input_value_port_count() const{
+
+ return 3;
+}
+int VisualScriptIndexSet::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptIndexSet::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptIndexSet::get_input_value_port_info(int p_idx) const{
+
+ if (p_idx==0) {
+ return PropertyInfo(Variant::NIL,"base");
+ } else if (p_idx==1){
+ return PropertyInfo(Variant::NIL,"index");
+
+ } else {
+ return PropertyInfo(Variant::NIL,"value");
+
+ }
+}
+
+PropertyInfo VisualScriptIndexSet::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptIndexSet::get_caption() const {
+
+ return "IndexSet";
+}
+
+String VisualScriptIndexSet::get_text() const {
+
+ return String("set");
+}
+
+
+class VisualScriptNodeInstanceIndexSet : public VisualScriptNodeInstance {
+public:
+
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ bool valid;
+ *p_outputs[0]=*p_inputs[0];
+ p_outputs[0]->set(*p_inputs[1],*p_inputs[2],&valid);
+
+ if (!valid) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Invalid set: "+p_inputs[1]->get_construct_string();
+ }
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptIndexSet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceIndexSet * instance = memnew(VisualScriptNodeInstanceIndexSet );
+ return instance;
+}
+VisualScriptIndexSet::VisualScriptIndexSet() {
+
+
+
+}
+
+
+//////////////////////////////////////////
+////////////////GLOBALCONSTANT///////////
+//////////////////////////////////////////
+
+int VisualScriptGlobalConstant::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptGlobalConstant::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptGlobalConstant::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptGlobalConstant::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptGlobalConstant::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::REAL,"value");
+}
+
+
+String VisualScriptGlobalConstant::get_caption() const {
+
+ return "GlobalConst";
+}
+
+String VisualScriptGlobalConstant::get_text() const {
+
+ return GlobalConstants::get_global_constant_name(index);
+}
+
+void VisualScriptGlobalConstant::set_global_constant(int p_which) {
+
+ index=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+int VisualScriptGlobalConstant::get_global_constant() {
+ return index;
+}
+
+
+class VisualScriptNodeInstanceGlobalConstant : public VisualScriptNodeInstance {
+public:
+
+ int index;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+ *p_outputs[0] = GlobalConstants::get_global_constant_value(index);
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptGlobalConstant::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceGlobalConstant * instance = memnew(VisualScriptNodeInstanceGlobalConstant );
+ instance->index=index;
+ return instance;
+}
+
+void VisualScriptGlobalConstant::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_global_constant","index"),&VisualScriptGlobalConstant::set_global_constant);
+ ObjectTypeDB::bind_method(_MD("get_global_constant"),&VisualScriptGlobalConstant::get_global_constant);
+
+ String cc;
+
+ for(int i=0;i<GlobalConstants::get_global_constant_count();i++) {
+
+ if (i>0)
+ cc+=",";
+ cc+=GlobalConstants::get_global_constant_name(i);
+ }
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"constant",PROPERTY_HINT_ENUM,cc),_SCS("set_global_constant"),_SCS("get_global_constant"));
+}
+
+VisualScriptGlobalConstant::VisualScriptGlobalConstant() {
+
+ index=0;
+}
+
+//////////////////////////////////////////
+////////////////CLASSCONSTANT///////////
+//////////////////////////////////////////
+
+int VisualScriptClassConstant::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptClassConstant::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptClassConstant::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptClassConstant::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptClassConstant::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptClassConstant::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptClassConstant::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::INT,"value");
+}
+
+
+String VisualScriptClassConstant::get_caption() const {
+
+ return "ClassConst";
+}
+
+String VisualScriptClassConstant::get_text() const {
+
+ return String(base_type)+"."+String(name);
+}
+
+void VisualScriptClassConstant::set_class_constant(const StringName& p_which) {
+
+ name=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptClassConstant::get_class_constant() {
+ return name;
+}
+
+
+void VisualScriptClassConstant::set_base_type(const StringName& p_which) {
+
+ base_type=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptClassConstant::get_base_type() {
+ return base_type;
+}
+
+class VisualScriptNodeInstanceClassConstant : public VisualScriptNodeInstance {
+public:
+
+ int value;
+ bool valid;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (!valid) {
+ r_error_str="Invalid constant name, pick a valid class constant.";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+ *p_outputs[0] = value;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptClassConstant::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceClassConstant * instance = memnew(VisualScriptNodeInstanceClassConstant );
+ instance->value=ObjectTypeDB::get_integer_constant(base_type,name,&instance->valid);
+ return instance;
+}
+
+void VisualScriptClassConstant::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="constant") {
+
+ List<String> constants;
+ ObjectTypeDB::get_integer_constant_list(base_type,&constants,true);
+
+ property.hint_string="";
+ for(List<String>::Element *E=constants.front();E;E=E->next()) {
+ if (property.hint_string!=String()) {
+ property.hint_string+=",";
+ }
+ property.hint_string+=E->get();
+ }
+ }
+}
+
+void VisualScriptClassConstant::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_class_constant","name"),&VisualScriptClassConstant::set_class_constant);
+ ObjectTypeDB::bind_method(_MD("get_class_constant"),&VisualScriptClassConstant::get_class_constant);
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","name"),&VisualScriptClassConstant::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptClassConstant::get_base_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"constant",PROPERTY_HINT_ENUM,""),_SCS("set_class_constant"),_SCS("get_class_constant"));
+}
+
+VisualScriptClassConstant::VisualScriptClassConstant() {
+
+ base_type="Object";
+}
+
+
+//////////////////////////////////////////
+////////////////BASICTYPECONSTANT///////////
+//////////////////////////////////////////
+
+int VisualScriptBasicTypeConstant::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptBasicTypeConstant::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptBasicTypeConstant::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptBasicTypeConstant::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptBasicTypeConstant::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptBasicTypeConstant::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptBasicTypeConstant::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::INT,"value");
+}
+
+
+String VisualScriptBasicTypeConstant::get_caption() const {
+
+ return "BasicConst";
+}
+
+String VisualScriptBasicTypeConstant::get_text() const {
+
+ return Variant::get_type_name(type)+"."+String(name);
+}
+
+void VisualScriptBasicTypeConstant::set_basic_type_constant(const StringName& p_which) {
+
+ name=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptBasicTypeConstant::get_basic_type_constant() const {
+ return name;
+}
+
+
+void VisualScriptBasicTypeConstant::set_basic_type(Variant::Type p_which) {
+
+ type=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const {
+ return type;
+}
+
+class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance {
+public:
+
+ int value;
+ bool valid;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (!valid) {
+ r_error_str="Invalid constant name, pick a valid basic type constant.";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+ *p_outputs[0] = value;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptBasicTypeConstant::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceBasicTypeConstant * instance = memnew(VisualScriptNodeInstanceBasicTypeConstant );
+ instance->value=Variant::get_numeric_constant_value(type,name,&instance->valid);
+ return instance;
+}
+
+void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="constant") {
+
+ List<StringName> constants;
+ Variant::get_numeric_constants_for_type(type,&constants);
+
+ if (constants.size()==0) {
+ property.usage=0;
+ return;
+ }
+ property.hint_string="";
+ for(List<StringName>::Element *E=constants.front();E;E=E->next()) {
+ if (property.hint_string!=String()) {
+ property.hint_string+=",";
+ }
+ property.hint_string+=String(E->get());
+ }
+
+ }
+}
+
+void VisualScriptBasicTypeConstant::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_basic_type","name"),&VisualScriptBasicTypeConstant::set_basic_type);
+ ObjectTypeDB::bind_method(_MD("get_basic_type"),&VisualScriptBasicTypeConstant::get_basic_type);
+
+ ObjectTypeDB::bind_method(_MD("set_basic_type_constant","name"),&VisualScriptBasicTypeConstant::set_basic_type_constant);
+ ObjectTypeDB::bind_method(_MD("get_basic_type_constant"),&VisualScriptBasicTypeConstant::get_basic_type_constant);
+
+
+ String argt="Null";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"basic_type",PROPERTY_HINT_ENUM,argt),_SCS("set_basic_type"),_SCS("get_basic_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"constant",PROPERTY_HINT_ENUM,""),_SCS("set_basic_type_constant"),_SCS("get_basic_type_constant"));
+}
+
+VisualScriptBasicTypeConstant::VisualScriptBasicTypeConstant() {
+
+ type=Variant::NIL;
+}
+
+
+
+//////////////////////////////////////////
+////////////////MATHCONSTANT///////////
+//////////////////////////////////////////
+
+
+const char* VisualScriptMathConstant::const_name[MATH_CONSTANT_MAX]={
+ "One",
+ "PI",
+ "PIx2",
+ "PI/2",
+ "E",
+ "Sqrt2",
+};
+
+double VisualScriptMathConstant::const_value[MATH_CONSTANT_MAX]={
+ 1.0,
+ Math_PI,
+ Math_PI*2,
+ Math_PI*0.5,
+ 2.71828182845904523536,
+ Math::sqrt(2.0)
+};
+
+
+int VisualScriptMathConstant::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptMathConstant::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptMathConstant::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptMathConstant::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptMathConstant::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptMathConstant::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptMathConstant::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::REAL,"value");
+}
+
+
+String VisualScriptMathConstant::get_caption() const {
+
+ return "MathConst";
+}
+
+String VisualScriptMathConstant::get_text() const {
+
+ return const_name[constant];
+}
+
+void VisualScriptMathConstant::set_math_constant(MathConstant p_which) {
+
+ constant=p_which;
+ _change_notify();
+ ports_changed_notify();
+}
+
+VisualScriptMathConstant::MathConstant VisualScriptMathConstant::get_math_constant() {
+ return constant;
+}
+
+class VisualScriptNodeInstanceMathConstant : public VisualScriptNodeInstance {
+public:
+
+ float value;
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0]=value;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptMathConstant::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceMathConstant * instance = memnew(VisualScriptNodeInstanceMathConstant );
+ instance->value=const_value[constant];
+ return instance;
+}
+
+
+void VisualScriptMathConstant::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_math_constant","which"),&VisualScriptMathConstant::set_math_constant);
+ ObjectTypeDB::bind_method(_MD("get_math_constant"),&VisualScriptMathConstant::get_math_constant);
+
+ String cc;
+
+ for(int i=0;i<MATH_CONSTANT_MAX;i++) {
+
+ if (i>0)
+ cc+=",";
+ cc+=const_name[i];
+ }
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"constant",PROPERTY_HINT_ENUM,cc),_SCS("set_math_constant"),_SCS("get_math_constant"));
+}
+
+VisualScriptMathConstant::VisualScriptMathConstant() {
+
+ constant=MATH_CONSTANT_ONE;
+}
+
+
+
+//////////////////////////////////////////
+////////////////GLOBALSINGLETON///////////
+//////////////////////////////////////////
+
+int VisualScriptEngineSingleton::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptEngineSingleton::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptEngineSingleton::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptEngineSingleton::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptEngineSingleton::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptEngineSingleton::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptEngineSingleton::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"instance");
+}
+
+
+String VisualScriptEngineSingleton::get_caption() const {
+
+ return "EngineSingleton";
+}
+
+String VisualScriptEngineSingleton::get_text() const {
+
+ return singleton;
+}
+
+void VisualScriptEngineSingleton::set_singleton(const String& p_string) {
+
+ singleton=p_string;
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+String VisualScriptEngineSingleton::get_singleton() {
+ return singleton;
+}
+
+
+
+class VisualScriptNodeInstanceEngineSingleton : public VisualScriptNodeInstance {
+public:
+
+ Object* singleton;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0]=singleton;
+ return 0;
+ }
+
+};
+
+VisualScriptNodeInstance* VisualScriptEngineSingleton::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceEngineSingleton * instance = memnew(VisualScriptNodeInstanceEngineSingleton );
+ instance->singleton=Globals::get_singleton()->get_singleton_object(singleton);
+ return instance;
+}
+
+VisualScriptEngineSingleton::TypeGuess VisualScriptEngineSingleton::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+ Object *obj=Globals::get_singleton()->get_singleton_object(singleton);
+ TypeGuess tg;
+ tg.type=Variant::OBJECT;
+ if (obj) {
+ tg.obj_type=obj->get_type();
+ tg.script=obj->get_script();
+ }
+
+ return tg;
+}
+
+
+void VisualScriptEngineSingleton::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_singleton","name"),&VisualScriptEngineSingleton::set_singleton);
+ ObjectTypeDB::bind_method(_MD("get_singleton"),&VisualScriptEngineSingleton::get_singleton);
+
+ String cc;
+
+ List<Globals::Singleton> singletons;
+
+ Globals::get_singleton()->get_singletons(&singletons);
+
+ for (List<Globals::Singleton>::Element *E=singletons.front();E;E=E->next()) {
+ if (E->get().name=="VS" || E->get().name=="PS" || E->get().name=="PS2D" || E->get().name=="AS" || E->get().name=="TS" || E->get().name=="SS" || E->get().name=="SS2D")
+ continue; //skip these, too simple named
+
+ if (cc!=String())
+ cc+=",";
+ cc+=E->get().name;
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"constant",PROPERTY_HINT_ENUM,cc),_SCS("set_singleton"),_SCS("get_singleton"));
+}
+
+VisualScriptEngineSingleton::VisualScriptEngineSingleton() {
+
+ singleton=String();
+}
+
+
+
+//////////////////////////////////////////
+////////////////GETNODE///////////
+//////////////////////////////////////////
+
+int VisualScriptSceneNode::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptSceneNode::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptSceneNode::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptSceneNode::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptSceneNode::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptSceneNode::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptSceneNode::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"node");
+}
+
+
+String VisualScriptSceneNode::get_caption() const {
+
+ return "SceneNode";
+}
+
+String VisualScriptSceneNode::get_text() const {
+
+ return path.simplified();
+}
+
+void VisualScriptSceneNode::set_node_path(const NodePath& p_path) {
+
+ path=p_path;
+ _change_notify();
+ ports_changed_notify();
+}
+
+NodePath VisualScriptSceneNode::get_node_path() {
+ return path;
+}
+
+
+class VisualScriptNodeInstanceSceneNode : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptSceneNode *node;
+ VisualScriptInstance *instance;
+ NodePath path;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Base object is not a Node!";
+ return 0;
+ }
+
+
+
+ Node* another = node->get_node(path);
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Path does not lead Node!";
+ return 0;
+ }
+
+ *p_outputs[0]=another;
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSceneNode::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSceneNode * instance = memnew(VisualScriptNodeInstanceSceneNode );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->path=path;
+ return instance;
+}
+
+
+
+
+#ifdef TOOLS_ENABLED
+
+static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) {
+
+ if (p_edited_scene!=p_current_node && p_current_node->get_owner()!=p_edited_scene)
+ return NULL;
+
+ Ref<Script> scr = p_current_node->get_script();
+
+ if (scr.is_valid() && scr==script)
+ return p_current_node;
+
+ for(int i=0;i<p_current_node->get_child_count();i++) {
+ Node *n = _find_script_node(p_edited_scene,p_current_node->get_child(i),script);
+ if (n)
+ return n;
+ }
+
+ return NULL;
+}
+
+#endif
+
+VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+
+ VisualScriptSceneNode::TypeGuess tg;
+ tg.type=Variant::OBJECT;
+ tg.obj_type="Node";
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return tg;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return tg;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return tg;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return tg;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return tg;
+
+ Node* another = script_node->get_node(path);
+
+ if (another) {
+ tg.obj_type=another->get_type();
+ tg.script=another->get_script();
+ }
+#endif
+ return tg;
+
+}
+
+
+void VisualScriptSceneNode::_validate_property(PropertyInfo& property) const {
+
+#ifdef TOOLS_ENABLED
+ if (property.name=="node_path") {
+
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return;
+
+ property.hint_string=script_node->get_path();
+ }
+#endif
+}
+
+void VisualScriptSceneNode::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_node_path","path"),&VisualScriptSceneNode::set_node_path);
+ ObjectTypeDB::bind_method(_MD("get_node_path"),&VisualScriptSceneNode::get_node_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"node_path",PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE),_SCS("set_node_path"),_SCS("get_node_path"));
+}
+
+VisualScriptSceneNode::VisualScriptSceneNode() {
+
+ path=String(".");
+}
+
+
+//////////////////////////////////////////
+////////////////SceneTree///////////
+//////////////////////////////////////////
+
+int VisualScriptSceneTree::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptSceneTree::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptSceneTree::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptSceneTree::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptSceneTree::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"instance");
+}
+
+
+String VisualScriptSceneTree::get_caption() const {
+
+ return "SceneTree";
+}
+
+String VisualScriptSceneTree::get_text() const {
+
+ return "";
+}
+
+
+class VisualScriptNodeInstanceSceneTree : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptSceneTree *node;
+ VisualScriptInstance *instance;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Base object is not a Node!";
+ return 0;
+ }
+
+ SceneTree* tree = node->get_tree();
+ if (!tree) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Attempt to get SceneTree while node is not in the active tree.";
+ return 0;
+ }
+
+ *p_outputs[0]=tree;
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSceneTree::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSceneTree * instance = memnew(VisualScriptNodeInstanceSceneTree );
+ instance->node=this;
+ instance->instance=p_instance;
+ return instance;
+}
+
+VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+ TypeGuess tg;
+ tg.type=Variant::OBJECT;
+ tg.obj_type="SceneTree";
+ return tg;
+}
+
+void VisualScriptSceneTree::_validate_property(PropertyInfo& property) const {
+
+}
+
+void VisualScriptSceneTree::_bind_methods() {
+
+}
+
+VisualScriptSceneTree::VisualScriptSceneTree() {
+
+}
+
+
+//////////////////////////////////////////
+////////////////RESPATH///////////
+//////////////////////////////////////////
+
+int VisualScriptResourcePath::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptResourcePath::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptResourcePath::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptResourcePath::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptResourcePath::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptResourcePath::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptResourcePath::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::STRING,"path");
+}
+
+
+String VisualScriptResourcePath::get_caption() const {
+
+ return "ResourcePath";
+}
+
+String VisualScriptResourcePath::get_text() const {
+
+ return path;
+}
+
+void VisualScriptResourcePath::set_resource_path(const String& p_path) {
+
+ path=p_path;
+ _change_notify();
+ ports_changed_notify();
+}
+
+String VisualScriptResourcePath::get_resource_path() {
+ return path;
+}
+
+
+class VisualScriptNodeInstanceResourcePath : public VisualScriptNodeInstance {
+public:
+
+ String path;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0] = path;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptResourcePath::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceResourcePath * instance = memnew(VisualScriptNodeInstanceResourcePath );
+ instance->path=path;
+ return instance;
+}
+
+
+
+void VisualScriptResourcePath::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_resource_path","path"),&VisualScriptResourcePath::set_resource_path);
+ ObjectTypeDB::bind_method(_MD("get_resource_path"),&VisualScriptResourcePath::get_resource_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"path",PROPERTY_HINT_FILE),_SCS("set_resource_path"),_SCS("get_resource_path"));
+}
+
+VisualScriptResourcePath::VisualScriptResourcePath() {
+
+ path="";
+}
+
+
+
+//////////////////////////////////////////
+////////////////SELF///////////
+//////////////////////////////////////////
+
+int VisualScriptSelf::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptSelf::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptSelf::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptSelf::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptSelf::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::OBJECT,"instance");
+}
+
+
+String VisualScriptSelf::get_caption() const {
+
+ return "Self";
+}
+
+String VisualScriptSelf::get_text() const {
+
+ if (get_visual_script().is_valid())
+ return get_visual_script()->get_instance_base_type();
+ else
+ return "";
+}
+
+
+class VisualScriptNodeInstanceSelf : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0] = instance->get_owner_ptr();
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSelf::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSelf * instance = memnew(VisualScriptNodeInstanceSelf );
+ instance->instance=p_instance;
+ return instance;
+}
+
+VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess* p_inputs, int p_output) const {
+
+ VisualScriptSceneNode::TypeGuess tg;
+ tg.type=Variant::OBJECT;
+ tg.obj_type="Object";
+
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return tg;
+
+ tg.obj_type=script->get_instance_base_type();
+ tg.script=script;
+
+ return tg;
+
+
+}
+
+
+void VisualScriptSelf::_bind_methods() {
+
+}
+
+VisualScriptSelf::VisualScriptSelf() {
+
+
+}
+
+//////////////////////////////////////////
+////////////////CUSTOM (SCRIPTED)///////////
+//////////////////////////////////////////
+
+int VisualScriptCustomNode::get_output_sequence_port_count() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_count")) {
+ return get_script_instance()->call("_get_output_sequence_port_count");
+ }
+ return 0;
+}
+
+bool VisualScriptCustomNode::has_input_sequence_port() const{
+
+ if (get_script_instance() && get_script_instance()->has_method("_has_input_sequence_port")) {
+ return get_script_instance()->call("_has_input_sequence_port");
+ }
+ return false;
+}
+
+int VisualScriptCustomNode::get_input_value_port_count() const{
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_count")) {
+ return get_script_instance()->call("_get_input_value_port_count");
+ }
+ return 0;
+}
+int VisualScriptCustomNode::get_output_value_port_count() const{
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_count")) {
+ return get_script_instance()->call("_get_output_value_port_count");
+ }
+ return 0;
+}
+
+String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const {
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_text")) {
+ return get_script_instance()->call("_get_output_sequence_port_text",p_port);
+ }
+
+ return String();
+}
+
+PropertyInfo VisualScriptCustomNode::get_input_value_port_info(int p_idx) const{
+
+ PropertyInfo info;
+ if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_type")) {
+ info.type=Variant::Type(int(get_script_instance()->call("_get_input_value_port_type",p_idx)));
+ }
+ if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_name")) {
+ info.name=get_script_instance()->call("_get_input_value_port_name",p_idx);
+ }
+ return info;
+}
+
+PropertyInfo VisualScriptCustomNode::get_output_value_port_info(int p_idx) const{
+
+ PropertyInfo info;
+ if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_type")) {
+ info.type=Variant::Type(int(get_script_instance()->call("_get_output_value_port_type",p_idx)));
+ }
+ if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_name")) {
+ info.name=get_script_instance()->call("_get_output_value_port_name",p_idx);
+ }
+ return info;
+}
+
+
+String VisualScriptCustomNode::get_caption() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_caption")) {
+ return get_script_instance()->call("_get_caption");
+ }
+ return "CustomNode";
+}
+
+String VisualScriptCustomNode::get_text() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_text")) {
+ return get_script_instance()->call("_get_text");
+ }
+ return "";
+}
+
+String VisualScriptCustomNode::get_category() const {
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_category")) {
+ return get_script_instance()->call("_get_category");
+ }
+ return "custom";
+}
+
+class VisualScriptNodeInstanceCustomNode : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ VisualScriptCustomNode *node;
+ int in_count;
+ int out_count;
+ int work_mem_size;
+
+ virtual int get_working_memory_size() const { return work_mem_size; }
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (node->get_script_instance()) {
+#ifdef DEBUG_ENABLED
+ if (!node->get_script_instance()->has_method(VisualScriptLanguage::singleton->_step)) {
+ r_error_str=RTR("Custom node has no _step() method, can't process graph.");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+#endif
+ Array in_values(true);
+ Array out_values(true);
+ Array work_mem(true);
+
+ in_values.resize(in_count);
+
+ for(int i=0;i<in_count;i++) {
+ in_values[i]=p_inputs[i];
+ }
+
+ out_values.resize(in_count);
+
+ work_mem.resize(work_mem_size);
+
+ for(int i=0;i<work_mem_size;i++) {
+ work_mem[i]=p_working_mem[i];
+ }
+
+ int ret_out;
+
+ Variant ret = node->get_script_instance()->call(VisualScriptLanguage::singleton->_step,in_values,out_values,p_start_mode,work_mem);
+ if (ret.get_type()==Variant::STRING) {
+ r_error_str=ret;
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ } else if (ret.is_num()) {
+ ret_out=ret;
+ } else {
+ r_error_str=RTR("Invalid return value from _step(), must be integer (seq out), or string (error).");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+ for(int i=0;i<out_count;i++) {
+ if (i<out_values.size()) {
+ *p_outputs[i]=out_values[i];
+ }
+ }
+
+ for(int i=0;i<work_mem_size;i++) {
+ if (i<work_mem.size()) {
+ p_working_mem[i]=work_mem[i];
+ }
+ }
+
+ return ret_out;
+
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptCustomNode::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceCustomNode * instance = memnew(VisualScriptNodeInstanceCustomNode );
+ instance->instance=p_instance;
+ instance->in_count=get_input_value_port_count();
+ instance->out_count=get_output_value_port_count();
+
+
+ if (get_script_instance() && get_script_instance()->has_method("_get_working_memory_size")) {
+ instance->work_mem_size = get_script_instance()->call("_get_working_memory_size");
+ } else {
+ instance->work_mem_size=0;
+ }
+
+ return instance;
+}
+
+
+
+void VisualScriptCustomNode::_bind_methods() {
+
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_output_sequence_port_count") );
+ BIND_VMETHOD( MethodInfo(Variant::BOOL,"_has_input_sequence_port") );
+
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_output_sequence_port_text",PropertyInfo(Variant::INT,"idx")) );
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_input_value_port_count") );
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_output_value_port_count") );
+
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_input_value_port_type",PropertyInfo(Variant::INT,"idx")) );
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_input_value_port_name",PropertyInfo(Variant::INT,"idx")) );
+
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_output_value_port_type",PropertyInfo(Variant::INT,"idx")) );
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_output_value_port_name",PropertyInfo(Variant::INT,"idx")) );
+
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_caption") );
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_text") );
+ BIND_VMETHOD( MethodInfo(Variant::STRING,"_get_category") );
+
+ BIND_VMETHOD( MethodInfo(Variant::INT,"_get_working_memory_size") );
+ BIND_VMETHOD( MethodInfo(Variant::NIL,"_step:Variant",PropertyInfo(Variant::ARRAY,"inputs"),PropertyInfo(Variant::ARRAY,"outputs"),PropertyInfo(Variant::INT,"start_mode"),PropertyInfo(Variant::ARRAY,"working_mem")) );
+
+ BIND_CONSTANT( START_MODE_BEGIN_SEQUENCE );
+ BIND_CONSTANT( START_MODE_CONTINUE_SEQUENCE );
+ BIND_CONSTANT( START_MODE_RESUME_YIELD );
+
+ BIND_CONSTANT( STEP_PUSH_STACK_BIT );
+ BIND_CONSTANT( STEP_GO_BACK_BIT );
+ BIND_CONSTANT( STEP_NO_ADVANCE_BIT );
+ BIND_CONSTANT( STEP_EXIT_FUNCTION_BIT );
+ BIND_CONSTANT( STEP_YIELD_BIT );
+
+}
+
+VisualScriptCustomNode::VisualScriptCustomNode() {
+
+
+}
+
+//////////////////////////////////////////
+////////////////SUBCALL///////////
+//////////////////////////////////////////
+
+int VisualScriptSubCall::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptSubCall::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptSubCall::get_input_value_port_count() const{
+
+ Ref<Script> script = get_script();
+
+ if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
+
+ MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
+ return mi.arguments.size();
+ }
+
+ return 0;
+}
+int VisualScriptSubCall::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptSubCall::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptSubCall::get_input_value_port_info(int p_idx) const{
+
+ Ref<Script> script = get_script();
+ if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
+
+ MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
+ return mi.arguments[p_idx];
+ }
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptSubCall::get_output_value_port_info(int p_idx) const{
+
+ Ref<Script> script = get_script();
+ if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
+ MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
+ return mi.return_val;
+ }
+ return PropertyInfo();
+}
+
+
+String VisualScriptSubCall::get_caption() const {
+
+ return "SubCall";
+}
+
+
+String VisualScriptSubCall::get_text() const {
+
+ Ref<Script> script = get_script();
+ if (script.is_valid()) {
+ if (script->get_name()!=String())
+ return script->get_name();
+ if (script->get_path().is_resource_file())
+ return script->get_path().get_file();
+ return script->get_type();
+ }
+ return "";
+}
+
+String VisualScriptSubCall::get_category() const {
+
+ return "custom";
+}
+
+class VisualScriptNodeInstanceSubCall : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ VisualScriptSubCall *subcall;
+ int input_args;
+ bool valid;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (!valid) {
+ r_error_str="Node requires a script with a _subcall(<args>) method to work.";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+ *p_outputs[0]=subcall->call(VisualScriptLanguage::singleton->_subcall,p_inputs,input_args,r_error_str);
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptSubCall::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceSubCall * instance = memnew(VisualScriptNodeInstanceSubCall );
+ instance->instance=p_instance;
+ Ref<Script> script = get_script();
+ if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
+ instance->valid=true;
+ instance->input_args=get_input_value_port_count();
+ } else {
+ instance->valid=false;
+ }
+ return instance;
+}
+
+
+
+void VisualScriptSubCall::_bind_methods() {
+
+ BIND_VMETHOD( MethodInfo(Variant::NIL,"_subcall",PropertyInfo(Variant::NIL,"arguments:Variant")) );
+
+}
+
+VisualScriptSubCall::VisualScriptSubCall() {
+
+
+}
+
+//////////////////////////////////////////
+////////////////Comment///////////
+//////////////////////////////////////////
+
+int VisualScriptComment::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptComment::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptComment::get_input_value_port_count() const{
+ return 0;
+}
+int VisualScriptComment::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptComment::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptComment::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptComment::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptComment::get_caption() const {
+
+ return title;
+}
+
+
+String VisualScriptComment::get_text() const {
+
+ return description;
+}
+
+void VisualScriptComment::set_title(const String& p_title) {
+
+
+ if (title==p_title)
+ return;
+ title=p_title;
+ ports_changed_notify();
+}
+
+String VisualScriptComment::get_title() const{
+
+ return title;
+}
+
+void VisualScriptComment::set_description(const String& p_description){
+
+ if (description==p_description)
+ return;
+ description=p_description;
+ ports_changed_notify();
+
+}
+String VisualScriptComment::get_description() const{
+
+ return description;
+}
+
+void VisualScriptComment::set_size(const Size2& p_size){
+
+ if (size==p_size)
+ return;
+ size=p_size;
+ ports_changed_notify();
+
+}
+Size2 VisualScriptComment::get_size() const{
+
+ return size;
+}
+
+
+String VisualScriptComment::get_category() const {
+
+ return "data";
+}
+
+class VisualScriptNodeInstanceComment : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptComment::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceComment * instance = memnew(VisualScriptNodeInstanceComment );
+ instance->instance=p_instance;
+ return instance;
+}
+
+
+
+void VisualScriptComment::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_title","title"),&VisualScriptComment::set_title);
+ ObjectTypeDB::bind_method(_MD("get_title"),&VisualScriptComment::get_title);
+
+ ObjectTypeDB::bind_method(_MD("set_description","description"),&VisualScriptComment::set_description);
+ ObjectTypeDB::bind_method(_MD("get_description"),&VisualScriptComment::get_description);
+
+ ObjectTypeDB::bind_method(_MD("set_size","size"),&VisualScriptComment::set_size);
+ ObjectTypeDB::bind_method(_MD("get_size"),&VisualScriptComment::get_size);
+
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"title"),_SCS("set_title"),_SCS("get_title"));
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"description",PROPERTY_HINT_MULTILINE_TEXT),_SCS("set_description"),_SCS("get_description"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"size"),_SCS("set_size"),_SCS("get_size"));
+
+}
+
+VisualScriptComment::VisualScriptComment() {
+
+ title="Comment";
+ size=Size2(150,150);
+}
+
+
+//////////////////////////////////////////
+////////////////Constructor///////////
+//////////////////////////////////////////
+
+int VisualScriptConstructor::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptConstructor::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptConstructor::get_input_value_port_count() const{
+ return constructor.arguments.size();
+}
+int VisualScriptConstructor::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptConstructor::get_output_sequence_port_text(int p_port) const {
+
+ return "";
+}
+
+PropertyInfo VisualScriptConstructor::get_input_value_port_info(int p_idx) const{
+
+ return constructor.arguments[p_idx];
+}
+
+PropertyInfo VisualScriptConstructor::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type,"value");
+}
+
+
+String VisualScriptConstructor::get_caption() const {
+
+ return "Construct";
+}
+
+
+String VisualScriptConstructor::get_text() const {
+
+ return "new "+Variant::get_type_name(type)+"()";
+}
+
+
+String VisualScriptConstructor::get_category() const {
+
+ return "functions";
+}
+
+void VisualScriptConstructor::set_constructor_type(Variant::Type p_type) {
+
+ if (type==p_type)
+ return;
+
+ type=p_type;
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptConstructor::get_constructor_type() const {
+
+ return type;
+}
+
+void VisualScriptConstructor::set_constructor(const Dictionary& p_info) {
+
+ constructor=MethodInfo::from_dict(p_info);
+ ports_changed_notify();
+}
+
+Dictionary VisualScriptConstructor::get_constructor() const {
+
+ return constructor;
+}
+
+
+class VisualScriptNodeInstanceConstructor : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ Variant::Type type;
+ int argcount;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ Variant::CallError ce;
+ *p_outputs[0]=Variant::construct(type,p_inputs,argcount,ce);
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ r_error_str="Invalid arguments for constructor";
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptConstructor::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceConstructor * instance = memnew(VisualScriptNodeInstanceConstructor );
+ instance->instance=p_instance;
+ instance->type=type;
+ instance->argcount=constructor.arguments.size();
+ return instance;
+}
+
+
+
+void VisualScriptConstructor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_constructor_type","type"),&VisualScriptConstructor::set_constructor_type);
+ ObjectTypeDB::bind_method(_MD("get_constructor_type"),&VisualScriptConstructor::get_constructor_type);
+
+ ObjectTypeDB::bind_method(_MD("set_constructor","constructor"),&VisualScriptConstructor::set_constructor);
+ ObjectTypeDB::bind_method(_MD("get_constructor"),&VisualScriptConstructor::get_constructor);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"type",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_constructor_type"),_SCS("get_constructor_type"));
+ ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"constructor",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_constructor"),_SCS("get_constructor"));
+
+}
+
+VisualScriptConstructor::VisualScriptConstructor() {
+
+ type=Variant::NIL;
+
+}
+
+static Map<String,Pair<Variant::Type,MethodInfo> > constructor_map;
+
+static Ref<VisualScriptNode> create_constructor_node(const String& p_name) {
+
+ ERR_FAIL_COND_V(!constructor_map.has(p_name),Ref<VisualScriptNode>());
+
+ Ref<VisualScriptConstructor> vsc;
+ vsc.instance();
+ vsc->set_constructor_type(constructor_map[p_name].first);
+ vsc->set_constructor(constructor_map[p_name].second);
+
+ return vsc;
+}
+
+//////////////////////////////////////////
+////////////////LocalVar///////////
+//////////////////////////////////////////
+
+int VisualScriptLocalVar::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptLocalVar::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptLocalVar::get_input_value_port_count() const{
+ return 0;
+}
+int VisualScriptLocalVar::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptLocalVar::get_output_sequence_port_text(int p_port) const {
+
+ return "";
+}
+
+PropertyInfo VisualScriptLocalVar::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+PropertyInfo VisualScriptLocalVar::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type,"get");
+}
+
+
+String VisualScriptLocalVar::get_caption() const {
+
+ return "LocalVarGet";
+}
+
+
+String VisualScriptLocalVar::get_text() const {
+
+ return name;
+}
+
+
+String VisualScriptLocalVar::get_category() const {
+
+ return "data";
+}
+
+
+void VisualScriptLocalVar::set_var_name(const StringName& p_name) {
+
+ if (name==p_name)
+ return;
+
+ name=p_name;
+ ports_changed_notify();
+
+}
+
+StringName VisualScriptLocalVar::get_var_name() const {
+
+ return name;
+}
+
+void VisualScriptLocalVar::set_var_type(Variant::Type p_type) {
+
+ type=p_type;
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptLocalVar::get_var_type() const {
+
+ return type;
+}
+
+
+class VisualScriptNodeInstanceLocalVar : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ StringName name;
+
+
+ virtual int get_working_memory_size() const { return 1; }
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_outputs[0]=*p_working_mem;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptLocalVar::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceLocalVar * instance = memnew(VisualScriptNodeInstanceLocalVar );
+ instance->instance=p_instance;
+ instance->name=name;
+
+ return instance;
+}
+
+
+
+void VisualScriptLocalVar::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_var_name","name"),&VisualScriptLocalVar::set_var_name);
+ ObjectTypeDB::bind_method(_MD("get_var_name"),&VisualScriptLocalVar::get_var_name);
+
+ ObjectTypeDB::bind_method(_MD("set_var_type","type"),&VisualScriptLocalVar::set_var_type);
+ ObjectTypeDB::bind_method(_MD("get_var_type"),&VisualScriptLocalVar::get_var_type);
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"variable/name"),_SCS("set_var_name"),_SCS("get_var_name"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"variable/type",PROPERTY_HINT_ENUM,argt),_SCS("set_var_type"),_SCS("get_var_type"));
+
+
+}
+
+VisualScriptLocalVar::VisualScriptLocalVar() {
+
+ name="new_local";
+ type=Variant::NIL;
+
+}
+
+//////////////////////////////////////////
+////////////////LocalVar///////////
+//////////////////////////////////////////
+
+int VisualScriptLocalVarSet::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptLocalVarSet::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptLocalVarSet::get_input_value_port_count() const{
+ return 1;
+}
+int VisualScriptLocalVarSet::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptLocalVarSet::get_output_sequence_port_text(int p_port) const {
+
+ return "";
+}
+
+PropertyInfo VisualScriptLocalVarSet::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type,"set");
+}
+PropertyInfo VisualScriptLocalVarSet::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type,"get");
+}
+
+
+String VisualScriptLocalVarSet::get_caption() const {
+
+ return "LocalVarSet";
+}
+
+
+String VisualScriptLocalVarSet::get_text() const {
+
+ return name;
+}
+
+
+String VisualScriptLocalVarSet::get_category() const {
+
+ return "data";
+}
+
+
+void VisualScriptLocalVarSet::set_var_name(const StringName& p_name) {
+
+ if (name==p_name)
+ return;
+
+ name=p_name;
+ ports_changed_notify();
+
+}
+
+StringName VisualScriptLocalVarSet::get_var_name() const {
+
+ return name;
+}
+
+void VisualScriptLocalVarSet::set_var_type(Variant::Type p_type) {
+
+ type=p_type;
+ ports_changed_notify();
+}
+
+Variant::Type VisualScriptLocalVarSet::get_var_type() const {
+
+ return type;
+}
+
+
+class VisualScriptNodeInstanceLocalVarSet : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ StringName name;
+
+
+ virtual int get_working_memory_size() const { return 1; }
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ *p_working_mem=*p_inputs[0];
+ *p_outputs[0]=*p_working_mem;
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptLocalVarSet::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceLocalVarSet * instance = memnew(VisualScriptNodeInstanceLocalVarSet );
+ instance->instance=p_instance;
+ instance->name=name;
+
+ return instance;
+}
+
+
+
+void VisualScriptLocalVarSet::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_var_name","name"),&VisualScriptLocalVarSet::set_var_name);
+ ObjectTypeDB::bind_method(_MD("get_var_name"),&VisualScriptLocalVarSet::get_var_name);
+
+ ObjectTypeDB::bind_method(_MD("set_var_type","type"),&VisualScriptLocalVarSet::set_var_type);
+ ObjectTypeDB::bind_method(_MD("get_var_type"),&VisualScriptLocalVarSet::get_var_type);
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"variable/name"),_SCS("set_var_name"),_SCS("get_var_name"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"variable/type",PROPERTY_HINT_ENUM,argt),_SCS("set_var_type"),_SCS("get_var_type"));
+
+
+}
+
+VisualScriptLocalVarSet::VisualScriptLocalVarSet() {
+
+ name="new_local";
+ type=Variant::NIL;
+
+}
+
+
+//////////////////////////////////////////
+////////////////LocalVar///////////
+//////////////////////////////////////////
+
+int VisualScriptInputAction::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptInputAction::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptInputAction::get_input_value_port_count() const{
+ return 0;
+}
+int VisualScriptInputAction::get_output_value_port_count() const{
+
+ return 1;
+}
+
+String VisualScriptInputAction::get_output_sequence_port_text(int p_port) const {
+
+ return "";
+}
+
+PropertyInfo VisualScriptInputAction::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(Variant::BOOL,"pressed");
+}
+
+
+String VisualScriptInputAction::get_caption() const {
+
+
+ return "Action";
+}
+
+
+String VisualScriptInputAction::get_text() const {
+
+ switch(mode) {
+ case MODE_PRESSED: {
+ return name;
+ } break;
+ case MODE_RELEASED: {
+ return "not "+name;
+ } break;
+ case MODE_JUST_PRESSED: {
+ return String(name)+" "+TTR("just pressed");
+ } break;
+ case MODE_JUST_RELEASED: {
+ return String(name)+" "+TTR("just released");
+ } break;
+ }
+
+ return String();
+}
+
+
+String VisualScriptInputAction::get_category() const {
+
+ return "data";
+}
+
+
+void VisualScriptInputAction::set_action_name(const StringName& p_name) {
+
+ if (name==p_name)
+ return;
+
+ name=p_name;
+ ports_changed_notify();
+
+}
+
+StringName VisualScriptInputAction::get_action_name() const {
+
+ return name;
+}
+
+void VisualScriptInputAction::set_action_mode(Mode p_mode) {
+
+ if (mode==p_mode)
+ return;
+
+ mode=p_mode;
+ ports_changed_notify();
+
+}
+VisualScriptInputAction::Mode VisualScriptInputAction::get_action_mode() const {
+
+ return mode;
+}
+
+
+class VisualScriptNodeInstanceInputAction : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ StringName action;
+ VisualScriptInputAction::Mode mode;
+
+
+ virtual int get_working_memory_size() const { return 1; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ switch(mode) {
+ case VisualScriptInputAction::MODE_PRESSED: {
+ *p_outputs[0]=Input::get_singleton()->is_action_pressed(action);
+ } break;
+ case VisualScriptInputAction::MODE_RELEASED: {
+ *p_outputs[0]=!Input::get_singleton()->is_action_pressed(action);
+ } break;
+ case VisualScriptInputAction::MODE_JUST_PRESSED: {
+ *p_outputs[0]=Input::get_singleton()->is_action_just_pressed(action);
+ } break;
+ case VisualScriptInputAction:: MODE_JUST_RELEASED: {
+ *p_outputs[0]=Input::get_singleton()->is_action_just_released(action);
+ } break;
+
+ }
+
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptInputAction::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceInputAction * instance = memnew(VisualScriptNodeInstanceInputAction );
+ instance->instance=p_instance;
+ instance->action=name;
+ instance->mode=mode;
+
+ return instance;
+}
+
+void VisualScriptInputAction::_validate_property(PropertyInfo& property) const {
+
+
+ if (property.name=="action") {
+
+ property.hint=PROPERTY_HINT_ENUM;
+ String actions;
+
+ List<PropertyInfo> pinfo;
+ Globals::get_singleton()->get_property_list(&pinfo);
+ Vector<String> al;
+
+ for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+ const PropertyInfo &pi=E->get();
+
+ if (!pi.name.begins_with("input/"))
+ continue;
+
+ String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
+
+
+ al.push_back(name);
+ }
+
+ al.sort();;
+
+ for(int i=0;i<al.size();i++) {
+ if (actions!=String())
+ actions+=",";
+ actions+=al[i];
+ }
+
+ property.hint_string=actions;
+ }
+}
+
+
+void VisualScriptInputAction::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_action_name","name"),&VisualScriptInputAction::set_action_name);
+ ObjectTypeDB::bind_method(_MD("get_action_name"),&VisualScriptInputAction::get_action_name);
+
+ ObjectTypeDB::bind_method(_MD("set_action_mode","mode"),&VisualScriptInputAction::set_action_mode);
+ ObjectTypeDB::bind_method(_MD("get_action_mode"),&VisualScriptInputAction::get_action_mode);
+
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"action"),_SCS("set_action_name"),_SCS("get_action_name"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Pressed,Released,JustPressed,JustReleased"),_SCS("set_action_mode"),_SCS("get_action_mode"));
+
+}
+
+VisualScriptInputAction::VisualScriptInputAction() {
+
+ name="";
+ mode=MODE_PRESSED;
+
+}
+
+//////////////////////////////////////////
+////////////////Constructor///////////
+//////////////////////////////////////////
+
+int VisualScriptDeconstruct::get_output_sequence_port_count() const {
+
+ return 0;
+}
+
+bool VisualScriptDeconstruct::has_input_sequence_port() const{
+
+ return false;
+}
+
+int VisualScriptDeconstruct::get_input_value_port_count() const{
+ return 1;
+}
+int VisualScriptDeconstruct::get_output_value_port_count() const{
+
+ return elements.size();
+}
+
+String VisualScriptDeconstruct::get_output_sequence_port_text(int p_port) const {
+
+ return "";
+}
+
+PropertyInfo VisualScriptDeconstruct::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo(type,"value");
+}
+
+PropertyInfo VisualScriptDeconstruct::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo(elements[p_idx].type,elements[p_idx].name);
+}
+
+
+String VisualScriptDeconstruct::get_caption() const {
+
+ return "Deconstruct";
+}
+
+
+String VisualScriptDeconstruct::get_text() const {
+
+ return "from "+Variant::get_type_name(type)+":";
+}
+
+
+String VisualScriptDeconstruct::get_category() const {
+
+ return "functions";
+}
+
+void VisualScriptDeconstruct::_update_elements() {
+
+ elements.clear();;
+ Variant v;
+ if (type==Variant::INPUT_EVENT) {
+ InputEvent ie;
+ ie.type=input_type;
+ v=ie;
+ } else {
+ Variant::CallError ce;
+ v = Variant::construct(type,NULL,0,ce);
+ }
+
+ List<PropertyInfo> pinfo;
+ v.get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
+
+ Element e;
+ e.name=E->get().name;
+ e.type=E->get().type;
+ elements.push_back(e);
+ }
+}
+
+void VisualScriptDeconstruct::set_deconstruct_type(Variant::Type p_type) {
+
+ if (type==p_type)
+ return;
+
+ type=p_type;
+ _update_elements();
+ ports_changed_notify();
+ _change_notify(); //to make input appear/disappear
+}
+
+Variant::Type VisualScriptDeconstruct::get_deconstruct_type() const {
+
+ return type;
+}
+
+void VisualScriptDeconstruct::set_deconstruct_input_type(InputEvent::Type p_input_type) {
+
+ if (input_type==p_input_type)
+ return;
+
+ input_type=p_input_type;
+ _update_elements();
+ ports_changed_notify();
+}
+
+InputEvent::Type VisualScriptDeconstruct::get_deconstruct_input_type() const {
+
+ return input_type;
+}
+
+void VisualScriptDeconstruct::_set_elem_cache(const Array& p_elements) {
+
+ ERR_FAIL_COND(p_elements.size()%2==1);
+ elements.resize(p_elements.size()/2);
+ for(int i=0;i<elements.size();i++) {
+ elements[i].name=p_elements[i*2+0];
+ elements[i].type=Variant::Type(int(p_elements[i*2+1]));
+ }
+}
+
+Array VisualScriptDeconstruct::_get_elem_cache() const {
+
+ Array ret;
+ for(int i=0;i<elements.size();i++) {
+ ret.push_back(elements[i].name);
+ ret.push_back(elements[i].type);
+ }
+ return ret;
+}
+
+class VisualScriptNodeInstanceDeconstruct : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptInstance* instance;
+ Vector<StringName> outputs;
+
+ //virtual int get_working_memory_size() const { return 0; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ Variant in=*p_inputs[0];
+
+ for(int i=0;i<outputs.size();i++) {
+ bool valid;
+ *p_outputs[i]=in.get(outputs[i],&valid);
+ if (!valid) {
+ r_error_str="Can't obtain element '"+String(outputs[i])+"' from "+Variant::get_type_name(in.get_type());
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+ }
+
+ return 0;
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptDeconstruct::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceDeconstruct * instance = memnew(VisualScriptNodeInstanceDeconstruct );
+ instance->instance=p_instance;
+ instance->outputs.resize(elements.size());
+ for(int i=0;i<elements.size();i++) {
+ instance->outputs[i]=elements[i].name;
+ }
+
+ return instance;
+}
+
+
+
+void VisualScriptDeconstruct::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="input_type") {
+ if (type!=Variant::INPUT_EVENT) {
+ property.usage=0;
+ }
+ }
+}
+
+
+void VisualScriptDeconstruct::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_deconstruct_type","type"),&VisualScriptDeconstruct::set_deconstruct_type);
+ ObjectTypeDB::bind_method(_MD("get_deconstruct_type"),&VisualScriptDeconstruct::get_deconstruct_type);
+
+ ObjectTypeDB::bind_method(_MD("set_deconstruct_input_type","input_type"),&VisualScriptDeconstruct::set_deconstruct_input_type);
+ ObjectTypeDB::bind_method(_MD("get_deconstruct_input_type"),&VisualScriptDeconstruct::get_deconstruct_input_type);
+
+ ObjectTypeDB::bind_method(_MD("_set_elem_cache","_cache"),&VisualScriptDeconstruct::_set_elem_cache);
+ ObjectTypeDB::bind_method(_MD("_get_elem_cache"),&VisualScriptDeconstruct::_get_elem_cache);
+
+ String argt="Any";
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+ argt+=","+Variant::get_type_name(Variant::Type(i));
+ }
+
+ String iet="None,Key,MouseMotion,MouseButton,JoystickMotion,JoystickButton,ScreenTouch,ScreenDrag,Action";
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"type",PROPERTY_HINT_ENUM,argt),_SCS("set_deconstruct_type"),_SCS("get_deconstruct_type"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"input_type",PROPERTY_HINT_ENUM,iet),_SCS("set_deconstruct_input_type"),_SCS("get_deconstruct_input_type"));
+ ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"elem_cache",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_elem_cache"),_SCS("_get_elem_cache"));
+
+}
+
+VisualScriptDeconstruct::VisualScriptDeconstruct() {
+
+ type=Variant::NIL;
+ input_type=InputEvent::NONE;
+
+}
+
+
+void register_visual_script_nodes() {
+
+ VisualScriptLanguage::singleton->add_register_func("data/set_variable",create_node_generic<VisualScriptVariableSet>);
+ VisualScriptLanguage::singleton->add_register_func("data/get_variable",create_node_generic<VisualScriptVariableGet>);
+ VisualScriptLanguage::singleton->add_register_func("data/engine_singleton",create_node_generic<VisualScriptEngineSingleton>);
+ VisualScriptLanguage::singleton->add_register_func("data/scene_node",create_node_generic<VisualScriptSceneNode>);
+ VisualScriptLanguage::singleton->add_register_func("data/scene_tree",create_node_generic<VisualScriptSceneTree>);
+ VisualScriptLanguage::singleton->add_register_func("data/resource_path",create_node_generic<VisualScriptResourcePath>);
+ VisualScriptLanguage::singleton->add_register_func("data/self",create_node_generic<VisualScriptSelf>);
+ VisualScriptLanguage::singleton->add_register_func("data/comment",create_node_generic<VisualScriptComment>);
+ VisualScriptLanguage::singleton->add_register_func("data/get_local_variable",create_node_generic<VisualScriptLocalVar>);
+ VisualScriptLanguage::singleton->add_register_func("data/set_local_variable",create_node_generic<VisualScriptLocalVarSet>);
+ VisualScriptLanguage::singleton->add_register_func("data/preload",create_node_generic<VisualScriptPreload>);
+ VisualScriptLanguage::singleton->add_register_func("data/action",create_node_generic<VisualScriptInputAction>);
+
+ VisualScriptLanguage::singleton->add_register_func("constants/constant",create_node_generic<VisualScriptConstant>);
+ VisualScriptLanguage::singleton->add_register_func("constants/math_constant",create_node_generic<VisualScriptMathConstant>);
+ VisualScriptLanguage::singleton->add_register_func("constants/class_constant",create_node_generic<VisualScriptClassConstant>);
+ VisualScriptLanguage::singleton->add_register_func("constants/global_constant",create_node_generic<VisualScriptGlobalConstant>);
+ VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant",create_node_generic<VisualScriptBasicTypeConstant>);
+
+ VisualScriptLanguage::singleton->add_register_func("custom/custom_node",create_node_generic<VisualScriptCustomNode>);
+ VisualScriptLanguage::singleton->add_register_func("custom/sub_call",create_node_generic<VisualScriptSubCall>);
+
+ VisualScriptLanguage::singleton->add_register_func("index/get_index",create_node_generic<VisualScriptIndexGet>);
+ VisualScriptLanguage::singleton->add_register_func("index/set_index",create_node_generic<VisualScriptIndexSet>);
+
+
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/equal",create_op_node<Variant::OP_EQUAL>);
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/not_equal",create_op_node<Variant::OP_NOT_EQUAL>);
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/less",create_op_node<Variant::OP_LESS>);
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/less_equal",create_op_node<Variant::OP_LESS_EQUAL>);
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/greater",create_op_node<Variant::OP_GREATER>);
+ VisualScriptLanguage::singleton->add_register_func("operators/compare/greater_equal",create_op_node<Variant::OP_GREATER_EQUAL>);
+ //mathematic
+ VisualScriptLanguage::singleton->add_register_func("operators/math/add",create_op_node<Variant::OP_ADD>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/subtract",create_op_node<Variant::OP_SUBSTRACT>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/multiply",create_op_node<Variant::OP_MULTIPLY>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/divide",create_op_node<Variant::OP_DIVIDE>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/negate",create_op_node<Variant::OP_NEGATE>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/remainder",create_op_node<Variant::OP_MODULE>);
+ VisualScriptLanguage::singleton->add_register_func("operators/math/string_concat",create_op_node<Variant::OP_STRING_CONCAT>);
+ //bitwise
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_left",create_op_node<Variant::OP_SHIFT_LEFT>);
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_right",create_op_node<Variant::OP_SHIFT_RIGHT>);
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_and",create_op_node<Variant::OP_BIT_AND>);
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_or",create_op_node<Variant::OP_BIT_OR>);
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_xor",create_op_node<Variant::OP_BIT_XOR>);
+ VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_negate",create_op_node<Variant::OP_BIT_NEGATE>);
+ //logic
+ VisualScriptLanguage::singleton->add_register_func("operators/logic/and",create_op_node<Variant::OP_AND>);
+ VisualScriptLanguage::singleton->add_register_func("operators/logic/or",create_op_node<Variant::OP_OR>);
+ VisualScriptLanguage::singleton->add_register_func("operators/logic/xor",create_op_node<Variant::OP_XOR>);
+ VisualScriptLanguage::singleton->add_register_func("operators/logic/not",create_op_node<Variant::OP_NOT>);
+ VisualScriptLanguage::singleton->add_register_func("operators/logic/in",create_op_node<Variant::OP_IN>);
+
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct",create_node_generic<VisualScriptDeconstruct>);
+
+ for(int i=1;i<Variant::VARIANT_MAX;i++) {
+
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(Variant::Type(i),&constructors);
+
+ for(List<MethodInfo>::Element *E=constructors.front();E;E=E->next()) {
+
+ if (E->get().arguments.size()>0) {
+
+
+ String name = "functions/constructors/"+Variant::get_type_name(Variant::Type(i))+" ( ";
+ for(int j=0;j<E->get().arguments.size();j++) {
+ if (j>0)
+ name+=", ";
+ if (E->get().arguments.size()==1)
+ name+=Variant::get_type_name(E->get().arguments[j].type);
+ else
+ name+=E->get().arguments[j].name;
+ }
+ name+=") ";
+
+ VisualScriptLanguage::singleton->add_register_func(name,create_constructor_node);
+ Pair<Variant::Type,MethodInfo> pair;
+ pair.first=Variant::Type(i);
+ pair.second=E->get();
+ constructor_map[name]=pair;
+ }
+ }
+ }
+}
+
+
+
+void unregister_visual_script_nodes() {
+
+ constructor_map.clear();
+}
+
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
new file mode 100644
index 0000000000..94eeadca57
--- /dev/null
+++ b/modules/visual_script/visual_script_nodes.h
@@ -0,0 +1,1068 @@
+#ifndef VISUAL_SCRIPT_NODES_H
+#define VISUAL_SCRIPT_NODES_H
+
+#include "visual_script.h"
+
+class VisualScriptFunction : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptFunction,VisualScriptNode)
+
+
+ struct Argument {
+ String name;
+ Variant::Type type;
+ };
+
+ Vector<Argument> arguments;
+
+ bool stack_less;
+ int stack_size;
+ ScriptInstance::RPCMode rpc_mode;
+
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "flow_control"; }
+
+ void add_argument(Variant::Type p_type,const String& p_name,int p_index=-1);
+ void set_argument_type(int p_argidx,Variant::Type p_type);
+ Variant::Type get_argument_type(int p_argidx) const;
+ void set_argument_name(int p_argidx,const String& p_name);
+ String get_argument_name(int p_argidx) const;
+ void remove_argument(int p_argidx);
+ int get_argument_count() const;
+
+
+ void set_stack_less(bool p_enable);
+ bool is_stack_less() const;
+
+ void set_stack_size(int p_size);
+ int get_stack_size() const;
+
+ void set_rpc_mode(ScriptInstance::RPCMode p_mode);
+ ScriptInstance::RPCMode get_rpc_mode() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptFunction();
+};
+
+
+class VisualScriptOperator : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptOperator,VisualScriptNode)
+
+
+ Variant::Type typed;
+ Variant::Operator op;
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ Variant::Operator get_operator() const;
+
+ void set_typed(Variant::Type p_op);
+ Variant::Type get_typed() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptOperator();
+};
+
+
+class VisualScriptVariableGet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptVariableGet,VisualScriptNode)
+
+
+ StringName variable;
+protected:
+
+ virtual void _validate_property(PropertyInfo& property) const;
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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_var);
+ StringName get_variable() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptVariableGet();
+};
+
+
+class VisualScriptVariableSet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptVariableSet,VisualScriptNode)
+
+
+ StringName variable;
+protected:
+
+ virtual void _validate_property(PropertyInfo& property) const;
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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_var);
+ StringName get_variable() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptVariableSet();
+};
+
+
+class VisualScriptConstant : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptConstant,VisualScriptNode)
+
+
+ Variant::Type type;
+ Variant value;
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ Variant::Type get_constant_type() const;
+
+ void set_constant_value(Variant p_value);
+ Variant get_constant_value() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptConstant();
+};
+
+
+
+class VisualScriptPreload : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptPreload,VisualScriptNode)
+
+
+ Ref<Resource> preload;
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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_value);
+ Ref<Resource> get_preload() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptPreload();
+};
+
+class VisualScriptIndexGet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptIndexGet,VisualScriptNode)
+
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+
+ VisualScriptIndexGet();
+};
+
+
+class VisualScriptIndexSet : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptIndexSet,VisualScriptNode)
+
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+
+ VisualScriptIndexSet();
+};
+
+
+
+class VisualScriptGlobalConstant : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptGlobalConstant,VisualScriptNode)
+
+ int index;
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ int get_global_constant();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptGlobalConstant();
+};
+
+
+class VisualScriptClassConstant : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptClassConstant,VisualScriptNode)
+
+ StringName base_type;
+ StringName name;
+protected:
+ static void _bind_methods();
+ virtual void _validate_property(PropertyInfo& property) const;
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ StringName get_class_constant();
+
+ void set_base_type(const StringName& p_which);
+ StringName get_base_type();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptClassConstant();
+};
+
+class VisualScriptBasicTypeConstant : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptBasicTypeConstant,VisualScriptNode)
+
+ Variant::Type type;
+ StringName name;
+protected:
+ static void _bind_methods();
+ virtual void _validate_property(PropertyInfo& property) const;
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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_basic_type_constant(const StringName& p_which);
+ StringName get_basic_type_constant() const;
+
+ void set_basic_type(Variant::Type p_which);
+ Variant::Type get_basic_type() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptBasicTypeConstant();
+};
+
+
+
+class VisualScriptMathConstant : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptMathConstant,VisualScriptNode)
+public:
+
+ enum MathConstant {
+ MATH_CONSTANT_ONE,
+ MATH_CONSTANT_PI,
+ MATH_CONSTANT_2PI,
+ MATH_CONSTANT_HALF_PI,
+ MATH_CONSTANT_E,
+ MATH_CONSTANT_SQRT2,
+ MATH_CONSTANT_MAX,
+ };
+
+private:
+ static const char* const_name[MATH_CONSTANT_MAX];
+ static double const_value[MATH_CONSTANT_MAX];
+ MathConstant constant;
+protected:
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ MathConstant get_math_constant();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptMathConstant();
+};
+
+VARIANT_ENUM_CAST( VisualScriptMathConstant::MathConstant )
+
+class VisualScriptEngineSingleton : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptEngineSingleton,VisualScriptNode)
+
+ String singleton;
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ String get_singleton();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+
+ VisualScriptEngineSingleton();
+};
+
+
+
+
+class VisualScriptSceneNode : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSceneNode,VisualScriptNode)
+
+ NodePath path;
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ NodePath get_node_path();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+ VisualScriptSceneNode();
+};
+
+
+
+
+class VisualScriptSceneTree : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSceneTree,VisualScriptNode)
+
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+ VisualScriptSceneTree();
+};
+
+
+
+class VisualScriptResourcePath : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptResourcePath,VisualScriptNode)
+
+ String path;
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ String get_resource_path();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptResourcePath();
+};
+
+
+class VisualScriptSelf : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSelf,VisualScriptNode)
+
+
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+
+ virtual TypeGuess guess_output_type(TypeGuess* p_inputs, int p_output) const;
+
+ VisualScriptSelf();
+};
+
+
+class VisualScriptCustomNode: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptCustomNode,VisualScriptNode)
+
+
+protected:
+
+ virtual bool _use_builtin_script() const { return true; }
+
+ static void _bind_methods();
+public:
+
+ enum StartMode { //replicated for step
+ START_MODE_BEGIN_SEQUENCE,
+ START_MODE_CONTINUE_SEQUENCE,
+ START_MODE_RESUME_YIELD
+ };
+
+ enum { //replicated for step
+ STEP_SHIFT=1<<24,
+ STEP_MASK=STEP_SHIFT-1,
+ STEP_PUSH_STACK_BIT=STEP_SHIFT, //push bit to stack
+ STEP_GO_BACK_BIT=STEP_SHIFT<<1, //go back to previous node
+ STEP_NO_ADVANCE_BIT=STEP_SHIFT<<2, //do not advance past this node
+ STEP_EXIT_FUNCTION_BIT=STEP_SHIFT<<3, //return from function
+ STEP_YIELD_BIT=STEP_SHIFT<<4, //yield (will find VisualScriptFunctionState state in first working memory)
+ };
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptCustomNode();
+};
+
+class VisualScriptSubCall: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptSubCall,VisualScriptNode)
+
+
+protected:
+
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptSubCall();
+};
+
+class VisualScriptComment: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptComment,VisualScriptNode)
+
+
+ String title;
+ String description;
+ Size2 size;
+protected:
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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_title(const String& p_title);
+ String get_title() const;
+
+ void set_description(const String& p_description);
+ String get_description() const;
+
+ void set_size(const Size2& p_size);
+ Size2 get_size() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptComment();
+};
+
+class VisualScriptConstructor: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptConstructor,VisualScriptNode)
+
+
+ Variant::Type type;
+ MethodInfo constructor;
+
+protected:
+
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ Variant::Type get_constructor_type() const;
+
+ void set_constructor(const Dictionary& p_info);
+ Dictionary get_constructor() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptConstructor();
+};
+
+
+
+
+class VisualScriptLocalVar: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptLocalVar,VisualScriptNode)
+
+ StringName name;
+ Variant::Type type;
+
+protected:
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ StringName get_var_name() const;
+
+ void set_var_type(Variant::Type p_type);
+ Variant::Type get_var_type() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptLocalVar();
+};
+
+class VisualScriptLocalVarSet: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptLocalVarSet,VisualScriptNode)
+
+ StringName name;
+ Variant::Type type;
+
+protected:
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ StringName get_var_name() const;
+
+ void set_var_type(Variant::Type p_type);
+ Variant::Type get_var_type() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptLocalVarSet();
+};
+
+
+
+class VisualScriptInputAction: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptInputAction,VisualScriptNode)
+public:
+ enum Mode {
+ MODE_PRESSED,
+ MODE_RELEASED,
+ MODE_JUST_PRESSED,
+ MODE_JUST_RELEASED,
+ };
+
+ StringName name;
+ Mode mode;
+
+protected:
+
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ StringName get_action_name() const;
+
+ void set_action_mode(Mode p_mode);
+ Mode get_action_mode() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptInputAction();
+};
+
+VARIANT_ENUM_CAST( VisualScriptInputAction::Mode )
+
+class VisualScriptDeconstruct: public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptDeconstruct,VisualScriptNode)
+
+
+ struct Element {
+ StringName name;
+ Variant::Type type;
+ };
+
+
+ Vector<Element> elements;
+
+ void _update_elements();
+ Variant::Type type;
+ InputEvent::Type input_type;
+
+ void _set_elem_cache(const Array& p_elements);
+ Array _get_elem_cache() const;
+
+ virtual void _validate_property(PropertyInfo& property) const;
+
+protected:
+
+
+ static void _bind_methods();
+public:
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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);
+ Variant::Type get_deconstruct_type() const;
+
+ void set_deconstruct_input_type(InputEvent::Type p_input_type);
+ InputEvent::Type get_deconstruct_input_type() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptDeconstruct();
+};
+
+
+void register_visual_script_nodes();
+void unregister_visual_script_nodes();
+
+#endif // VISUAL_SCRIPT_NODES_H
diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp
new file mode 100644
index 0000000000..221c46b6fd
--- /dev/null
+++ b/modules/visual_script/visual_script_yield_nodes.cpp
@@ -0,0 +1,625 @@
+#include "visual_script_yield_nodes.h"
+#include "scene/main/scene_main_loop.h"
+#include "os/os.h"
+#include "scene/main/node.h"
+#include "visual_script_nodes.h"
+
+//////////////////////////////////////////
+////////////////YIELD///////////
+//////////////////////////////////////////
+
+int VisualScriptYield::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptYield::has_input_sequence_port() const{
+
+ return true;
+}
+
+int VisualScriptYield::get_input_value_port_count() const{
+
+ return 0;
+}
+int VisualScriptYield::get_output_value_port_count() const{
+
+ return 0;
+}
+
+String VisualScriptYield::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptYield::get_input_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+PropertyInfo VisualScriptYield::get_output_value_port_info(int p_idx) const{
+
+ return PropertyInfo();
+}
+
+
+String VisualScriptYield::get_caption() const {
+
+ return yield_mode==YIELD_RETURN?"Yield":"Wait";
+}
+
+String VisualScriptYield::get_text() const {
+
+ switch (yield_mode) {
+ case YIELD_RETURN: return ""; break;
+ case YIELD_FRAME: return "Next Frame"; break;
+ case YIELD_FIXED_FRAME: return "Next Fixed Frame"; break;
+ case YIELD_WAIT: return rtos(wait_time)+" sec(s)"; break;
+ }
+
+ return String();
+}
+
+
+class VisualScriptNodeInstanceYield : public VisualScriptNodeInstance {
+public:
+
+ VisualScriptYield::YieldMode mode;
+ float wait_time;
+
+ virtual int get_working_memory_size() const { return 1; } //yield needs at least 1
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return false; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_RESUME_YIELD) {
+ return 0; //resuming yield
+ } else {
+ //yield
+
+
+ SceneTree *tree = OS::get_singleton()->get_main_loop()->cast_to<SceneTree>();
+ if (!tree) {
+ r_error_str="Main Loop is not SceneTree";
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return 0;
+ }
+
+ Ref<VisualScriptFunctionState> state;
+ state.instance();
+
+ int ret = STEP_YIELD_BIT;
+ switch(mode) {
+
+ case VisualScriptYield::YIELD_RETURN: ret=STEP_EXIT_FUNCTION_BIT; break; //return the yield
+ case VisualScriptYield::YIELD_FRAME: state->connect_to_signal(tree,"idle_frame",Array()); break;
+ case VisualScriptYield::YIELD_FIXED_FRAME: state->connect_to_signal(tree,"fixed_frame",Array()); break;
+ case VisualScriptYield::YIELD_WAIT: state->connect_to_signal(tree->create_timer(wait_time).ptr(),"timeout",Array()); break;
+
+ }
+
+ *p_working_mem=state;
+
+ return ret;
+ }
+ }
+
+};
+
+VisualScriptNodeInstance* VisualScriptYield::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceYield * instance = memnew(VisualScriptNodeInstanceYield );
+ //instance->instance=p_instance;
+ instance->mode=yield_mode;
+ instance->wait_time=wait_time;
+ return instance;
+}
+
+void VisualScriptYield::set_yield_mode(YieldMode p_mode) {
+
+ if (yield_mode==p_mode)
+ return;
+ yield_mode=p_mode;
+ ports_changed_notify();
+ _change_notify();
+}
+
+VisualScriptYield::YieldMode VisualScriptYield::get_yield_mode(){
+
+ return yield_mode;
+}
+
+void VisualScriptYield::set_wait_time(float p_time) {
+
+ if (wait_time==p_time)
+ return;
+ wait_time=p_time;
+ ports_changed_notify();
+
+}
+
+float VisualScriptYield::get_wait_time(){
+
+ return wait_time;
+}
+
+
+void VisualScriptYield::_validate_property(PropertyInfo& property) const {
+
+
+ if (property.name=="wait_time") {
+ if (yield_mode!=YIELD_WAIT) {
+ property.usage=0;
+ }
+ }
+}
+
+void VisualScriptYield::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_yield_mode","mode"),&VisualScriptYield::set_yield_mode);
+ ObjectTypeDB::bind_method(_MD("get_yield_mode"),&VisualScriptYield::get_yield_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_wait_time","sec"),&VisualScriptYield::set_wait_time);
+ ObjectTypeDB::bind_method(_MD("get_wait_time"),&VisualScriptYield::get_wait_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Frame,FixedFrame,Time",PROPERTY_USAGE_NOEDITOR),_SCS("set_yield_mode"),_SCS("get_yield_mode"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"wait_time"),_SCS("set_wait_time"),_SCS("get_wait_time"));
+
+
+ BIND_CONSTANT( YIELD_FRAME );
+ BIND_CONSTANT( YIELD_FIXED_FRAME );
+ BIND_CONSTANT( YIELD_WAIT );
+
+}
+
+VisualScriptYield::VisualScriptYield() {
+
+ yield_mode=YIELD_FRAME;
+ wait_time=1;
+
+}
+
+
+template<VisualScriptYield::YieldMode MODE>
+static Ref<VisualScriptNode> create_yield_node(const String& p_name) {
+
+ Ref<VisualScriptYield> node;
+ node.instance();
+ node->set_yield_mode(MODE);
+ return node;
+}
+
+///////////////////////////////////////////////////
+////////////////YIELD SIGNAL//////////////////////
+//////////////////////////////////////////////////
+
+int VisualScriptYieldSignal::get_output_sequence_port_count() const {
+
+ return 1;
+}
+
+bool VisualScriptYieldSignal::has_input_sequence_port() const{
+
+ return true;
+}
+#ifdef TOOLS_ENABLED
+
+static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) {
+
+ if (p_edited_scene!=p_current_node && p_current_node->get_owner()!=p_edited_scene)
+ return NULL;
+
+ Ref<Script> scr = p_current_node->get_script();
+
+ if (scr.is_valid() && scr==script)
+ return p_current_node;
+
+ for(int i=0;i<p_current_node->get_child_count();i++) {
+ Node *n = _find_script_node(p_edited_scene,p_current_node->get_child(i),script);
+ if (n)
+ return n;
+ }
+
+ return NULL;
+}
+
+#endif
+Node *VisualScriptYieldSignal::_get_base_node() const {
+
+#ifdef TOOLS_ENABLED
+ Ref<Script> script = get_visual_script();
+ if (!script.is_valid())
+ return NULL;
+
+ MainLoop * main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop)
+ return NULL;
+
+ SceneTree *scene_tree = main_loop->cast_to<SceneTree>();
+
+ if (!scene_tree)
+ return NULL;
+
+ Node *edited_scene = scene_tree->get_edited_scene_root();
+
+ if (!edited_scene)
+ return NULL;
+
+ Node* script_node = _find_script_node(edited_scene,edited_scene,script);
+
+ if (!script_node)
+ return NULL;
+
+ if (!script_node->has_node(base_path))
+ return NULL;
+
+ Node *path_to = script_node->get_node(base_path);
+
+ return path_to;
+#else
+
+ return NULL;
+#endif
+}
+
+StringName VisualScriptYieldSignal::_get_base_type() const {
+
+ if (call_mode==CALL_MODE_SELF && get_visual_script().is_valid())
+ return get_visual_script()->get_instance_base_type();
+ else if (call_mode==CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
+ Node *path = _get_base_node();
+ if (path)
+ return path->get_type();
+
+ }
+
+ return base_type;
+}
+
+int VisualScriptYieldSignal::get_input_value_port_count() const{
+
+ if (call_mode==CALL_MODE_INSTANCE)
+ return 1;
+ else
+ return 0;
+
+}
+int VisualScriptYieldSignal::get_output_value_port_count() const{
+
+
+ MethodInfo sr;
+
+ if (!ObjectTypeDB::get_signal(_get_base_type(),signal,&sr))
+ return 0;
+
+ return sr.arguments.size();
+
+}
+
+String VisualScriptYieldSignal::get_output_sequence_port_text(int p_port) const {
+
+ return String();
+}
+
+PropertyInfo VisualScriptYieldSignal::get_input_value_port_info(int p_idx) const{
+
+ if (call_mode==CALL_MODE_INSTANCE)
+ return PropertyInfo(Variant::OBJECT,"instance");
+ else
+ return PropertyInfo();
+
+}
+
+PropertyInfo VisualScriptYieldSignal::get_output_value_port_info(int p_idx) const{
+
+ MethodInfo sr;
+
+ if (!ObjectTypeDB::get_signal(_get_base_type(),signal,&sr))
+ return PropertyInfo(); //no signal
+ ERR_FAIL_INDEX_V(p_idx,sr.arguments.size(),PropertyInfo());
+ return sr.arguments[p_idx];
+
+}
+
+
+String VisualScriptYieldSignal::get_caption() const {
+
+ static const char*cname[3]= {
+ "WaitSignal",
+ "WaitNodeSignal",
+ "WaitInstanceSigna;",
+ };
+
+ return cname[call_mode];
+}
+
+String VisualScriptYieldSignal::get_text() const {
+
+ if (call_mode==CALL_MODE_SELF)
+ return " "+String(signal)+"()";
+ else
+ return " "+_get_base_type()+"."+String(signal)+"()";
+
+}
+
+
+void VisualScriptYieldSignal::set_base_type(const StringName& p_type) {
+
+ if (base_type==p_type)
+ return;
+
+ base_type=p_type;
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+StringName VisualScriptYieldSignal::get_base_type() const{
+
+ return base_type;
+}
+
+void VisualScriptYieldSignal::set_signal(const StringName& p_type){
+
+ if (signal==p_type)
+ return;
+
+ signal=p_type;
+
+ _change_notify();
+ ports_changed_notify();
+}
+StringName VisualScriptYieldSignal::get_signal() const {
+
+
+ return signal;
+}
+
+void VisualScriptYieldSignal::set_base_path(const NodePath& p_type) {
+
+ if (base_path==p_type)
+ return;
+
+ base_path=p_type;
+
+ _change_notify();
+ ports_changed_notify();
+}
+
+NodePath VisualScriptYieldSignal::get_base_path() const {
+
+ return base_path;
+}
+
+
+void VisualScriptYieldSignal::set_call_mode(CallMode p_mode) {
+
+ if (call_mode==p_mode)
+ return;
+
+ call_mode=p_mode;
+
+ _change_notify();
+ ports_changed_notify();
+
+}
+
+VisualScriptYieldSignal::CallMode VisualScriptYieldSignal::get_call_mode() const {
+
+ return call_mode;
+}
+
+
+void VisualScriptYieldSignal::_validate_property(PropertyInfo& property) const {
+
+ if (property.name=="signal/base_type") {
+ if (call_mode!=CALL_MODE_INSTANCE) {
+ property.usage=PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+
+
+ if (property.name=="signal/node_path") {
+ if (call_mode!=CALL_MODE_NODE_PATH) {
+ property.usage=0;
+ } else {
+
+ Node *bnode = _get_base_node();
+ if (bnode) {
+ property.hint_string=bnode->get_path(); //convert to loong string
+ } else {
+
+ }
+ }
+ }
+
+ if (property.name=="signal/signal") {
+ property.hint=PROPERTY_HINT_ENUM;
+
+
+ List<MethodInfo> methods;
+
+ ObjectTypeDB::get_signal_list(_get_base_type(),&methods);
+
+ List<String> mstring;
+ for (List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
+ if (E->get().name.begins_with("_"))
+ continue;
+ mstring.push_back(E->get().name.get_slice(":",0));
+ }
+
+ mstring.sort();
+
+ String ml;
+ for (List<String>::Element *E=mstring.front();E;E=E->next()) {
+
+ if (ml!=String())
+ ml+=",";
+ ml+=E->get();
+ }
+
+ property.hint_string=ml;
+ }
+
+
+}
+
+
+void VisualScriptYieldSignal::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_base_type","base_type"),&VisualScriptYieldSignal::set_base_type);
+ ObjectTypeDB::bind_method(_MD("get_base_type"),&VisualScriptYieldSignal::get_base_type);
+
+ ObjectTypeDB::bind_method(_MD("set_signal","signal"),&VisualScriptYieldSignal::set_signal);
+ ObjectTypeDB::bind_method(_MD("get_signal"),&VisualScriptYieldSignal::get_signal);
+
+ ObjectTypeDB::bind_method(_MD("set_call_mode","mode"),&VisualScriptYieldSignal::set_call_mode);
+ ObjectTypeDB::bind_method(_MD("get_call_mode"),&VisualScriptYieldSignal::get_call_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_base_path","base_path"),&VisualScriptYieldSignal::set_base_path);
+ ObjectTypeDB::bind_method(_MD("get_base_path"),&VisualScriptYieldSignal::get_base_path);
+
+
+
+ String bt;
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ if (i>0)
+ bt+=",";
+
+ bt+=Variant::get_type_name(Variant::Type(i));
+ }
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"signal/call_mode",PROPERTY_HINT_ENUM,"Self,Node Path,Instance"),_SCS("set_call_mode"),_SCS("get_call_mode"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"signal/base_type",PROPERTY_HINT_TYPE_STRING,"Object"),_SCS("set_base_type"),_SCS("get_base_type"));
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"signal/node_path",PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE),_SCS("set_base_path"),_SCS("get_base_path"));
+ ADD_PROPERTY(PropertyInfo(Variant::STRING,"signal/signal"),_SCS("set_signal"),_SCS("get_signal"));
+
+
+ BIND_CONSTANT( CALL_MODE_SELF );
+ BIND_CONSTANT( CALL_MODE_NODE_PATH);
+ BIND_CONSTANT( CALL_MODE_INSTANCE);
+
+}
+
+class VisualScriptNodeInstanceYieldSignal : public VisualScriptNodeInstance {
+public:
+
+
+ VisualScriptYieldSignal::CallMode call_mode;
+ NodePath node_path;
+ int output_args;
+ StringName signal;
+
+ VisualScriptYieldSignal *node;
+ VisualScriptInstance *instance;
+
+
+
+ virtual int get_working_memory_size() const { return 1; }
+ //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
+ //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
+
+ virtual int step(const Variant** p_inputs,Variant** p_outputs,StartMode p_start_mode,Variant* p_working_mem,Variant::CallError& r_error,String& r_error_str) {
+
+ if (p_start_mode==START_MODE_RESUME_YIELD) {
+ return 0; //resuming yield
+ } else {
+ //yield
+
+ Object * object;
+
+ switch(call_mode) {
+
+ case VisualScriptYieldSignal::CALL_MODE_SELF: {
+
+ object=instance->get_owner_ptr();
+
+ } break;
+ case VisualScriptYieldSignal::CALL_MODE_NODE_PATH: {
+
+ Node* node = instance->get_owner_ptr()->cast_to<Node>();
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Base object is not a Node!";
+ return 0;
+ }
+
+ Node* another = node->get_node(node_path);
+ if (!node) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Path does not lead Node!";
+ return 0;
+ }
+
+ object=another;
+
+ } break;
+ case VisualScriptYieldSignal::CALL_MODE_INSTANCE: {
+
+ object = *p_inputs[0];
+ if (!object) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_error_str="Supplied instance input is null.";
+ return 0;
+
+ }
+
+ } break;
+
+ }
+
+ Ref<VisualScriptFunctionState> state;
+ state.instance();
+
+ state->connect_to_signal(object,signal,Array());
+
+ *p_working_mem=state;
+
+ return STEP_YIELD_BIT;
+ }
+
+
+ }
+
+
+};
+
+VisualScriptNodeInstance* VisualScriptYieldSignal::instance(VisualScriptInstance* p_instance) {
+
+ VisualScriptNodeInstanceYieldSignal * instance = memnew(VisualScriptNodeInstanceYieldSignal );
+ instance->node=this;
+ instance->instance=p_instance;
+ instance->signal=signal;
+ instance->call_mode=call_mode;
+ instance->node_path=base_path;
+ instance->output_args = get_output_value_port_count();
+ return instance;
+}
+VisualScriptYieldSignal::VisualScriptYieldSignal() {
+
+ call_mode=CALL_MODE_SELF;
+ base_type="Object";
+
+}
+
+template<VisualScriptYieldSignal::CallMode cmode>
+static Ref<VisualScriptNode> create_yield_signal_node(const String& p_name) {
+
+ Ref<VisualScriptYieldSignal> node;
+ node.instance();
+ node->set_call_mode(cmode);
+ return node;
+}
+
+void register_visual_script_yield_nodes() {
+
+ VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_frame",create_yield_node<VisualScriptYield::YIELD_FRAME>);
+ VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_fixed_frame",create_yield_node<VisualScriptYield::YIELD_FIXED_FRAME>);
+ VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_time",create_yield_node<VisualScriptYield::YIELD_WAIT>);
+
+
+ VisualScriptLanguage::singleton->add_register_func("functions/yield",create_yield_node<VisualScriptYield::YIELD_RETURN>);
+ VisualScriptLanguage::singleton->add_register_func("functions/yield_signal",create_node_generic<VisualScriptYieldSignal>);
+
+}
diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h
new file mode 100644
index 0000000000..ae7f8c15c1
--- /dev/null
+++ b/modules/visual_script/visual_script_yield_nodes.h
@@ -0,0 +1,128 @@
+#ifndef VISUAL_SCRIPT_YIELD_NODES_H
+#define VISUAL_SCRIPT_YIELD_NODES_H
+
+#include "visual_script.h"
+
+class VisualScriptYield : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptYield,VisualScriptNode)
+public:
+
+ enum YieldMode {
+ YIELD_RETURN,
+ YIELD_FRAME,
+ YIELD_FIXED_FRAME,
+ YIELD_WAIT
+
+ };
+private:
+
+ YieldMode yield_mode;
+ float wait_time;
+
+
+protected:
+
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_yield_mode(YieldMode p_mode);
+ YieldMode get_yield_mode();
+
+ void set_wait_time(float p_time);
+ float get_wait_time();
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptYield();
+};
+VARIANT_ENUM_CAST( VisualScriptYield::YieldMode )
+
+class VisualScriptYieldSignal : public VisualScriptNode {
+
+ OBJ_TYPE(VisualScriptYieldSignal,VisualScriptNode)
+public:
+ enum CallMode {
+ CALL_MODE_SELF,
+ CALL_MODE_NODE_PATH,
+ CALL_MODE_INSTANCE,
+
+ };
+private:
+
+ CallMode call_mode;
+ StringName base_type;
+ NodePath base_path;
+ StringName signal;
+
+ Node *_get_base_node() const;
+ StringName _get_base_type() const;
+
+
+protected:
+ virtual void _validate_property(PropertyInfo& property) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual int get_output_sequence_port_count() const;
+ virtual bool has_input_sequence_port() const;
+
+
+ virtual String get_output_sequence_port_text(int p_port) const;
+
+
+ virtual int get_input_value_port_count() const;
+ virtual int get_output_value_port_count() const;
+
+
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+ 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 "functions"; }
+
+ void set_base_type(const StringName& p_type);
+ StringName get_base_type() const;
+
+ void set_signal(const StringName& p_type);
+ StringName get_signal() const;
+
+ void set_base_path(const NodePath& p_type);
+ NodePath get_base_path() const;
+
+ void set_call_mode(CallMode p_mode);
+ CallMode get_call_mode() const;
+
+ virtual VisualScriptNodeInstance* instance(VisualScriptInstance* p_instance);
+
+ VisualScriptYieldSignal();
+};
+
+VARIANT_ENUM_CAST(VisualScriptYieldSignal::CallMode );
+
+void register_visual_script_yield_nodes();
+
+#endif // VISUAL_SCRIPT_YIELD_NODES_H