feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

5
engine/scene/main/SCsub Normal file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.scene_sources, "*.cpp")

View file

@ -0,0 +1,61 @@
/**************************************************************************/
/* canvas_item.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 DISABLE_DEPRECATED
void CanvasItem::_draw_circle_bind_compat_84472(const Point2 &p_pos, real_t p_radius, const Color &p_color) {
draw_circle(p_pos, p_radius, p_color, true, -1.0, false);
}
void CanvasItem::_draw_rect_bind_compat_84523(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width) {
draw_rect(p_rect, p_color, p_filled, p_width, false);
}
void CanvasItem::_draw_dashed_line_bind_compat_84523(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned) {
draw_dashed_line(p_from, p_to, p_color, p_width, p_dash, p_aligned, false);
}
void CanvasItem::_draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width) {
draw_multiline(p_points, p_color, p_width, false);
}
void CanvasItem::_draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width) {
draw_multiline_colors(p_points, p_colors, p_width, false);
}
void CanvasItem::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::_draw_circle_bind_compat_84472);
ClassDB::bind_compatibility_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::_draw_rect_bind_compat_84523, DEFVAL(true), DEFVAL(-1.0));
ClassDB::bind_compatibility_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash", "aligned"), &CanvasItem::_draw_dashed_line_bind_compat_84523, DEFVAL(-1.0), DEFVAL(2.0), DEFVAL(true));
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::_draw_multiline_bind_compat_84523, DEFVAL(-1.0));
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::_draw_multiline_colors_bind_compat_84523, DEFVAL(-1.0));
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,445 @@
/**************************************************************************/
/* canvas_item.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 CANVAS_ITEM_H
#define CANVAS_ITEM_H
#include "scene/main/node.h"
#include "scene/resources/canvas_item_material.h"
#include "scene/resources/font.h"
class CanvasLayer;
class MultiMesh;
class StyleBox;
class Window;
class World2D;
class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
friend class CanvasLayer;
public:
enum TextureFilter {
TEXTURE_FILTER_PARENT_NODE,
TEXTURE_FILTER_NEAREST,
TEXTURE_FILTER_LINEAR,
TEXTURE_FILTER_NEAREST_WITH_MIPMAPS,
TEXTURE_FILTER_LINEAR_WITH_MIPMAPS,
TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC,
TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC,
TEXTURE_FILTER_MAX
};
enum TextureRepeat {
TEXTURE_REPEAT_PARENT_NODE,
TEXTURE_REPEAT_DISABLED,
TEXTURE_REPEAT_ENABLED,
TEXTURE_REPEAT_MIRROR,
TEXTURE_REPEAT_MAX,
};
enum ClipChildrenMode {
CLIP_CHILDREN_DISABLED,
CLIP_CHILDREN_ONLY,
CLIP_CHILDREN_AND_DRAW,
CLIP_CHILDREN_MAX,
};
private:
mutable SelfList<Node>
xform_change;
RID canvas_item;
StringName canvas_group;
CanvasLayer *canvas_layer = nullptr;
Color modulate = Color(1, 1, 1, 1);
Color self_modulate = Color(1, 1, 1, 1);
List<CanvasItem *> children_items;
List<CanvasItem *>::Element *C = nullptr;
int light_mask = 1;
uint32_t visibility_layer = 1;
int z_index = 0;
bool z_relative = true;
bool y_sort_enabled = false;
Window *window = nullptr;
bool visible = true;
bool parent_visible_in_tree = false;
bool pending_update = false;
bool top_level = false;
bool drawing = false;
bool block_transform_notify = false;
bool behind = false;
bool use_parent_material = false;
bool notify_local_transform = false;
bool notify_transform = false;
bool hide_clip_children = false;
ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED;
mutable RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
mutable RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
Ref<Material> material;
mutable Transform2D global_transform;
mutable MTFlag global_invalid;
_FORCE_INLINE_ bool _is_global_invalid() const { return is_group_processing() ? global_invalid.mt.is_set() : global_invalid.st; }
void _set_global_invalid(bool p_invalid) const;
void _top_level_raise_self();
void _propagate_visibility_changed(bool p_parent_visible_in_tree);
void _handle_visibility_change(bool p_visible);
virtual void _top_level_changed();
virtual void _top_level_changed_on_parent();
void _redraw_callback();
void _enter_canvas();
void _exit_canvas();
void _window_visibility_changed();
void _notify_transform(CanvasItem *p_node);
virtual void _physics_interpolated_changed() override;
static CanvasItem *current_item_drawn;
friend class Viewport;
void _refresh_texture_repeat_cache() const;
void _update_texture_repeat_changed(bool p_propagate);
void _refresh_texture_filter_cache() const;
void _update_texture_filter_changed(bool p_propagate);
void _notify_transform_deferred();
protected:
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat);
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter);
_FORCE_INLINE_ void _notify_transform() {
_notify_transform(this);
if (is_inside_tree() && !block_transform_notify && notify_local_transform) {
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
}
}
void item_rect_changed(bool p_size_changed = true);
void _notification(int p_what);
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
void _draw_circle_bind_compat_84472(const Point2 &p_pos, real_t p_radius, const Color &p_color);
void _draw_rect_bind_compat_84523(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width);
void _draw_dashed_line_bind_compat_84523(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned);
void _draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width);
void _draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width);
static void _bind_compatibility_methods();
#endif
void _validate_property(PropertyInfo &p_property) const;
_FORCE_INLINE_ void set_hide_clip_children(bool p_value) { hide_clip_children = p_value; }
GDVIRTUAL0(_draw)
public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique
NOTIFICATION_DRAW = 30,
NOTIFICATION_VISIBILITY_CHANGED = 31,
NOTIFICATION_ENTER_CANVAS = 32,
NOTIFICATION_EXIT_CANVAS = 33,
NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35,
NOTIFICATION_WORLD_2D_CHANGED = 36,
};
/* EDITOR */
#ifdef TOOLS_ENABLED
// Select the node
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state) {}
virtual Dictionary _edit_get_state() const { return Dictionary(); }
// Used to move the node
virtual void _edit_set_position(const Point2 &p_position) = 0;
virtual Point2 _edit_get_position() const = 0;
// Used to scale the node
virtual void _edit_set_scale(const Size2 &p_scale) = 0;
virtual Size2 _edit_get_scale() const = 0;
// Used to rotate the node
virtual bool _edit_use_rotation() const { return false; };
virtual void _edit_set_rotation(real_t p_rotation) {}
virtual real_t _edit_get_rotation() const { return 0.0; };
// Used to resize/move the node
virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode()
virtual void _edit_set_rect(const Rect2 &p_rect) {}
virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD
// Used to set a pivot
virtual bool _edit_use_pivot() const { return false; };
virtual void _edit_set_pivot(const Point2 &p_pivot) {}
virtual Point2 _edit_get_pivot() const { return Point2(); };
virtual Transform2D _edit_get_transform() const;
#endif
void update_draw_order();
/* VISIBILITY */
void set_visible(bool p_visible);
bool is_visible() const;
bool is_visible_in_tree() const;
void show();
void hide();
void queue_redraw();
void move_to_front();
void set_clip_children_mode(ClipChildrenMode p_clip_mode);
ClipChildrenMode get_clip_children_mode() const;
virtual void set_light_mask(int p_light_mask);
int get_light_mask() const;
void set_modulate(const Color &p_modulate);
Color get_modulate() const;
Color get_modulate_in_tree() const;
virtual void set_self_modulate(const Color &p_self_modulate);
Color get_self_modulate() const;
void set_visibility_layer(uint32_t p_visibility_layer);
uint32_t get_visibility_layer() const;
void set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable);
bool get_visibility_layer_bit(uint32_t p_visibility_layer) const;
/* ORDERING */
virtual void set_z_index(int p_z);
int get_z_index() const;
int get_effective_z_index() const;
void set_z_as_relative(bool p_enabled);
bool is_z_relative() const;
virtual void set_y_sort_enabled(bool p_enabled);
virtual bool is_y_sort_enabled() const;
/* DRAWING API */
void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, real_t p_dash = 2.0, bool p_aligned = true, bool p_antialiased = false);
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false);
void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false);
void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = -1.0, bool p_antialiased = false);
void draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color, bool p_filled = true, real_t p_width = -1.0, bool p_antialiased = false);
void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0, double p_scale = 1.0);
void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1));
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>());
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);
void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
void draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
void draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
void draw_set_transform_matrix(const Transform2D &p_matrix);
void draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset = 0);
void draw_end_animation();
static CanvasItem *get_current_item_drawn();
/* RECT / TRANSFORM */
void set_as_top_level(bool p_top_level);
bool is_set_as_top_level() const;
void set_draw_behind_parent(bool p_enable);
bool is_draw_behind_parent_enabled() const;
CanvasItem *get_parent_item() const;
virtual Transform2D get_transform() const = 0;
virtual Transform2D get_global_transform() const;
virtual Transform2D get_global_transform_with_canvas() const;
virtual Transform2D get_screen_transform() const;
CanvasItem *get_top_level() const;
_FORCE_INLINE_ RID get_canvas_item() const {
return canvas_item;
}
void set_block_transform_notify(bool p_enable);
bool is_block_transform_notify_enabled() const;
Transform2D get_canvas_transform() const;
Transform2D get_viewport_transform() const;
Rect2 get_viewport_rect() const;
RID get_viewport_rid() const;
RID get_canvas() const;
ObjectID get_canvas_layer_instance_id() const;
Ref<World2D> get_world_2d() const;
virtual void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
virtual void set_use_parent_material(bool p_use_parent_material);
bool get_use_parent_material() const;
Ref<InputEvent> make_input_local(const Ref<InputEvent> &p_event) const;
Vector2 make_canvas_position_local(const Vector2 &screen_point) const;
Vector2 get_global_mouse_position() const;
Vector2 get_local_mouse_position() const;
void set_notify_local_transform(bool p_enable);
bool is_local_transform_notification_enabled() const;
void set_notify_transform(bool p_enable);
bool is_transform_notification_enabled() const;
void force_update_transform();
virtual void set_texture_filter(TextureFilter p_texture_filter);
TextureFilter get_texture_filter() const;
virtual void set_texture_repeat(TextureRepeat p_texture_repeat);
TextureRepeat get_texture_repeat() const;
TextureFilter get_texture_filter_in_tree() const;
TextureRepeat get_texture_repeat_in_tree() const;
// Used by control nodes to retrieve the parent's anchorable area
virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); };
int get_canvas_layer() const;
CanvasLayer *get_canvas_layer_node() const;
CanvasItem();
~CanvasItem();
};
VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode)
class CanvasTexture : public Texture2D {
GDCLASS(CanvasTexture, Texture2D);
OBJ_SAVE_TYPE(Texture2D); // Saves derived classes with common type so they can be interchanged.
Ref<Texture2D> diffuse_texture;
Ref<Texture2D> normal_texture;
Ref<Texture2D> specular_texture;
Color specular = Color(1, 1, 1, 1);
real_t shininess = 1.0;
RID canvas_texture;
CanvasItem::TextureFilter texture_filter = CanvasItem::TEXTURE_FILTER_PARENT_NODE;
CanvasItem::TextureRepeat texture_repeat = CanvasItem::TEXTURE_REPEAT_PARENT_NODE;
protected:
static void _bind_methods();
public:
void set_diffuse_texture(const Ref<Texture2D> &p_diffuse);
Ref<Texture2D> get_diffuse_texture() const;
void set_normal_texture(const Ref<Texture2D> &p_normal);
Ref<Texture2D> get_normal_texture() const;
void set_specular_texture(const Ref<Texture2D> &p_specular);
Ref<Texture2D> get_specular_texture() const;
void set_specular_color(const Color &p_color);
Color get_specular_color() const;
void set_specular_shininess(real_t p_shininess);
real_t get_specular_shininess() const;
void set_texture_filter(CanvasItem::TextureFilter p_filter);
CanvasItem::TextureFilter get_texture_filter() const;
void set_texture_repeat(CanvasItem::TextureRepeat p_repeat);
CanvasItem::TextureRepeat get_texture_repeat() const;
virtual int get_width() const override;
virtual int get_height() const override;
virtual bool is_pixel_opaque(int p_x, int p_y) const override;
virtual bool has_alpha() const override;
virtual Ref<Image> get_image() const override;
virtual RID get_rid() const override;
CanvasTexture();
~CanvasTexture();
};
#endif // CANVAS_ITEM_H

View file

@ -0,0 +1,370 @@
/**************************************************************************/
/* canvas_layer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "canvas_layer.h"
#include "scene/main/canvas_item.h"
#include "scene/main/viewport.h"
#include "scene/resources/world_2d.h"
void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
if (viewport.is_valid()) {
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
vp->gui_set_root_order_dirty();
}
}
int CanvasLayer::get_layer() const {
return layer;
}
void CanvasLayer::set_visible(bool p_visible) {
if (p_visible == visible) {
return;
}
visible = p_visible;
emit_signal(SceneStringName(visibility_changed));
for (int i = 0; i < get_child_count(); i++) {
CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
if (c) {
RenderingServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible());
c->_propagate_visibility_changed(p_visible);
}
}
}
void CanvasLayer::show() {
set_visible(true);
}
void CanvasLayer::hide() {
set_visible(false);
}
bool CanvasLayer::is_visible() const {
return visible;
}
void CanvasLayer::set_transform(const Transform2D &p_xform) {
transform = p_xform;
locrotscale_dirty = true;
if (viewport.is_valid()) {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
Transform2D CanvasLayer::get_transform() const {
return transform;
}
Transform2D CanvasLayer::get_final_transform() const {
if (is_following_viewport()) {
Transform2D follow;
follow.scale(Vector2(get_follow_viewport_scale(), get_follow_viewport_scale()));
if (vp) {
follow = vp->get_canvas_transform() * follow;
}
return follow * transform;
}
return transform;
}
void CanvasLayer::_update_xform() {
transform.set_rotation_and_scale(rot, scale);
transform.set_origin(ofs);
if (viewport.is_valid()) {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
void CanvasLayer::_update_locrotscale() {
ofs = transform.columns[2];
rot = transform.get_rotation();
scale = transform.get_scale();
locrotscale_dirty = false;
}
void CanvasLayer::set_offset(const Vector2 &p_offset) {
if (locrotscale_dirty) {
_update_locrotscale();
}
ofs = p_offset;
_update_xform();
}
Vector2 CanvasLayer::get_offset() const {
if (locrotscale_dirty) {
const_cast<CanvasLayer *>(this)->_update_locrotscale();
}
return ofs;
}
void CanvasLayer::set_rotation(real_t p_radians) {
if (locrotscale_dirty) {
_update_locrotscale();
}
rot = p_radians;
_update_xform();
}
real_t CanvasLayer::get_rotation() const {
if (locrotscale_dirty) {
const_cast<CanvasLayer *>(this)->_update_locrotscale();
}
return rot;
}
void CanvasLayer::set_scale(const Vector2 &p_scale) {
if (locrotscale_dirty) {
_update_locrotscale();
}
scale = p_scale;
_update_xform();
}
Vector2 CanvasLayer::get_scale() const {
if (locrotscale_dirty) {
const_cast<CanvasLayer *>(this)->_update_locrotscale();
}
return scale;
}
void CanvasLayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
vp = custom_viewport;
} else {
vp = Node::get_viewport();
}
ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
vp->_canvas_layer_add(this);
viewport = vp->get_viewport_rid();
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
_update_follow_viewport();
if (vp) {
get_parent()->connect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent()), CONNECT_REFERENCE_COUNTED);
vp->canvas_parent_mark_dirty(get_parent());
}
} break;
case NOTIFICATION_EXIT_TREE: {
ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent()));
vp->_canvas_layer_remove(this);
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
_update_follow_viewport(false);
} break;
}
}
void CanvasLayer::update_draw_order() {
if (is_inside_tree()) {
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
}
}
Size2 CanvasLayer::get_viewport_size() const {
if (!is_inside_tree()) {
return Size2(1, 1);
}
ERR_FAIL_NULL_V_MSG(vp, Size2(1, 1), "Viewport is not initialized.");
Rect2 r = vp->get_visible_rect();
return r.size;
}
RID CanvasLayer::get_viewport() const {
return viewport;
}
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
ERR_FAIL_NULL_MSG(p_viewport, "Cannot set viewport to nullptr.");
if (is_inside_tree()) {
vp->_canvas_layer_remove(this);
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
}
custom_viewport = Object::cast_to<Viewport>(p_viewport);
if (custom_viewport) {
custom_viewport_id = custom_viewport->get_instance_id();
} else {
custom_viewport_id = ObjectID();
}
if (is_inside_tree()) {
if (custom_viewport) {
vp = custom_viewport;
} else {
vp = Node::get_viewport();
}
vp->_canvas_layer_add(this);
viewport = vp->get_viewport_rid();
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
Node *CanvasLayer::get_custom_viewport() const {
return custom_viewport;
}
void CanvasLayer::reset_sort_index() {
sort_index = 0;
}
int CanvasLayer::get_sort_index() {
return sort_index++;
}
RID CanvasLayer::get_canvas() const {
return canvas;
}
void CanvasLayer::set_follow_viewport(bool p_enable) {
if (follow_viewport == p_enable) {
return;
}
follow_viewport = p_enable;
_update_follow_viewport();
notify_property_list_changed();
}
bool CanvasLayer::is_following_viewport() const {
return follow_viewport;
}
void CanvasLayer::set_follow_viewport_scale(float p_ratio) {
follow_viewport_scale = p_ratio;
_update_follow_viewport();
}
float CanvasLayer::get_follow_viewport_scale() const {
return follow_viewport_scale;
}
void CanvasLayer::_update_follow_viewport(bool p_force_exit) {
if (!is_inside_tree()) {
return;
}
if (p_force_exit || !follow_viewport) {
RS::get_singleton()->canvas_set_parent(canvas, RID(), 1.0);
} else {
RS::get_singleton()->canvas_set_parent(canvas, vp->get_world_2d()->get_canvas(), follow_viewport_scale);
}
}
void CanvasLayer::_validate_property(PropertyInfo &p_property) const {
if (!follow_viewport && p_property.name == "follow_viewport_scale") {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer);
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasLayer::set_visible);
ClassDB::bind_method(D_METHOD("is_visible"), &CanvasLayer::is_visible);
ClassDB::bind_method(D_METHOD("show"), &CanvasLayer::show);
ClassDB::bind_method(D_METHOD("hide"), &CanvasLayer::hide);
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform);
ClassDB::bind_method(D_METHOD("get_final_transform"), &CanvasLayer::get_final_transform);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CanvasLayer::set_offset);
ClassDB::bind_method(D_METHOD("get_offset"), &CanvasLayer::get_offset);
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &CanvasLayer::set_rotation);
ClassDB::bind_method(D_METHOD("get_rotation"), &CanvasLayer::get_rotation);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &CanvasLayer::set_scale);
ClassDB::bind_method(D_METHOD("get_scale"), &CanvasLayer::get_scale);
ClassDB::bind_method(D_METHOD("set_follow_viewport", "enable"), &CanvasLayer::set_follow_viewport);
ClassDB::bind_method(D_METHOD("is_following_viewport"), &CanvasLayer::is_following_viewport);
ClassDB::bind_method(D_METHOD("set_follow_viewport_scale", "scale"), &CanvasLayer::set_follow_viewport_scale);
ClassDB::bind_method(D_METHOD("get_follow_viewport_scale"), &CanvasLayer::get_follow_viewport_scale);
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport);
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport);
ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas);
ADD_GROUP("Layer", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px"), "set_transform", "get_transform");
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
ADD_GROUP("Follow Viewport", "follow_viewport");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enabled"), "set_follow_viewport", "is_following_viewport");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_less"), "set_follow_viewport_scale", "get_follow_viewport_scale");
ADD_SIGNAL(MethodInfo("visibility_changed"));
}
CanvasLayer::CanvasLayer() {
canvas = RS::get_singleton()->canvas_create();
}
CanvasLayer::~CanvasLayer() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(canvas);
}

View file

@ -0,0 +1,115 @@
/**************************************************************************/
/* canvas_layer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 CANVAS_LAYER_H
#define CANVAS_LAYER_H
#include "scene/main/node.h"
class Viewport;
class CanvasLayer : public Node {
GDCLASS(CanvasLayer, Node);
bool locrotscale_dirty = false;
Vector2 ofs;
Size2 scale = Vector2(1, 1);
real_t rot = 0.0;
int layer = 1;
Transform2D transform;
RID canvas;
ObjectID custom_viewport_id; // to check validity
Viewport *custom_viewport = nullptr;
RID viewport;
Viewport *vp = nullptr;
int sort_index = 0;
bool visible = true;
bool follow_viewport = false;
float follow_viewport_scale = 1.0;
void _update_xform();
void _update_locrotscale();
void _update_follow_viewport(bool p_force_exit = false);
protected:
void _notification(int p_what);
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
public:
void update_draw_order();
void set_layer(int p_xform);
int get_layer() const;
void set_visible(bool p_visible);
bool is_visible() const;
void show();
void hide();
void set_transform(const Transform2D &p_xform);
Transform2D get_transform() const;
Transform2D get_final_transform() const;
void set_offset(const Vector2 &p_offset);
Vector2 get_offset() const;
void set_rotation(real_t p_radians);
real_t get_rotation() const;
void set_scale(const Size2 &p_scale);
Size2 get_scale() const;
Size2 get_viewport_size() const;
RID get_viewport() const;
void set_custom_viewport(Node *p_viewport);
Node *get_custom_viewport() const;
void reset_sort_index();
int get_sort_index();
void set_follow_viewport(bool p_enable);
bool is_following_viewport() const;
void set_follow_viewport_scale(float p_ratio);
float get_follow_viewport_scale() const;
RID get_canvas() const;
CanvasLayer();
~CanvasLayer();
};
#endif // CANVAS_LAYER_H

View file

@ -0,0 +1,670 @@
/**************************************************************************/
/* http_request.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "http_request.h"
#include "core/io/compression.h"
#include "scene/main/timer.h"
Error HTTPRequest::_request() {
return client->connect_to_host(url, port, use_tls ? tls_options : nullptr);
}
Error HTTPRequest::_parse_url(const String &p_url) {
use_tls = false;
request_string = "";
port = 80;
request_sent = false;
got_response = false;
body_len = -1;
body.clear();
downloaded.set(0);
final_body_size.set(0);
redirections = 0;
String scheme;
Error err = p_url.parse_url(scheme, url, port, request_string);
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
if (scheme == "https://") {
use_tls = true;
} else if (scheme != "http://") {
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Invalid URL scheme: '%s'.", scheme));
}
if (port == 0) {
port = use_tls ? 443 : 80;
}
if (request_string.is_empty()) {
request_string = "/";
}
return OK;
}
bool HTTPRequest::has_header(const PackedStringArray &p_headers, const String &p_header_name) {
bool exists = false;
String lower_case_header_name = p_header_name.to_lower();
for (int i = 0; i < p_headers.size() && !exists; i++) {
String sanitized = p_headers[i].strip_edges().to_lower();
if (sanitized.begins_with(lower_case_header_name)) {
exists = true;
}
}
return exists;
}
String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const String &p_header_name) {
String value = "";
String lowwer_case_header_name = p_header_name.to_lower();
for (int i = 0; i < p_headers.size(); i++) {
if (p_headers[i].find(":") > 0) {
Vector<String> parts = p_headers[i].split(":", false, 1);
if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
value = parts[1].strip_edges();
break;
}
}
}
return value;
}
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const String &p_request_data) {
// Copy the string into a raw buffer.
Vector<uint8_t> raw_data;
CharString charstr = p_request_data.utf8();
size_t len = charstr.length();
if (len > 0) {
raw_data.resize(len);
uint8_t *w = raw_data.ptrw();
memcpy(w, charstr.ptr(), len);
}
return request_raw(p_url, p_custom_headers, p_method, raw_data);
}
Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) {
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
if (timeout > 0) {
timer->stop();
timer->start(timeout);
}
method = p_method;
Error err = _parse_url(p_url);
if (err) {
return err;
}
headers = p_custom_headers;
if (accept_gzip) {
// If the user has specified an Accept-Encoding header, don't overwrite it.
if (!has_header(headers, "Accept-Encoding")) {
headers.push_back("Accept-Encoding: gzip, deflate");
}
}
request_data = p_request_data_raw;
requesting = true;
if (use_threads.is_set()) {
thread_done.clear();
thread_request_quit.clear();
client->set_blocking_mode(true);
thread.start(_thread_func, this);
} else {
client->set_blocking_mode(false);
err = _request();
if (err != OK) {
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return ERR_CANT_CONNECT;
}
set_process_internal(true);
}
return OK;
}
void HTTPRequest::_thread_func(void *p_userdata) {
HTTPRequest *hr = static_cast<HTTPRequest *>(p_userdata);
Error err = hr->_request();
if (err != OK) {
hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
} else {
while (!hr->thread_request_quit.is_set()) {
bool exit = hr->_update_connection();
if (exit) {
break;
}
OS::get_singleton()->delay_usec(1);
}
}
hr->thread_done.set();
}
void HTTPRequest::cancel_request() {
timer->stop();
if (!requesting) {
return;
}
if (!use_threads.is_set()) {
set_process_internal(false);
} else {
thread_request_quit.set();
if (thread.is_started()) {
thread.wait_to_finish();
}
}
file.unref();
decompressor.unref();
client->close();
body.clear();
got_response = false;
response_code = -1;
request_sent = false;
requesting = false;
}
bool HTTPRequest::_handle_response(bool *ret_value) {
if (!client->has_response()) {
_defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray());
*ret_value = true;
return true;
}
got_response = true;
response_code = client->get_response_code();
List<String> rheaders;
client->get_response_headers(&rheaders);
response_headers.clear();
downloaded.set(0);
final_body_size.set(0);
decompressor.unref();
for (const String &E : rheaders) {
response_headers.push_back(E);
}
if (response_code == 301 || response_code == 302) {
// Handle redirect.
if (max_redirects >= 0 && redirections >= max_redirects) {
_defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
*ret_value = true;
return true;
}
String new_request;
for (const String &E : rheaders) {
if (E.containsn("Location: ")) {
new_request = E.substr(9, E.length()).strip_edges();
}
}
if (!new_request.is_empty()) {
// Process redirect.
client->close();
int new_redirs = redirections + 1; // Because _request() will clear it.
Error err;
if (new_request.begins_with("http")) {
// New url, new request.
_parse_url(new_request);
} else {
request_string = new_request;
}
err = _request();
if (err == OK) {
request_sent = false;
got_response = false;
body_len = -1;
body.clear();
downloaded.set(0);
final_body_size.set(0);
redirections = new_redirs;
*ret_value = false;
return true;
}
}
}
// Check if we need to start streaming decompression.
String content_encoding;
if (accept_gzip) {
content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower();
}
if (content_encoding == "gzip") {
decompressor.instantiate();
decompressor->start_decompression(false, get_download_chunk_size());
} else if (content_encoding == "deflate") {
decompressor.instantiate();
decompressor->start_decompression(true, get_download_chunk_size());
}
return false;
}
bool HTTPRequest::_update_connection() {
switch (client->get_status()) {
case HTTPClient::STATUS_DISCONNECTED: {
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true; // End it, since it's disconnected.
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
// Must wait.
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
_defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
case HTTPClient::STATUS_CONNECTING: {
client->poll();
// Must wait.
return false;
} break; // Connecting to IP.
case HTTPClient::STATUS_CANT_CONNECT: {
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
case HTTPClient::STATUS_CONNECTED: {
if (request_sent) {
if (!got_response) {
// No body.
bool ret_value;
if (_handle_response(&ret_value)) {
return ret_value;
}
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
return true;
}
if (body_len < 0) {
// Chunked transfer is done.
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
_defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
return true;
// Request might have been done.
} else {
// Did not request yet, do request.
int size = request_data.size();
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
if (err != OK) {
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
}
request_sent = true;
return false;
}
} break; // Connected: break requests only accepted here.
case HTTPClient::STATUS_REQUESTING: {
// Must wait, still requesting.
client->poll();
return false;
} break; // Request in progress.
case HTTPClient::STATUS_BODY: {
if (!got_response) {
bool ret_value;
if (_handle_response(&ret_value)) {
return ret_value;
}
if (!client->is_response_chunked() && client->get_response_body_length() == 0) {
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
return true;
}
// No body len (-1) if chunked or no content-length header was provided.
// Change your webserver configuration if you want body len.
body_len = client->get_response_body_length();
if (body_size_limit >= 0 && body_len > body_size_limit) {
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
return true;
}
if (!download_to_file.is_empty()) {
file = FileAccess::open(download_to_file, FileAccess::WRITE);
if (file.is_null()) {
_defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
return true;
}
}
}
client->poll();
if (client->get_status() != HTTPClient::STATUS_BODY) {
return false;
}
PackedByteArray chunk;
if (decompressor.is_null()) {
// Chunk can be read directly.
chunk = client->read_response_body_chunk();
downloaded.add(chunk.size());
} else {
// Chunk is the result of decompression.
PackedByteArray compressed = client->read_response_body_chunk();
downloaded.add(compressed.size());
int pos = 0;
int left = compressed.size();
while (left) {
int w = 0;
Error err = decompressor->put_partial_data(compressed.ptr() + pos, left, w);
if (err == OK) {
PackedByteArray dc;
dc.resize(decompressor->get_available_bytes());
err = decompressor->get_data(dc.ptrw(), dc.size());
chunk.append_array(dc);
}
if (err != OK) {
_defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray());
return true;
}
// We need this check here because a "zip bomb" could result in a chunk of few kilos decompressing into gigabytes of data.
if (body_size_limit >= 0 && final_body_size.get() + chunk.size() > body_size_limit) {
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
return true;
}
pos += w;
left -= w;
}
}
final_body_size.add(chunk.size());
if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) {
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
return true;
}
if (chunk.size()) {
if (file.is_valid()) {
const uint8_t *r = chunk.ptr();
file->store_buffer(r, chunk.size());
if (file->get_error() != OK) {
_defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
return true;
}
} else {
body.append_array(chunk);
}
}
if (body_len >= 0) {
if (downloaded.get() == body_len) {
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
} else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) {
// We read till EOF, with no errors. Request is done.
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
return false;
} break; // Request resulted in body: break which must be read.
case HTTPClient::STATUS_CONNECTION_ERROR: {
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: {
_defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
} break;
}
ERR_FAIL_V(false);
}
void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
callable_mp(this, &HTTPRequest::_request_done).call_deferred(p_status, p_code, p_headers, p_data);
}
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
cancel_request();
emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
}
void HTTPRequest::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PROCESS: {
if (use_threads.is_set()) {
return;
}
bool done = _update_connection();
if (done) {
set_process_internal(false);
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (requesting) {
cancel_request();
}
} break;
}
}
void HTTPRequest::set_use_threads(bool p_use) {
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
#ifdef THREADS_ENABLED
use_threads.set_to(p_use);
#endif
}
bool HTTPRequest::is_using_threads() const {
return use_threads.is_set();
}
void HTTPRequest::set_accept_gzip(bool p_gzip) {
accept_gzip = p_gzip;
}
bool HTTPRequest::is_accepting_gzip() const {
return accept_gzip;
}
void HTTPRequest::set_body_size_limit(int p_bytes) {
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
body_size_limit = p_bytes;
}
int HTTPRequest::get_body_size_limit() const {
return body_size_limit;
}
void HTTPRequest::set_download_file(const String &p_file) {
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
download_to_file = p_file;
}
String HTTPRequest::get_download_file() const {
return download_to_file;
}
void HTTPRequest::set_download_chunk_size(int p_chunk_size) {
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
client->set_read_chunk_size(p_chunk_size);
}
int HTTPRequest::get_download_chunk_size() const {
return client->get_read_chunk_size();
}
HTTPClient::Status HTTPRequest::get_http_client_status() const {
return client->get_status();
}
void HTTPRequest::set_max_redirects(int p_max) {
max_redirects = p_max;
}
int HTTPRequest::get_max_redirects() const {
return max_redirects;
}
int HTTPRequest::get_downloaded_bytes() const {
return downloaded.get();
}
int HTTPRequest::get_body_size() const {
return body_len;
}
void HTTPRequest::set_http_proxy(const String &p_host, int p_port) {
client->set_http_proxy(p_host, p_port);
}
void HTTPRequest::set_https_proxy(const String &p_host, int p_port) {
client->set_https_proxy(p_host, p_port);
}
void HTTPRequest::set_timeout(double p_timeout) {
ERR_FAIL_COND(p_timeout < 0);
timeout = p_timeout;
}
double HTTPRequest::get_timeout() {
return timeout;
}
void HTTPRequest::_timeout() {
cancel_request();
_defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray());
}
void HTTPRequest::set_tls_options(const Ref<TLSOptions> &p_options) {
ERR_FAIL_COND(p_options.is_null() || p_options->is_server());
tls_options = p_options;
}
void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray()));
ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request);
ClassDB::bind_method(D_METHOD("set_tls_options", "client_options"), &HTTPRequest::set_tls_options);
ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status);
ClassDB::bind_method(D_METHOD("set_use_threads", "enable"), &HTTPRequest::set_use_threads);
ClassDB::bind_method(D_METHOD("is_using_threads"), &HTTPRequest::is_using_threads);
ClassDB::bind_method(D_METHOD("set_accept_gzip", "enable"), &HTTPRequest::set_accept_gzip);
ClassDB::bind_method(D_METHOD("is_accepting_gzip"), &HTTPRequest::is_accepting_gzip);
ClassDB::bind_method(D_METHOD("set_body_size_limit", "bytes"), &HTTPRequest::set_body_size_limit);
ClassDB::bind_method(D_METHOD("get_body_size_limit"), &HTTPRequest::get_body_size_limit);
ClassDB::bind_method(D_METHOD("set_max_redirects", "amount"), &HTTPRequest::set_max_redirects);
ClassDB::bind_method(D_METHOD("get_max_redirects"), &HTTPRequest::get_max_redirects);
ClassDB::bind_method(D_METHOD("set_download_file", "path"), &HTTPRequest::set_download_file);
ClassDB::bind_method(D_METHOD("get_download_file"), &HTTPRequest::get_download_file);
ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes);
ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size);
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout);
ClassDB::bind_method(D_METHOD("get_timeout"), &HTTPRequest::get_timeout);
ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size);
ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size);
ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy);
ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file");
ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216,suffix:B"), "set_download_chunk_size", "get_download_chunk_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000,suffix:B"), "set_body_size_limit", "get_body_size_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeout", PROPERTY_HINT_RANGE, "0,3600,0.1,or_greater,suffix:s"), "set_timeout", "get_timeout");
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
BIND_ENUM_CONSTANT(RESULT_SUCCESS);
BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH);
BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT);
BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE);
BIND_ENUM_CONSTANT(RESULT_CONNECTION_ERROR);
BIND_ENUM_CONSTANT(RESULT_TLS_HANDSHAKE_ERROR);
BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE);
BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED);
BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED);
BIND_ENUM_CONSTANT(RESULT_REQUEST_FAILED);
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_CANT_OPEN);
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_WRITE_ERROR);
BIND_ENUM_CONSTANT(RESULT_REDIRECT_LIMIT_REACHED);
BIND_ENUM_CONSTANT(RESULT_TIMEOUT);
}
HTTPRequest::HTTPRequest() {
client = Ref<HTTPClient>(HTTPClient::create());
tls_options = TLSOptions::client();
timer = memnew(Timer);
timer->set_one_shot(true);
timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout));
add_child(timer);
}

View file

@ -0,0 +1,171 @@
/**************************************************************************/
/* http_request.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 HTTP_REQUEST_H
#define HTTP_REQUEST_H
#include "core/io/http_client.h"
#include "core/io/stream_peer_gzip.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "scene/main/node.h"
class Timer;
class HTTPRequest : public Node {
GDCLASS(HTTPRequest, Node);
public:
enum Result {
RESULT_SUCCESS,
RESULT_CHUNKED_BODY_SIZE_MISMATCH,
RESULT_CANT_CONNECT,
RESULT_CANT_RESOLVE,
RESULT_CONNECTION_ERROR,
RESULT_TLS_HANDSHAKE_ERROR,
RESULT_NO_RESPONSE,
RESULT_BODY_SIZE_LIMIT_EXCEEDED,
RESULT_BODY_DECOMPRESS_FAILED,
RESULT_REQUEST_FAILED,
RESULT_DOWNLOAD_FILE_CANT_OPEN,
RESULT_DOWNLOAD_FILE_WRITE_ERROR,
RESULT_REDIRECT_LIMIT_REACHED,
RESULT_TIMEOUT
};
private:
bool requesting = false;
String request_string;
String url;
int port = 80;
Vector<String> headers;
bool use_tls = false;
Ref<TLSOptions> tls_options;
HTTPClient::Method method;
Vector<uint8_t> request_data;
bool request_sent = false;
Ref<HTTPClient> client;
PackedByteArray body;
SafeFlag use_threads;
bool accept_gzip = true;
bool got_response = false;
int response_code = 0;
Vector<String> response_headers;
String download_to_file;
Ref<StreamPeerGZIP> decompressor;
Ref<FileAccess> file;
int body_len = -1;
SafeNumeric<int> downloaded;
SafeNumeric<int> final_body_size;
int body_size_limit = -1;
int redirections = 0;
bool _update_connection();
int max_redirects = 8;
double timeout = 0;
void _redirect_request(const String &p_new_url);
bool _handle_response(bool *ret_value);
Error _parse_url(const String &p_url);
Error _request();
bool has_header(const PackedStringArray &p_headers, const String &p_header_name);
String get_header_value(const PackedStringArray &p_headers, const String &header_name);
SafeFlag thread_done;
SafeFlag thread_request_quit;
Thread thread;
void _defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
static void _thread_func(void *p_userdata);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request
Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request
void cancel_request();
HTTPClient::Status get_http_client_status() const;
void set_use_threads(bool p_use);
bool is_using_threads() const;
void set_accept_gzip(bool p_gzip);
bool is_accepting_gzip() const;
void set_download_file(const String &p_file);
String get_download_file() const;
void set_download_chunk_size(int p_chunk_size);
int get_download_chunk_size() const;
void set_body_size_limit(int p_bytes);
int get_body_size_limit() const;
void set_max_redirects(int p_max);
int get_max_redirects() const;
Timer *timer = nullptr;
void set_timeout(double p_timeout);
double get_timeout();
void _timeout();
int get_downloaded_bytes() const;
int get_body_size() const;
void set_http_proxy(const String &p_host, int p_port);
void set_https_proxy(const String &p_host, int p_port);
void set_tls_options(const Ref<TLSOptions> &p_options);
HTTPRequest();
};
VARIANT_ENUM_CAST(HTTPRequest::Result);
#endif // HTTP_REQUEST_H

View file

@ -0,0 +1,257 @@
/**************************************************************************/
/* instance_placeholder.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "instance_placeholder.h"
#include "core/io/resource_loader.h"
#include "scene/resources/packed_scene.h"
bool InstancePlaceholder::_set(const StringName &p_name, const Variant &p_value) {
PropSet ps;
ps.name = p_name;
ps.value = p_value;
stored_values.push_back(ps);
return true;
}
bool InstancePlaceholder::_get(const StringName &p_name, Variant &r_ret) const {
for (const PropSet &E : stored_values) {
if (E.name == p_name) {
r_ret = E.value;
return true;
}
}
return false;
}
void InstancePlaceholder::_get_property_list(List<PropertyInfo> *p_list) const {
for (const PropSet &E : stored_values) {
PropertyInfo pi;
pi.name = E.name;
pi.type = E.value.get_type();
pi.usage = PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
}
void InstancePlaceholder::set_instance_path(const String &p_name) {
path = p_name;
}
String InstancePlaceholder::get_instance_path() const {
return path;
}
Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene> &p_custom_scene) {
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
Node *base = get_parent();
if (!base) {
return nullptr;
}
Ref<PackedScene> ps;
if (p_custom_scene.is_valid()) {
ps = p_custom_scene;
} else {
ps = ResourceLoader::load(path, "PackedScene");
}
if (!ps.is_valid()) {
return nullptr;
}
Node *instance = ps->instantiate();
if (!instance) {
return nullptr;
}
instance->set_name(get_name());
instance->set_multiplayer_authority(get_multiplayer_authority());
int pos = get_index();
for (const PropSet &E : stored_values) {
set_value_on_instance(this, instance, E);
}
if (p_replace) {
queue_free();
base->remove_child(this);
}
base->add_child(instance);
base->move_child(instance, pos);
return instance;
}
// This method will attempt to set the correct values on the placeholder instance
// for regular types this is trivial and unnecessary.
// For nodes however this becomes a bit tricky because they might now have existed until the instantiation,
// so this method will try to find the correct nodes and resolve them.
void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set) {
bool is_valid;
// If we don't have any info, we can't do anything,
// so try setting the value directly.
Variant current = p_instance->get(p_set.name, &is_valid);
if (!is_valid) {
p_instance->set(p_set.name, p_set.value, &is_valid);
return;
}
Variant::Type current_type = current.get_type();
Variant::Type placeholder_type = p_set.value.get_type();
// Arrays are a special case, because their containing type might be different.
if (current_type != Variant::Type::ARRAY) {
// Check if the variant types match.
if (Variant::evaluate(Variant::OP_EQUAL, current_type, placeholder_type)) {
p_instance->set(p_set.name, p_set.value, &is_valid);
if (is_valid) {
return;
}
// Types match but setting failed? This is strange, so let's print a warning!
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
return;
}
} else {
// We are dealing with an Array.
// Let's check if the subtype of the array matches first.
// This is needed because the set method of ScriptInstance checks for type,
// but the ClassDB set method doesn't! So we cannot reliably know what actually happens.
Array current_array = current;
Array placeholder_array = p_set.value;
if (current_array.is_same_typed(placeholder_array)) {
p_instance->set(p_set.name, p_set.value, &is_valid);
if (is_valid) {
return;
}
// Internal array types match but setting failed? This is strange, so let's print a warning!
WARN_PRINT(vformat("Array Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(Variant::Type(current_array.get_typed_builtin())), p_placeholder->get_name()));
}
// Arrays are not the same internal type. This should be happening because we have a NodePath Array,
// but the instance wants a Node Array.
}
switch (current_type) {
case Variant::Type::NIL: {
Ref<Resource> resource = p_set.value;
if (placeholder_type != Variant::Type::NODE_PATH && !resource.is_valid()) {
break;
}
// If it's nil but we have a NodePath or a Resource, we guess what works.
p_instance->set(p_set.name, p_set.value, &is_valid);
if (is_valid) {
break;
}
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid);
break;
}
case Variant::Type::OBJECT: {
if (placeholder_type != Variant::Type::NODE_PATH) {
break;
}
// Easiest case, we want a node, but we have a deferred NodePath.
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value));
break;
}
case Variant::Type::ARRAY: {
// If we have reached here it means our array types don't match,
// so we will convert the placeholder array into the correct type
// and resolve nodes if necessary.
Array current_array = current;
Array converted_array;
Array placeholder_array = p_set.value;
converted_array = current_array.duplicate();
converted_array.resize(placeholder_array.size());
if (Variant::evaluate(Variant::OP_EQUAL, current_array.get_typed_builtin(), Variant::Type::NODE_PATH)) {
// We want a typed NodePath array.
for (int i = 0; i < placeholder_array.size(); i++) {
converted_array.set(i, placeholder_array[i]);
}
} else {
// We want Nodes, convert NodePaths.
for (int i = 0; i < placeholder_array.size(); i++) {
converted_array.set(i, try_get_node(p_placeholder, p_instance, placeholder_array[i]));
}
}
p_instance->set(p_set.name, converted_array, &is_valid);
if (!is_valid) {
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
}
break;
}
default: {
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
break;
}
}
}
Node *InstancePlaceholder::try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path) {
// First try to resolve internally,
// if that fails try resolving externally.
Node *node = p_instance->get_node_or_null(p_path);
if (node == nullptr) {
node = p_placeholder->get_node_or_null(p_path);
}
return node;
}
Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) {
Dictionary ret;
PackedStringArray order;
for (const PropSet &E : stored_values) {
ret[E.name] = E.value;
if (p_with_order) {
order.push_back(E.name);
}
};
if (p_with_order) {
ret[".order"] = order;
}
return ret;
};
void InstancePlaceholder::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "replace", "custom_scene"), &InstancePlaceholder::create_instance, DEFVAL(false), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("get_instance_path"), &InstancePlaceholder::get_instance_path);
}
InstancePlaceholder::InstancePlaceholder() {
}

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* instance_placeholder.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 INSTANCE_PLACEHOLDER_H
#define INSTANCE_PLACEHOLDER_H
#include "scene/main/node.h"
class PackedScene;
class InstancePlaceholder : public Node {
GDCLASS(InstancePlaceholder, Node);
String path;
struct PropSet {
StringName name;
Variant value;
};
List<PropSet> stored_values;
private:
void set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set);
Node *try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path);
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:
void set_instance_path(const String &p_name);
String get_instance_path() const;
Dictionary get_stored_values(bool p_with_order = false);
Node *create_instance(bool p_replace = false, const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>());
InstancePlaceholder();
};
#endif // INSTANCE_PLACEHOLDER_H

View file

@ -0,0 +1,117 @@
/**************************************************************************/
/* missing_node.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "missing_node.h"
bool MissingNode::_set(const StringName &p_name, const Variant &p_value) {
if (is_recording_properties()) {
properties.insert(p_name, p_value);
return true; //always valid to set (add)
} else {
if (!properties.has(p_name)) {
return false;
}
properties[p_name] = p_value;
return true;
}
}
bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const {
if (!properties.has(p_name)) {
return false;
}
r_ret = properties[p_name];
return true;
}
void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const {
for (const KeyValue<StringName, Variant> &E : properties) {
p_list->push_back(PropertyInfo(E.value.get_type(), E.key));
}
}
void MissingNode::set_original_class(const String &p_class) {
original_class = p_class;
}
String MissingNode::get_original_class() const {
return original_class;
}
void MissingNode::set_original_scene(const String &p_scene) {
original_scene = p_scene;
}
String MissingNode::get_original_scene() const {
return original_scene;
}
void MissingNode::set_recording_properties(bool p_enable) {
recording_properties = p_enable;
}
bool MissingNode::is_recording_properties() const {
return recording_properties;
}
PackedStringArray MissingNode::get_configuration_warnings() const {
// The mere existence of this node is warning.
PackedStringArray ret;
if (!original_scene.is_empty()) {
ret.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
ret.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
} else if (!original_class.is_empty()) {
ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
} else {
ret.push_back(RTR("Unrecognized missing node. Check scene dependency errors for details."));
}
return ret;
}
void MissingNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class);
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class);
ClassDB::bind_method(D_METHOD("set_original_scene", "name"), &MissingNode::set_original_class);
ClassDB::bind_method(D_METHOD("get_original_scene"), &MissingNode::get_original_class);
ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties);
ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties);
// Expose, but not save.
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_scene", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_scene", "get_original_scene");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
}
MissingNode::MissingNode() {
}

View file

@ -0,0 +1,67 @@
/**************************************************************************/
/* missing_node.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 MISSING_NODE_H
#define MISSING_NODE_H
#include "core/io/missing_resource.h"
#include "scene/main/node.h"
class MissingNode : public Node {
GDCLASS(MissingNode, Node)
HashMap<StringName, Variant> properties;
String original_class;
String original_scene;
bool recording_properties = false;
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:
void set_original_class(const String &p_class);
String get_original_class() const;
void set_original_scene(const String &p_scene);
String get_original_scene() const;
void set_recording_properties(bool p_enable);
bool is_recording_properties() const;
virtual PackedStringArray get_configuration_warnings() const override;
MissingNode();
};
#endif // MISSING_NODE_H

View file

@ -0,0 +1,396 @@
/**************************************************************************/
/* multiplayer_api.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "multiplayer_api.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include <stdint.h>
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
#endif
StringName MultiplayerAPI::default_interface;
void MultiplayerAPI::set_default_interface(const StringName &p_interface) {
ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface));
default_interface = StringName(p_interface, true);
}
StringName MultiplayerAPI::get_default_interface() {
return default_interface;
}
Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() {
if (default_interface != StringName()) {
return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface)));
}
return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension));
}
// The variant is compressed and encoded; The first byte contains all the meta
// information and the format is:
// - The first LSB 6 bits are used for the variant type.
// - The next two bits are used to store the encoding mode.
// - Boolean values uses the encoding mode to store the value.
#define VARIANT_META_TYPE_MASK 0x3F
#define VARIANT_META_EMODE_MASK 0xC0
#define VARIANT_META_BOOL_MASK 0x80
#define ENCODE_8 0 << 6
#define ENCODE_16 1 << 6
#define ENCODE_32 2 << 6
#define ENCODE_64 3 << 6
Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
// Unreachable because `VARIANT_MAX` == 38 and `ENCODE_VARIANT_MASK` == 77
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
uint8_t *buf = r_buffer;
r_len = 0;
uint8_t encode_mode = 0;
switch (p_variant.get_type()) {
case Variant::BOOL: {
if (buf) {
// We don't use encode_mode for booleans, so we can use it to store the value.
buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
buf[0] |= p_variant.get_type();
}
r_len += 1;
} break;
case Variant::INT: {
if (buf) {
// Reserve the first byte for the meta.
buf += 1;
}
r_len += 1;
int64_t val = p_variant;
if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
// Use 8 bit
encode_mode = ENCODE_8;
if (buf) {
buf[0] = val;
}
r_len += 1;
} else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
// Use 16 bit
encode_mode = ENCODE_16;
if (buf) {
encode_uint16(val, buf);
}
r_len += 2;
} else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
// Use 32 bit
encode_mode = ENCODE_32;
if (buf) {
encode_uint32(val, buf);
}
r_len += 4;
} else {
// Use 64 bit
encode_mode = ENCODE_64;
if (buf) {
encode_uint64(val, buf);
}
r_len += 8;
}
// Store the meta
if (buf) {
buf -= 1;
buf[0] = encode_mode | p_variant.get_type();
}
} break;
default:
// Any other case is not yet compressed.
Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
if (r_buffer) {
// The first byte is not used by the marshaling, so store the type
// so we know how to decompress and decode this variant.
r_buffer[0] = p_variant.get_type();
}
}
return OK;
}
Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
const uint8_t *buf = p_buffer;
int len = p_len;
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
switch (type) {
case Variant::BOOL: {
bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
r_variant = val;
if (r_len) {
*r_len = 1;
}
} break;
case Variant::INT: {
buf += 1;
len -= 1;
if (r_len) {
*r_len = 1;
}
if (encode_mode == ENCODE_8) {
// 8 bits.
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
int8_t val = buf[0];
r_variant = val;
if (r_len) {
(*r_len) += 1;
}
} else if (encode_mode == ENCODE_16) {
// 16 bits.
ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
int16_t val = decode_uint16(buf);
r_variant = val;
if (r_len) {
(*r_len) += 2;
}
} else if (encode_mode == ENCODE_32) {
// 32 bits.
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t val = decode_uint32(buf);
r_variant = val;
if (r_len) {
(*r_len) += 4;
}
} else {
// 64 bits.
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
int64_t val = decode_uint64(buf);
r_variant = val;
if (r_len) {
(*r_len) += 8;
}
}
} break;
default:
Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
}
return OK;
}
Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
r_len = 0;
int size = 0;
if (p_count == 0) {
if (r_raw) {
*r_raw = true;
}
return OK;
}
// Try raw encoding optimization.
if (r_raw && p_count == 1) {
*r_raw = false;
const Variant &v = *(p_variants[0]);
if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
*r_raw = true;
const PackedByteArray pba = v;
if (p_buffer) {
memcpy(p_buffer, pba.ptr(), pba.size());
}
r_len += pba.size();
} else {
encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
r_len += size;
}
return OK;
}
// Regular encoding.
for (int i = 0; i < p_count; i++) {
const Variant &v = *(p_variants[i]);
encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
r_len += size;
}
return OK;
}
Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
r_len = 0;
int argc = r_variants.size();
if (argc == 0 && p_raw) {
return OK;
}
ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
if (p_raw) {
r_len = p_len;
PackedByteArray pba;
pba.resize(p_len);
memcpy(pba.ptrw(), p_buffer, p_len);
r_variants.write[0] = pba;
return OK;
}
for (int i = 0; i < argc; i++) {
ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
int vlen;
Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
r_len += vlen;
}
return OK;
}
Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) {
Vector<Variant> args;
Vector<const Variant *> argsp;
args.resize(p_args.size());
argsp.resize(p_args.size());
Variant *ptr = args.ptrw();
const Variant **pptr = argsp.ptrw();
for (int i = 0; i < p_args.size(); i++) {
ptr[i] = p_args[i];
pptr[i] = &ptr[i];
}
return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size());
}
void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add);
ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove);
ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface);
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface);
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface);
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("connected_to_server"));
ADD_SIGNAL(MethodInfo("connection_failed"));
ADD_SIGNAL(MethodInfo("server_disconnected"));
BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER);
BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
}
/// MultiplayerAPIExtension
Error MultiplayerAPIExtension::poll() {
Error err = OK;
GDVIRTUAL_CALL(_poll, err);
return err;
}
void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer);
}
Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() {
Ref<MultiplayerPeer> peer;
GDVIRTUAL_CALL(_get_multiplayer_peer, peer);
return peer;
}
int MultiplayerAPIExtension::get_unique_id() {
int id = 1;
GDVIRTUAL_CALL(_get_unique_id, id);
return id;
}
Vector<int> MultiplayerAPIExtension::get_peer_ids() {
Vector<int> ids;
GDVIRTUAL_CALL(_get_peer_ids, ids);
return ids;
}
Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) {
return ERR_UNAVAILABLE;
}
Array args;
for (int i = 0; i < p_argcount; i++) {
args.push_back(*p_arg[i]);
}
Error ret = FAILED;
GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret);
return ret;
}
int MultiplayerAPIExtension::get_remote_sender_id() {
int id = 0;
GDVIRTUAL_CALL(_get_remote_sender_id, id);
return id;
}
Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) {
Error err = ERR_UNAVAILABLE;
GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err);
return err;
}
Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) {
Error err = ERR_UNAVAILABLE;
GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err);
return err;
}
void MultiplayerAPIExtension::_bind_methods() {
GDVIRTUAL_BIND(_poll);
GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer");
GDVIRTUAL_BIND(_get_multiplayer_peer);
GDVIRTUAL_BIND(_get_unique_id);
GDVIRTUAL_BIND(_get_peer_ids);
GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args");
GDVIRTUAL_BIND(_get_remote_sender_id);
GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration");
GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration");
}

View file

@ -0,0 +1,115 @@
/**************************************************************************/
/* multiplayer_api.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 MULTIPLAYER_API_H
#define MULTIPLAYER_API_H
#include "core/object/ref_counted.h"
#include "scene/main/multiplayer_peer.h"
class MultiplayerAPI : public RefCounted {
GDCLASS(MultiplayerAPI, RefCounted);
private:
static StringName default_interface;
protected:
static void _bind_methods();
Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array());
public:
enum RPCMode {
RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
RPC_MODE_ANY_PEER, // Any peer can call this RPC
RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC
};
static Ref<MultiplayerAPI> create_default_interface();
static void set_default_interface(const StringName &p_interface);
static StringName get_default_interface();
static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
virtual Error poll() = 0;
virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0;
virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0;
virtual int get_unique_id() = 0;
virtual Vector<int> get_peer_ids() = 0;
virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0;
virtual int get_remote_sender_id() = 0;
virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0;
virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0;
bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); }
bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; }
MultiplayerAPI() {}
virtual ~MultiplayerAPI() {}
};
VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
class MultiplayerAPIExtension : public MultiplayerAPI {
GDCLASS(MultiplayerAPIExtension, MultiplayerAPI);
protected:
static void _bind_methods();
public:
virtual Error poll() override;
virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
virtual int get_unique_id() override;
virtual Vector<int> get_peer_ids() override;
virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
virtual int get_remote_sender_id() override;
virtual Error object_configuration_add(Object *p_object, Variant p_config) override;
virtual Error object_configuration_remove(Object *p_object, Variant p_config) override;
// Extensions
GDVIRTUAL0R(Error, _poll);
GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>);
GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer);
GDVIRTUAL0RC(int, _get_unique_id);
GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids);
GDVIRTUAL4R(Error, _rpc, int, Object *, StringName, Array);
GDVIRTUAL0RC(int, _get_remote_sender_id);
GDVIRTUAL2R(Error, _object_configuration_add, Object *, Variant);
GDVIRTUAL2R(Error, _object_configuration_remove, Object *, Variant);
};
#endif // MULTIPLAYER_API_H

View file

@ -0,0 +1,228 @@
/**************************************************************************/
/* multiplayer_peer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "multiplayer_peer.h"
#include "core/os/os.h"
uint32_t MultiplayerPeer::generate_unique_id() const {
uint32_t hash = 0;
while (hash == 0 || hash == 1) {
hash = hash_murmur3_one_32(
(uint32_t)OS::get_singleton()->get_ticks_usec());
hash = hash_murmur3_one_32(
(uint32_t)OS::get_singleton()->get_unix_time(), hash);
hash = hash_murmur3_one_32(
(uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
hash = hash_murmur3_one_32(
(uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
hash = hash_murmur3_one_32(
(uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
hash = hash_fmix32(hash);
hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
}
return hash;
}
void MultiplayerPeer::set_transfer_channel(int p_channel) {
transfer_channel = p_channel;
}
int MultiplayerPeer::get_transfer_channel() const {
return transfer_channel;
}
void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode;
}
MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const {
return transfer_mode;
}
void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
refuse_connections = p_enable;
}
bool MultiplayerPeer::is_refusing_new_connections() const {
return refuse_connections;
}
bool MultiplayerPeer::is_server_relay_supported() const {
return false;
}
void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel);
ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
ClassDB::bind_method(D_METHOD("close"), &MultiplayerPeer::close);
ClassDB::bind_method(D_METHOD("disconnect_peer", "peer", "force"), &MultiplayerPeer::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
BIND_CONSTANT(TARGET_PEER_BROADCAST);
BIND_CONSTANT(TARGET_PEER_SERVER);
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
}
/*************/
Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
Error err;
if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
return err;
}
if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) {
if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) {
return FAILED;
}
if (script_buffer.size() == 0) {
return Error::ERR_UNAVAILABLE;
}
*r_buffer = script_buffer.ptr();
r_buffer_size = script_buffer.size();
return Error::OK;
}
WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
return FAILED;
}
Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
Error err;
if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
return err;
}
if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
PackedByteArray a;
a.resize(p_buffer_size);
memcpy(a.ptrw(), p_buffer, p_buffer_size);
if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) {
return FAILED;
}
return err;
}
WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
return FAILED;
}
void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
return;
}
MultiplayerPeer::set_refuse_new_connections(p_enable);
}
bool MultiplayerPeerExtension::is_refusing_new_connections() const {
bool refusing;
if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
return refusing;
}
return MultiplayerPeer::is_refusing_new_connections();
}
bool MultiplayerPeerExtension::is_server_relay_supported() const {
bool can_relay;
if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) {
return can_relay;
}
return MultiplayerPeer::is_server_relay_supported();
}
void MultiplayerPeerExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
GDVIRTUAL_BIND(_get_available_packet_count);
GDVIRTUAL_BIND(_get_max_packet_size);
GDVIRTUAL_BIND(_get_packet_script)
GDVIRTUAL_BIND(_put_packet_script, "p_buffer");
GDVIRTUAL_BIND(_get_packet_channel);
GDVIRTUAL_BIND(_get_packet_mode);
GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
GDVIRTUAL_BIND(_get_transfer_channel);
GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
GDVIRTUAL_BIND(_get_transfer_mode);
GDVIRTUAL_BIND(_set_target_peer, "p_peer");
GDVIRTUAL_BIND(_get_packet_peer);
GDVIRTUAL_BIND(_is_server);
GDVIRTUAL_BIND(_poll);
GDVIRTUAL_BIND(_close);
GDVIRTUAL_BIND(_disconnect_peer, "p_peer", "p_force");
GDVIRTUAL_BIND(_get_unique_id);
GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
GDVIRTUAL_BIND(_is_refusing_new_connections);
GDVIRTUAL_BIND(_is_server_relay_supported);
GDVIRTUAL_BIND(_get_connection_status);
ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_RELIABLE);
ADD_PROPERTY_DEFAULT("transfer_channel", 0);
}

View file

@ -0,0 +1,150 @@
/**************************************************************************/
/* multiplayer_peer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 MULTIPLAYER_PEER_H
#define MULTIPLAYER_PEER_H
#include "core/io/packet_peer.h"
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
class MultiplayerPeer : public PacketPeer {
GDCLASS(MultiplayerPeer, PacketPeer);
public:
enum TransferMode {
TRANSFER_MODE_UNRELIABLE,
TRANSFER_MODE_UNRELIABLE_ORDERED,
TRANSFER_MODE_RELIABLE
};
protected:
static void _bind_methods();
private:
int transfer_channel = 0;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
bool refuse_connections = false;
public:
enum {
TARGET_PEER_BROADCAST = 0,
TARGET_PEER_SERVER = 1
};
enum ConnectionStatus {
CONNECTION_DISCONNECTED,
CONNECTION_CONNECTING,
CONNECTION_CONNECTED,
};
virtual void set_transfer_channel(int p_channel);
virtual int get_transfer_channel() const;
virtual void set_transfer_mode(TransferMode p_mode);
virtual TransferMode get_transfer_mode() const;
virtual void set_refuse_new_connections(bool p_enable);
virtual bool is_refusing_new_connections() const;
virtual bool is_server_relay_supported() const;
virtual void set_target_peer(int p_peer_id) = 0;
virtual int get_packet_peer() const = 0;
virtual TransferMode get_packet_mode() const = 0;
virtual int get_packet_channel() const = 0;
virtual void disconnect_peer(int p_peer, bool p_force = false) = 0;
virtual bool is_server() const = 0;
virtual void poll() = 0;
virtual void close() = 0;
virtual int get_unique_id() const = 0;
virtual ConnectionStatus get_connection_status() const = 0;
uint32_t generate_unique_id() const;
MultiplayerPeer() {}
};
VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode);
class MultiplayerPeerExtension : public MultiplayerPeer {
GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
protected:
static void _bind_methods();
PackedByteArray script_buffer;
public:
/* PacketPeer extension */
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
GDVIRTUAL2R(Error, _get_packet, GDExtensionConstPtr<const uint8_t *>, GDExtensionPtr<int>);
GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript.
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
GDVIRTUAL2R(Error, _put_packet, GDExtensionConstPtr<const uint8_t>, int);
GDVIRTUAL1R(Error, _put_packet_script, PackedByteArray); // For GDScript.
EXBIND0RC(int, get_available_packet_count);
EXBIND0RC(int, get_max_packet_size);
/* MultiplayerPeer extension */
virtual void set_refuse_new_connections(bool p_enable) override;
GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional.
virtual bool is_refusing_new_connections() const override;
GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional.
virtual bool is_server_relay_supported() const override;
GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional.
EXBIND1(set_transfer_channel, int);
EXBIND0RC(int, get_transfer_channel);
EXBIND1(set_transfer_mode, TransferMode);
EXBIND0RC(TransferMode, get_transfer_mode);
EXBIND1(set_target_peer, int);
EXBIND0RC(int, get_packet_peer);
EXBIND0RC(TransferMode, get_packet_mode);
EXBIND0RC(int, get_packet_channel);
EXBIND0RC(bool, is_server);
EXBIND0(poll);
EXBIND0(close);
EXBIND2(disconnect_peer, int, bool);
EXBIND0RC(int, get_unique_id);
EXBIND0RC(ConnectionStatus, get_connection_status);
};
#endif // MULTIPLAYER_PEER_H

3943
engine/scene/main/node.cpp Normal file

File diff suppressed because it is too large Load diff

820
engine/scene/main/node.h Normal file
View file

@ -0,0 +1,820 @@
/**************************************************************************/
/* node.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 NODE_H
#define NODE_H
#include "core/string/node_path.h"
#include "core/templates/rb_map.h"
#include "core/variant/typed_array.h"
#include "scene/main/scene_tree.h"
#include "scene/scene_string_names.h"
class Viewport;
class Window;
class SceneState;
class Tween;
class PropertyTweener;
SAFE_FLAG_TYPE_PUN_GUARANTEES
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
class Node : public Object {
GDCLASS(Node, Object);
protected:
// During group processing, these are thread-safe.
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
union MTFlag {
SafeFlag mt;
bool st;
MTFlag() :
mt{} {}
};
template <typename T>
union MTNumeric {
SafeNumeric<T> mt;
T st;
MTNumeric() :
mt{} {}
};
public:
// N.B. Any enum stored as a bitfield should be specified as UNSIGNED to work around
// some compilers trying to store it as signed, and requiring 1 more bit than necessary.
enum ProcessMode : unsigned int {
PROCESS_MODE_INHERIT, // same as parent node
PROCESS_MODE_PAUSABLE, // process only if not paused
PROCESS_MODE_WHEN_PAUSED, // process only if paused
PROCESS_MODE_ALWAYS, // process always
PROCESS_MODE_DISABLED, // never process
};
enum ProcessThreadGroup {
PROCESS_THREAD_GROUP_INHERIT,
PROCESS_THREAD_GROUP_MAIN_THREAD,
PROCESS_THREAD_GROUP_SUB_THREAD,
};
enum ProcessThreadMessages {
FLAG_PROCESS_THREAD_MESSAGES = 1,
FLAG_PROCESS_THREAD_MESSAGES_PHYSICS = 2,
FLAG_PROCESS_THREAD_MESSAGES_ALL = 3,
};
enum PhysicsInterpolationMode : unsigned int {
PHYSICS_INTERPOLATION_MODE_INHERIT,
PHYSICS_INTERPOLATION_MODE_ON,
PHYSICS_INTERPOLATION_MODE_OFF,
};
enum DuplicateFlags {
DUPLICATE_SIGNALS = 1,
DUPLICATE_GROUPS = 2,
DUPLICATE_SCRIPTS = 4,
DUPLICATE_USE_INSTANTIATION = 8,
#ifdef TOOLS_ENABLED
DUPLICATE_FROM_EDITOR = 16,
#endif
};
enum NameCasing {
NAME_CASING_PASCAL_CASE,
NAME_CASING_CAMEL_CASE,
NAME_CASING_SNAKE_CASE
};
enum InternalMode {
INTERNAL_MODE_DISABLED,
INTERNAL_MODE_FRONT,
INTERNAL_MODE_BACK,
};
enum AutoTranslateMode {
AUTO_TRANSLATE_MODE_INHERIT,
AUTO_TRANSLATE_MODE_ALWAYS,
AUTO_TRANSLATE_MODE_DISABLED,
};
struct Comparator {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
static int orphan_node_count;
void _update_process(bool p_enable, bool p_for_children);
private:
struct GroupData {
bool persistent = false;
SceneTree::Group *group = nullptr;
};
struct ComparatorByIndex {
bool operator()(const Node *p_left, const Node *p_right) const {
static const uint32_t order[3] = { 1, 0, 2 };
uint32_t order_left = order[p_left->data.internal_mode];
uint32_t order_right = order[p_right->data.internal_mode];
if (order_left == order_right) {
return p_left->data.index < p_right->data.index;
}
return order_left < order_right;
}
};
struct ComparatorWithPriority {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
};
struct ComparatorWithPhysicsPriority {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.physics_process_priority == p_a->data.physics_process_priority ? p_b->is_greater_than(p_a) : p_b->data.physics_process_priority > p_a->data.physics_process_priority; }
};
// This Data struct is to avoid namespace pollution in derived classes.
struct Data {
String scene_file_path;
Ref<SceneState> instance_state;
Ref<SceneState> inherited_state;
Node *parent = nullptr;
Node *owner = nullptr;
HashMap<StringName, Node *> children;
mutable bool children_cache_dirty = true;
mutable LocalVector<Node *> children_cache;
HashMap<StringName, Node *> owned_unique_nodes;
bool unique_name_in_owner = false;
InternalMode internal_mode = INTERNAL_MODE_DISABLED;
mutable int internal_children_front_count_cache = 0;
mutable int internal_children_back_count_cache = 0;
mutable int external_children_count_cache = 0;
mutable int index = -1; // relative to front, normal or back.
int depth = -1;
int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
StringName name;
SceneTree *tree = nullptr;
#ifdef TOOLS_ENABLED
NodePath import_path; // Path used when imported, used by scene editors to keep tracking.
#endif
String editor_description;
Viewport *viewport = nullptr;
HashMap<StringName, GroupData> grouped;
List<Node *>::Element *OW = nullptr; // Owned element.
List<Node *> owned;
Node *process_owner = nullptr;
ProcessThreadGroup process_thread_group = PROCESS_THREAD_GROUP_INHERIT;
Node *process_thread_group_owner = nullptr;
int process_thread_group_order = 0;
BitField<ProcessThreadMessages> process_thread_messages;
void *process_group = nullptr; // to avoid cyclic dependency
int multiplayer_authority = 1; // Server by default.
Variant rpc_config;
// Variables used to properly sort the node when processing, ignored otherwise.
int process_priority = 0;
int physics_process_priority = 0;
// Keep bitpacked values together to get better packing.
ProcessMode process_mode : 3;
PhysicsInterpolationMode physics_interpolation_mode : 2;
bool physics_process : 1;
bool process : 1;
bool physics_process_internal : 1;
bool process_internal : 1;
bool input : 1;
bool shortcut_input : 1;
bool unhandled_input : 1;
bool unhandled_key_input : 1;
// Physics interpolation can be turned on and off on a per node basis.
// This only takes effect when the SceneTree (or project setting) physics interpolation
// is switched on.
bool physics_interpolated : 1;
bool parent_owned : 1;
bool in_constructor : 1;
bool use_placeholder : 1;
bool display_folded : 1;
bool editable_instance : 1;
bool inside_tree : 1;
bool ready_notified : 1;
bool ready_first : 1;
AutoTranslateMode auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
mutable bool is_auto_translating = true;
mutable bool is_auto_translate_dirty = true;
mutable NodePath *path_cache = nullptr;
} data;
Ref<MultiplayerAPI> multiplayer;
String _get_tree_string_pretty(const String &p_prefix, bool p_last);
String _get_tree_string(const Node *p_node);
Node *_get_child_by_name(const StringName &p_name) const;
void _replace_connections_target(Node *p_new_target);
void _validate_child_name(Node *p_child, bool p_force_human_readable = false);
void _generate_serial_child_name(const Node *p_child, StringName &name) const;
void _propagate_reverse_notification(int p_notification);
void _propagate_deferred_notification(int p_notification, bool p_reverse);
void _propagate_enter_tree();
void _propagate_ready();
void _propagate_exit_tree();
void _propagate_after_exit_tree();
void _propagate_physics_interpolated(bool p_interpolated);
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
void _propagate_groups_dirty();
Array _get_node_and_resource(const NodePath &p_path);
void _duplicate_properties(const Node *p_root, const Node *p_original, Node *p_copy, int p_flags) const;
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const;
TypedArray<StringName> _get_groups() const;
Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
friend class SceneTree;
void _set_tree(SceneTree *p_tree);
void _propagate_pause_notification(bool p_enable);
_FORCE_INLINE_ bool _can_process(bool p_paused) const;
_FORCE_INLINE_ bool _is_enabled() const;
void _release_unique_name_in_owner();
void _acquire_unique_name_in_owner();
void _clean_up_owner();
_FORCE_INLINE_ void _update_children_cache() const {
if (unlikely(data.children_cache_dirty)) {
_update_children_cache_impl();
}
}
void _update_children_cache_impl() const;
// Process group management
void _add_process_group();
void _remove_process_group();
void _add_to_process_thread_group();
void _remove_from_process_thread_group();
void _remove_tree_from_process_thread_group();
void _add_tree_to_process_thread_group(Node *p_owner);
static thread_local Node *current_process_thread_group;
Variant _call_deferred_thread_group_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
void _notification(int p_notification);
virtual void _physics_interpolated_changed();
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
virtual void move_child_notify(Node *p_child);
virtual void owner_changed_notify();
void _propagate_replace_owner(Node *p_owner, Node *p_by_owner);
static void _bind_methods();
static String _get_name_num_separator();
friend class SceneState;
void _add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode = INTERNAL_MODE_DISABLED);
void _set_owner_nocheck(Node *p_owner);
void _set_name_nocheck(const StringName &p_name);
//call from SceneTree
void _call_input(const Ref<InputEvent> &p_event);
void _call_shortcut_input(const Ref<InputEvent> &p_event);
void _call_unhandled_input(const Ref<InputEvent> &p_event);
void _call_unhandled_key_input(const Ref<InputEvent> &p_event);
void _validate_property(PropertyInfo &p_property) const;
protected:
virtual void input(const Ref<InputEvent> &p_event);
virtual void shortcut_input(const Ref<InputEvent> &p_key_event);
virtual void unhandled_input(const Ref<InputEvent> &p_event);
virtual void unhandled_key_input(const Ref<InputEvent> &p_key_event);
GDVIRTUAL1(_process, double)
GDVIRTUAL1(_physics_process, double)
GDVIRTUAL0(_enter_tree)
GDVIRTUAL0(_exit_tree)
GDVIRTUAL0(_ready)
GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings)
GDVIRTUAL1(_input, Ref<InputEvent>)
GDVIRTUAL1(_shortcut_input, Ref<InputEvent>)
GDVIRTUAL1(_unhandled_input, Ref<InputEvent>)
GDVIRTUAL1(_unhandled_key_input, Ref<InputEvent>)
public:
enum {
// You can make your own, but don't use the same numbers as other notifications in other nodes.
NOTIFICATION_ENTER_TREE = 10,
NOTIFICATION_EXIT_TREE = 11,
NOTIFICATION_MOVED_IN_PARENT = 12,
NOTIFICATION_READY = 13,
NOTIFICATION_PAUSED = 14,
NOTIFICATION_UNPAUSED = 15,
NOTIFICATION_PHYSICS_PROCESS = 16,
NOTIFICATION_PROCESS = 17,
NOTIFICATION_PARENTED = 18,
NOTIFICATION_UNPARENTED = 19,
NOTIFICATION_SCENE_INSTANTIATED = 20,
NOTIFICATION_DRAG_BEGIN = 21,
NOTIFICATION_DRAG_END = 22,
NOTIFICATION_PATH_RENAMED = 23,
NOTIFICATION_CHILD_ORDER_CHANGED = 24,
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
NOTIFICATION_POST_ENTER_TREE = 27,
NOTIFICATION_DISABLED = 28,
NOTIFICATION_ENABLED = 29,
NOTIFICATION_RESET_PHYSICS_INTERPOLATION = 2001, // A GodotSpace Odyssey.
// Keep these linked to Node.
NOTIFICATION_WM_MOUSE_ENTER = 1002,
NOTIFICATION_WM_MOUSE_EXIT = 1003,
NOTIFICATION_WM_WINDOW_FOCUS_IN = 1004,
NOTIFICATION_WM_WINDOW_FOCUS_OUT = 1005,
NOTIFICATION_WM_CLOSE_REQUEST = 1006,
NOTIFICATION_WM_GO_BACK_REQUEST = 1007,
NOTIFICATION_WM_SIZE_CHANGED = 1008,
NOTIFICATION_WM_DPI_CHANGE = 1009,
NOTIFICATION_VP_MOUSE_ENTER = 1010,
NOTIFICATION_VP_MOUSE_EXIT = 1011,
NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING,
NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED,
NOTIFICATION_WM_ABOUT = MainLoop::NOTIFICATION_WM_ABOUT,
NOTIFICATION_CRASH = MainLoop::NOTIFICATION_CRASH,
NOTIFICATION_OS_IME_UPDATE = MainLoop::NOTIFICATION_OS_IME_UPDATE,
NOTIFICATION_APPLICATION_RESUMED = MainLoop::NOTIFICATION_APPLICATION_RESUMED,
NOTIFICATION_APPLICATION_PAUSED = MainLoop::NOTIFICATION_APPLICATION_PAUSED,
NOTIFICATION_APPLICATION_FOCUS_IN = MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN,
NOTIFICATION_APPLICATION_FOCUS_OUT = MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT,
NOTIFICATION_TEXT_SERVER_CHANGED = MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED,
// Editor specific node notifications
NOTIFICATION_EDITOR_PRE_SAVE = 9001,
NOTIFICATION_EDITOR_POST_SAVE = 9002,
};
/* NODE/TREE */
StringName get_name() const;
String get_description() const;
void set_name(const String &p_name);
InternalMode get_internal_mode() const;
void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
void remove_child(Node *p_child);
int get_child_count(bool p_include_internal = true) const;
Node *get_child(int p_index, bool p_include_internal = true) const;
TypedArray<Node> get_children(bool p_include_internal = true) const;
bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;
Node *find_child(const String &p_pattern, bool p_recursive = true, bool p_owned = true) const;
TypedArray<Node> find_children(const String &p_pattern, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const;
bool has_node_and_resource(const NodePath &p_path) const;
Node *get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const;
virtual void reparent(Node *p_parent, bool p_keep_global_transform = true);
Node *get_parent() const;
Node *find_parent(const String &p_pattern) const;
Window *get_window() const;
Window *get_last_exclusive_window() const;
_FORCE_INLINE_ SceneTree *get_tree() const {
ERR_FAIL_NULL_V(data.tree, nullptr);
return data.tree;
}
_FORCE_INLINE_ bool is_inside_tree() const { return data.inside_tree; }
bool is_ancestor_of(const Node *p_node) const;
bool is_greater_than(const Node *p_node) const;
NodePath get_path() const;
NodePath get_path_to(const Node *p_node, bool p_use_unique_path = false) const;
Node *find_common_parent_with(const Node *p_node) const;
void add_to_group(const StringName &p_identifier, bool p_persistent = false);
void remove_from_group(const StringName &p_identifier);
bool is_in_group(const StringName &p_identifier) const;
struct GroupInfo {
StringName name;
bool persistent = false;
};
void get_groups(List<GroupInfo> *p_groups) const;
int get_persistent_group_count() const;
void move_child(Node *p_child, int p_index);
void _move_child(Node *p_child, int p_index, bool p_ignore_end = false);
void set_owner(Node *p_owner);
Node *get_owner() const;
void get_owned_by(Node *p_by, List<Node *> *p_owned);
void set_unique_name_in_owner(bool p_enabled);
bool is_unique_name_in_owner() const;
_FORCE_INLINE_ int get_index(bool p_include_internal = true) const {
// p_include_internal = false doesn't make sense if the node is internal.
ERR_FAIL_COND_V_MSG(!p_include_internal && data.internal_mode != INTERNAL_MODE_DISABLED, -1, "Node is internal. Can't get index with 'include_internal' being false.");
if (!data.parent) {
return data.index;
}
data.parent->_update_children_cache();
if (!p_include_internal) {
return data.index;
} else {
switch (data.internal_mode) {
case INTERNAL_MODE_DISABLED: {
return data.parent->data.internal_children_front_count_cache + data.index;
} break;
case INTERNAL_MODE_FRONT: {
return data.index;
} break;
case INTERNAL_MODE_BACK: {
return data.parent->data.internal_children_front_count_cache + data.parent->data.external_children_count_cache + data.index;
} break;
}
return -1;
}
}
Ref<Tween> create_tween();
void print_tree();
void print_tree_pretty();
String get_tree_string();
String get_tree_string_pretty();
void set_scene_file_path(const String &p_scene_file_path);
String get_scene_file_path() const;
void set_editor_description(const String &p_editor_description);
String get_editor_description() const;
void set_editable_instance(Node *p_node, bool p_editable);
bool is_editable_instance(const Node *p_node) const;
Node *get_deepest_editable_node(Node *p_start_node) const;
#ifdef TOOLS_ENABLED
void set_property_pinned(const String &p_property, bool p_pinned);
bool is_property_pinned(const StringName &p_property) const;
virtual StringName get_property_store_alias(const StringName &p_property) const;
bool is_part_of_edited_scene() const;
#else
bool is_part_of_edited_scene() const { return false; }
#endif
void get_storable_properties(HashSet<StringName> &r_storable_properties) const;
virtual String to_string() override;
/* NOTIFICATIONS */
void propagate_notification(int p_notification);
void propagate_call(const StringName &p_method, const Array &p_args = Array(), const bool p_parent_first = false);
/* PROCESSING */
void set_physics_process(bool p_process);
double get_physics_process_delta_time() const;
bool is_physics_processing() const;
void set_process(bool p_process);
double get_process_delta_time() const;
bool is_processing() const;
void set_physics_process_internal(bool p_process_internal);
bool is_physics_processing_internal() const;
void set_process_internal(bool p_process_internal);
bool is_processing_internal() const;
void set_process_priority(int p_priority);
int get_process_priority() const;
void set_process_thread_group_order(int p_order);
int get_process_thread_group_order() const;
void set_physics_process_priority(int p_priority);
int get_physics_process_priority() const;
void set_process_input(bool p_enable);
bool is_processing_input() const;
void set_process_shortcut_input(bool p_enable);
bool is_processing_shortcut_input() const;
void set_process_unhandled_input(bool p_enable);
bool is_processing_unhandled_input() const;
void set_process_unhandled_key_input(bool p_enable);
bool is_processing_unhandled_key_input() const;
_FORCE_INLINE_ bool _is_any_processing() const {
return data.process || data.process_internal || data.physics_process || data.physics_process_internal;
}
_FORCE_INLINE_ bool is_accessible_from_caller_thread() const {
if (current_process_thread_group == nullptr) {
// No thread processing.
// Only accessible if node is outside the scene tree
// or access will happen from a node-safe thread.
return !data.inside_tree || is_current_thread_safe_for_nodes();
} else {
// Thread processing.
return current_process_thread_group == data.process_thread_group_owner;
}
}
_FORCE_INLINE_ bool is_readable_from_caller_thread() const {
if (current_process_thread_group == nullptr) {
// No thread processing.
// Only accessible if node is outside the scene tree
// or access will happen from a node-safe thread.
return is_current_thread_safe_for_nodes() || unlikely(!data.inside_tree);
} else {
// Thread processing.
return true;
}
}
_FORCE_INLINE_ static bool is_group_processing() { return current_process_thread_group; }
void set_process_thread_messages(BitField<ProcessThreadMessages> p_flags);
BitField<ProcessThreadMessages> get_process_thread_messages() const;
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;
#ifdef TOOLS_ENABLED
Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const;
Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
void remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
void remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
#endif
// used by editors, to save what has changed only
void set_scene_instance_state(const Ref<SceneState> &p_state);
Ref<SceneState> get_scene_instance_state() const;
void set_scene_inherited_state(const Ref<SceneState> &p_state);
Ref<SceneState> get_scene_inherited_state() const;
void set_scene_instance_load_placeholder(bool p_enable);
bool get_scene_instance_load_placeholder() const;
template <typename... VarArgs>
Vector<Variant> make_binds(VarArgs... p_args) {
Vector<Variant> binds = { p_args... };
return binds;
}
void replace_by(Node *p_node, bool p_keep_data = false);
void set_process_mode(ProcessMode p_mode);
ProcessMode get_process_mode() const;
bool can_process() const;
bool can_process_notification(int p_what) const;
void set_physics_interpolation_mode(PhysicsInterpolationMode p_mode);
PhysicsInterpolationMode get_physics_interpolation_mode() const { return data.physics_interpolation_mode; }
_FORCE_INLINE_ bool is_physics_interpolated() const { return data.physics_interpolated; }
_FORCE_INLINE_ bool is_physics_interpolated_and_enabled() const { return is_inside_tree() && get_tree()->is_physics_interpolation_enabled() && is_physics_interpolated(); }
void reset_physics_interpolation();
bool is_enabled() const;
bool is_ready() const;
void request_ready();
void set_process_thread_group(ProcessThreadGroup p_mode);
ProcessThreadGroup get_process_thread_group() const;
static void print_orphan_nodes();
#ifdef TOOLS_ENABLED
String validate_child_name(Node *p_child);
String prevalidate_child_name(Node *p_child, StringName p_name);
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
static String adjust_name_casing(const String &p_name);
void queue_free();
//hacks for speed
static void init_node_hrcr();
void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes
void set_import_path(const NodePath &p_import_path); //path used when imported, used by scene editors to keep tracking
NodePath get_import_path() const;
bool is_owned_by_parent() const;
void clear_internal_tree_resource_paths();
_FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; }
virtual PackedStringArray get_configuration_warnings() const;
void update_configuration_warnings();
void set_display_folded(bool p_folded);
bool is_displayed_folded() const;
/* NETWORK */
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
const Variant get_node_rpc_config() const;
template <typename... VarArgs>
Error rpc(const StringName &p_method, VarArgs... p_args);
template <typename... VarArgs>
Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args);
Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
/* INTERNATIONALIZATION */
void set_auto_translate_mode(AutoTranslateMode p_mode);
AutoTranslateMode get_auto_translate_mode() const;
bool can_auto_translate() const;
_FORCE_INLINE_ String atr(const String p_message, const StringName p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; }
_FORCE_INLINE_ String atr_n(const String p_message, const StringName &p_message_plural, int p_n, const StringName p_context = "") const { return can_auto_translate() ? tr_n(p_message, p_message_plural, p_n, p_context) : p_message; }
/* THREADING */
void call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
template <typename... VarArgs>
void call_deferred_thread_group(const StringName &p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
void set_deferred_thread_group(const StringName &p_property, const Variant &p_value);
void notify_deferred_thread_group(int p_notification);
void call_thread_safep(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
template <typename... VarArgs>
void call_thread_safe(const StringName &p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
void set_thread_safe(const StringName &p_property, const Variant &p_value);
void notify_thread_safe(int p_notification);
// These inherited functions need proper multithread locking when overridden in Node.
#ifdef DEBUG_ENABLED
virtual void set_script(const Variant &p_script) override;
virtual Variant get_script() const override;
virtual bool has_meta(const StringName &p_name) const override;
virtual void set_meta(const StringName &p_name, const Variant &p_value) override;
virtual void remove_meta(const StringName &p_name) override;
virtual Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const override;
virtual void get_meta_list(List<StringName> *p_list) const override;
virtual Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) override;
virtual bool has_signal(const StringName &p_name) const override;
virtual void get_signal_list(List<MethodInfo> *p_signals) const override;
virtual void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const override;
virtual void get_all_signal_connections(List<Connection> *p_connections) const override;
virtual int get_persistent_signal_connection_count() const override;
virtual void get_signals_connected_to_this(List<Connection> *p_connections) const override;
virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override;
virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override;
virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override;
#endif
Node();
~Node();
};
VARIANT_ENUM_CAST(Node::DuplicateFlags);
VARIANT_ENUM_CAST(Node::ProcessMode);
VARIANT_ENUM_CAST(Node::ProcessThreadGroup);
VARIANT_BITFIELD_CAST(Node::ProcessThreadMessages);
VARIANT_ENUM_CAST(Node::InternalMode);
VARIANT_ENUM_CAST(Node::PhysicsInterpolationMode);
VARIANT_ENUM_CAST(Node::AutoTranslateMode);
typedef HashSet<Node *, Node::Comparator> NodeSet;
// Template definitions must be in the header so they are always fully initialized before their usage.
// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
template <typename... VarArgs>
Error Node::rpc(const StringName &p_method, VarArgs... p_args) {
return rpc_id(0, p_method, p_args...);
}
template <typename... VarArgs>
Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
#ifdef DEBUG_ENABLED
#define ERR_THREAD_GUARD ERR_FAIL_COND_MSG(!is_accessible_from_caller_thread(), vformat("Caller thread can't call this function in this node (%s). Use call_deferred() or call_thread_group() instead.", get_description()));
#define ERR_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_accessible_from_caller_thread(), (m_ret), vformat("Caller thread can't call this function in this node (%s). Use call_deferred() or call_thread_group() instead.", get_description()));
#define ERR_MAIN_THREAD_GUARD ERR_FAIL_COND_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), vformat("This function in this node (%s) can only be accessed from the main thread. Use call_deferred() instead.", get_description()));
#define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), (m_ret), vformat("This function in this node (%s) can only be accessed from the main thread. Use call_deferred() instead.", get_description()));
#define ERR_READ_THREAD_GUARD ERR_FAIL_COND_MSG(!is_readable_from_caller_thread(), vformat("This function in this node (%s) can only be accessed from either the main thread or a thread group. Use call_deferred() instead.", get_description()));
#define ERR_READ_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_readable_from_caller_thread(), (m_ret), vformat("This function in this node (%s) can only be accessed from either the main thread or a thread group. Use call_deferred() instead.", get_description()));
#else
#define ERR_THREAD_GUARD
#define ERR_THREAD_GUARD_V(m_ret)
#define ERR_MAIN_THREAD_GUARD
#define ERR_MAIN_THREAD_GUARD_V(m_ret)
#define ERR_READ_THREAD_GUARD
#define ERR_READ_THREAD_GUARD_V(m_ret)
#endif
// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector.
#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration."));
#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions."));
#endif // NODE_H

View file

@ -0,0 +1,154 @@
/**************************************************************************/
/* resource_preloader.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "resource_preloader.h"
#include "core/templates/rb_set.h"
void ResourcePreloader::_set_resources(const Array &p_data) {
resources.clear();
ERR_FAIL_COND(p_data.size() != 2);
Vector<String> names = p_data[0];
Array resdata = p_data[1];
ERR_FAIL_COND(names.size() != resdata.size());
for (int i = 0; i < resdata.size(); i++) {
Ref<Resource> resource = resdata[i];
ERR_CONTINUE(!resource.is_valid());
resources[names[i]] = resource;
//add_resource(names[i],resource);
}
}
Array ResourcePreloader::_get_resources() const {
Vector<String> names;
Array arr;
arr.resize(resources.size());
names.resize(resources.size());
RBSet<String> sorted_names;
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
sorted_names.insert(E.key);
}
int i = 0;
for (const String &E : sorted_names) {
names.set(i, E);
arr[i] = resources[E];
i++;
}
Array res;
res.push_back(names);
res.push_back(arr);
return res;
}
void ResourcePreloader::add_resource(const StringName &p_name, const Ref<Resource> &p_resource) {
ERR_FAIL_COND(p_resource.is_null());
if (resources.has(p_name)) {
StringName new_name;
int idx = 2;
while (true) {
new_name = p_name.operator String() + " " + itos(idx);
if (resources.has(new_name)) {
idx++;
continue;
}
break;
}
add_resource(new_name, p_resource);
} else {
resources[p_name] = p_resource;
}
}
void ResourcePreloader::remove_resource(const StringName &p_name) {
ERR_FAIL_COND(!resources.has(p_name));
resources.erase(p_name);
}
void ResourcePreloader::rename_resource(const StringName &p_from_name, const StringName &p_to_name) {
ERR_FAIL_COND(!resources.has(p_from_name));
Ref<Resource> res = resources[p_from_name];
resources.erase(p_from_name);
add_resource(p_to_name, res);
}
bool ResourcePreloader::has_resource(const StringName &p_name) const {
return resources.has(p_name);
}
Ref<Resource> ResourcePreloader::get_resource(const StringName &p_name) const {
ERR_FAIL_COND_V(!resources.has(p_name), Ref<Resource>());
return resources[p_name];
}
Vector<String> ResourcePreloader::_get_resource_list() const {
Vector<String> res;
res.resize(resources.size());
int i = 0;
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
res.set(i, E.key);
i++;
}
return res;
}
void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
p_list->push_back(E.key);
}
}
void ResourcePreloader::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_resources", "resources"), &ResourcePreloader::_set_resources);
ClassDB::bind_method(D_METHOD("_get_resources"), &ResourcePreloader::_get_resources);
ClassDB::bind_method(D_METHOD("add_resource", "name", "resource"), &ResourcePreloader::add_resource);
ClassDB::bind_method(D_METHOD("remove_resource", "name"), &ResourcePreloader::remove_resource);
ClassDB::bind_method(D_METHOD("rename_resource", "name", "newname"), &ResourcePreloader::rename_resource);
ClassDB::bind_method(D_METHOD("has_resource", "name"), &ResourcePreloader::has_resource);
ClassDB::bind_method(D_METHOD("get_resource", "name"), &ResourcePreloader::get_resource);
ClassDB::bind_method(D_METHOD("get_resource_list"), &ResourcePreloader::_get_resource_list);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources");
}
ResourcePreloader::ResourcePreloader() {
}

View file

@ -0,0 +1,60 @@
/**************************************************************************/
/* resource_preloader.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 RESOURCE_PRELOADER_H
#define RESOURCE_PRELOADER_H
#include "scene/main/node.h"
class ResourcePreloader : public Node {
GDCLASS(ResourcePreloader, Node);
HashMap<StringName, Ref<Resource>> resources;
void _set_resources(const Array &p_data);
Array _get_resources() const;
Vector<String> _get_resource_list() const;
protected:
static void _bind_methods();
public:
void add_resource(const StringName &p_name, const Ref<Resource> &p_resource);
void remove_resource(const StringName &p_name);
void rename_resource(const StringName &p_from_name, const StringName &p_to_name);
bool has_resource(const StringName &p_name) const;
Ref<Resource> get_resource(const StringName &p_name) const;
void get_resource_list(List<StringName> *p_list);
ResourcePreloader();
};
#endif // RESOURCE_PRELOADER_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,432 @@
/**************************************************************************/
/* scene_tree.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 SCENE_TREE_H
#define SCENE_TREE_H
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/paged_allocator.h"
#include "core/templates/self_list.h"
#include "scene/resources/mesh.h"
#undef Window
class PackedScene;
class Node;
class Window;
class Material;
class Mesh;
class MultiplayerAPI;
class SceneDebugger;
class Tween;
class Viewport;
class SceneTreeTimer : public RefCounted {
GDCLASS(SceneTreeTimer, RefCounted);
double time_left = 0.0;
bool process_always = true;
bool process_in_physics = false;
bool ignore_time_scale = false;
protected:
static void _bind_methods();
public:
void set_time_left(double p_time);
double get_time_left() const;
void set_process_always(bool p_process_always);
bool is_process_always();
void set_process_in_physics(bool p_process_in_physics);
bool is_process_in_physics();
void set_ignore_time_scale(bool p_ignore);
bool is_ignore_time_scale();
void release_connections();
SceneTreeTimer();
};
class SceneTree : public MainLoop {
_THREAD_SAFE_CLASS_
GDCLASS(SceneTree, MainLoop);
public:
typedef void (*IdleCallback)();
private:
CallQueue::Allocator *process_group_call_queue_allocator = nullptr;
struct ProcessGroup {
CallQueue call_queue;
Vector<Node *> nodes;
Vector<Node *> physics_nodes;
bool node_order_dirty = true;
bool physics_node_order_dirty = true;
bool removed = false;
Node *owner = nullptr;
uint64_t last_pass = 0;
};
struct ProcessGroupSort {
_FORCE_INLINE_ bool operator()(const ProcessGroup *p_left, const ProcessGroup *p_right) const;
};
PagedAllocator<ProcessGroup, true> group_allocator; // Allocate groups on pages, to enhance cache usage.
LocalVector<ProcessGroup *> process_groups;
bool process_groups_dirty = true;
LocalVector<ProcessGroup *> local_process_group_cache; // Used when processing to group what needs to
uint64_t process_last_pass = 1;
ProcessGroup default_process_group;
bool node_threading_disabled = false;
struct Group {
Vector<Node *> nodes;
bool changed = false;
};
Window *root = nullptr;
double physics_process_time = 0.0;
double process_time = 0.0;
bool accept_quit = true;
bool quit_on_go_back = true;
#ifdef DEBUG_ENABLED
bool debug_collisions_hint = false;
bool debug_paths_hint = false;
bool debug_navigation_hint = false;
#endif
bool paused = false;
HashMap<StringName, Group> group_map;
bool _quit = false;
bool _physics_interpolation_enabled = false;
StringName tree_changed_name = "tree_changed";
StringName node_added_name = "node_added";
StringName node_removed_name = "node_removed";
StringName node_renamed_name = "node_renamed";
int64_t current_frame = 0;
int nodes_in_tree_count = 0;
#ifdef TOOLS_ENABLED
Node *edited_scene_root = nullptr;
#endif
struct UGCall {
StringName group;
StringName call;
static uint32_t hash(const UGCall &p_val) {
return p_val.group.hash() ^ p_val.call.hash();
}
bool operator==(const UGCall &p_with) const { return group == p_with.group && call == p_with.call; }
bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; }
};
// Safety for when a node is deleted while a group is being called.
int nodes_removed_on_group_call_lock = 0;
HashSet<Node *> nodes_removed_on_group_call; // Skip erased nodes.
List<ObjectID> delete_queue;
HashMap<UGCall, Vector<Variant>, UGCall> unique_group_calls;
bool ugc_locked = false;
void _flush_ugc();
_FORCE_INLINE_ void _update_group_order(Group &g);
TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
Node *current_scene = nullptr;
Node *prev_scene = nullptr;
Node *pending_new_scene = nullptr;
Color debug_collisions_color;
Color debug_collision_contact_color;
Color debug_paths_color;
float debug_paths_width = 1.0f;
Ref<ArrayMesh> debug_contact_mesh;
Ref<Material> debug_paths_material;
Ref<Material> collision_material;
int collision_debug_contacts;
void _flush_scene_change();
List<Ref<SceneTreeTimer>> timers;
List<Ref<Tween>> tweens;
///network///
Ref<MultiplayerAPI> multiplayer;
HashMap<NodePath, Ref<MultiplayerAPI>> custom_multiplayers;
bool multiplayer_poll = true;
static SceneTree *singleton;
friend class Node;
void tree_changed();
void node_added(Node *p_node);
void node_removed(Node *p_node);
void node_renamed(Node *p_node);
void process_timers(double p_delta, bool p_physics_frame);
void process_tweens(double p_delta, bool p_physics_frame);
Group *add_to_group(const StringName &p_group, Node *p_node);
void remove_from_group(const StringName &p_group, Node *p_node);
void make_group_changed(const StringName &p_group);
void _process_group(ProcessGroup *p_group, bool p_physics);
void _process_groups_thread(uint32_t p_index, bool p_physics);
void _process(bool p_physics);
void _remove_process_group(Node *p_node);
void _add_process_group(Node *p_node);
void _remove_node_from_process_group(Node *p_node, Node *p_owner);
void _add_node_to_process_group(Node *p_node, Node *p_owner);
void _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _flush_delete_queue();
// Optimization.
friend class CanvasItem;
friend class Node3D;
friend class Viewport;
SelfList<Node>::List xform_change_list;
#ifdef DEBUG_ENABLED // No live editor in release build.
friend class LiveEditor;
#endif
enum {
MAX_IDLE_CALLBACKS = 256
};
static IdleCallback idle_callbacks[MAX_IDLE_CALLBACKS];
static int idle_callback_count;
void _call_idle_callbacks();
void _main_window_focus_in();
void _main_window_close();
void _main_window_go_back();
enum CallInputType {
CALL_INPUT_TYPE_INPUT,
CALL_INPUT_TYPE_SHORTCUT_INPUT,
CALL_INPUT_TYPE_UNHANDLED_INPUT,
CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT,
};
//used by viewport
void _call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport);
protected:
void _notification(int p_notification);
static void _bind_methods();
public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = 2000
};
enum GroupCallFlags {
GROUP_CALL_DEFAULT = 0,
GROUP_CALL_REVERSE = 1,
GROUP_CALL_DEFERRED = 2,
GROUP_CALL_UNIQUE = 4,
};
_FORCE_INLINE_ Window *get_root() const { return root; }
void call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount);
void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification);
void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value);
// `notify_group()` is immediate by default since Godot 4.0.
void notify_group(const StringName &p_group, int p_notification);
// `set_group()` is immediate by default since Godot 4.0.
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
template <typename... VarArgs>
// `call_group()` is immediate by default since Godot 4.0.
void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <typename... VarArgs>
void call_group_flags(uint32_t p_flags, const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
call_group_flagsp(p_flags, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
void flush_transform_notifications();
virtual void initialize() override;
virtual void iteration_prepare() override;
virtual bool physics_process(double p_time) override;
virtual bool process(double p_time) override;
virtual void finalize() override;
bool is_auto_accept_quit() const;
void set_auto_accept_quit(bool p_enable);
bool is_quit_on_go_back() const;
void set_quit_on_go_back(bool p_enable);
void quit(int p_exit_code = EXIT_SUCCESS);
_FORCE_INLINE_ double get_physics_process_time() const { return physics_process_time; }
_FORCE_INLINE_ double get_process_time() const { return process_time; }
void set_pause(bool p_enabled);
bool is_paused() const;
#ifdef DEBUG_ENABLED
void set_debug_collisions_hint(bool p_enabled);
bool is_debugging_collisions_hint() const;
void set_debug_paths_hint(bool p_enabled);
bool is_debugging_paths_hint() const;
void set_debug_navigation_hint(bool p_enabled);
bool is_debugging_navigation_hint() const;
#else
void set_debug_collisions_hint(bool p_enabled) {}
bool is_debugging_collisions_hint() const { return false; }
void set_debug_paths_hint(bool p_enabled) {}
bool is_debugging_paths_hint() const { return false; }
void set_debug_navigation_hint(bool p_enabled) {}
bool is_debugging_navigation_hint() const { return false; }
#endif
void set_debug_collisions_color(const Color &p_color);
Color get_debug_collisions_color() const;
void set_debug_collision_contact_color(const Color &p_color);
Color get_debug_collision_contact_color() const;
void set_debug_paths_color(const Color &p_color);
Color get_debug_paths_color() const;
void set_debug_paths_width(float p_width);
float get_debug_paths_width() const;
Ref<Material> get_debug_paths_material();
Ref<Material> get_debug_collision_material();
Ref<ArrayMesh> get_debug_contact_mesh();
int get_collision_debug_contact_count() { return collision_debug_contacts; }
int64_t get_frame() const;
int get_node_count() const;
void queue_delete(Object *p_object);
void get_nodes_in_group(const StringName &p_group, List<Node *> *p_list);
Node *get_first_node_in_group(const StringName &p_group);
bool has_group(const StringName &p_identifier) const;
int get_node_count_in_group(const StringName &p_group) const;
//void change_scene(const String& p_path);
//Node *get_loaded_scene();
void set_edited_scene_root(Node *p_node);
Node *get_edited_scene_root() const;
void set_current_scene(Node *p_scene);
Node *get_current_scene() const;
Error change_scene_to_file(const String &p_path);
Error change_scene_to_packed(const Ref<PackedScene> &p_scene);
Error reload_current_scene();
void unload_current_scene();
Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false);
Ref<Tween> create_tween();
TypedArray<Tween> get_processed_tweens();
//used by Main::start, don't use otherwise
void add_current_scene(Node *p_current);
static SceneTree *get_singleton() { return singleton; }
#ifdef TOOLS_ENABLED
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
//network API
Ref<MultiplayerAPI> get_multiplayer(const NodePath &p_for_path = NodePath()) const;
void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path = NodePath());
void set_multiplayer_poll_enabled(bool p_enabled);
bool is_multiplayer_poll_enabled() const;
static void add_idle_callback(IdleCallback p_callback);
void set_disable_node_threading(bool p_disable);
//default texture settings
void set_physics_interpolation_enabled(bool p_enabled);
bool is_physics_interpolation_enabled() const;
SceneTree();
~SceneTree();
};
VARIANT_ENUM_CAST(SceneTree::GroupCallFlags);
#endif // SCENE_TREE_H

View file

@ -0,0 +1,287 @@
/**************************************************************************/
/* shader_globals_override.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "shader_globals_override.h"
#include "scene/main/node.h"
StringName *ShaderGlobalsOverride::_remap(const StringName &p_name) const {
StringName *r = param_remaps.getptr(p_name);
if (!r) {
//not cached, do caching
String p = p_name;
if (p.begins_with("params/")) {
String q = p.replace_first("params/", "");
param_remaps[p] = q;
r = param_remaps.getptr(p);
}
}
return r;
}
bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_value) {
StringName *r = _remap(p_name);
if (r) {
Override *o = overrides.getptr(*r);
if (!o) {
Override ov;
ov.in_use = false;
overrides[*r] = ov;
o = overrides.getptr(*r);
}
if (o) {
o->override = p_value;
if (active) {
if (o->override.get_type() == Variant::OBJECT) {
RID tex_rid = p_value;
RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid);
} else {
RS::get_singleton()->global_shader_parameter_set_override(*r, p_value);
}
}
o->in_use = p_value.get_type() != Variant::NIL;
return true;
}
}
return false;
}
bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const {
StringName *r = _remap(p_name);
if (r) {
const Override *o = overrides.getptr(*r);
if (o) {
r_ret = o->override;
return true;
}
}
return false;
}
void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const {
Vector<StringName> variables;
variables = RS::get_singleton()->global_shader_parameter_get_list();
for (int i = 0; i < variables.size(); i++) {
PropertyInfo pinfo;
pinfo.name = "params/" + variables[i];
pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) {
case RS::GLOBAL_VAR_TYPE_BOOL: {
pinfo.type = Variant::BOOL;
} break;
case RS::GLOBAL_VAR_TYPE_BVEC2: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y";
} break;
case RS::GLOBAL_VAR_TYPE_BVEC3: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y,z";
} break;
case RS::GLOBAL_VAR_TYPE_BVEC4: {
pinfo.type = Variant::INT;
pinfo.hint = PROPERTY_HINT_FLAGS;
pinfo.hint_string = "x,y,z,w";
} break;
case RS::GLOBAL_VAR_TYPE_INT: {
pinfo.type = Variant::INT;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC2: {
pinfo.type = Variant::VECTOR2I;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC3: {
pinfo.type = Variant::VECTOR3I;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC4: {
pinfo.type = Variant::VECTOR4I;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2I: {
pinfo.type = Variant::RECT2I;
} break;
case RS::GLOBAL_VAR_TYPE_UINT: {
pinfo.type = Variant::INT;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC2: {
pinfo.type = Variant::VECTOR2I;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC3: {
pinfo.type = Variant::VECTOR3I;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC4: {
pinfo.type = Variant::VECTOR4I;
} break;
case RS::GLOBAL_VAR_TYPE_FLOAT: {
pinfo.type = Variant::FLOAT;
} break;
case RS::GLOBAL_VAR_TYPE_VEC2: {
pinfo.type = Variant::VECTOR2;
} break;
case RS::GLOBAL_VAR_TYPE_VEC3: {
pinfo.type = Variant::VECTOR3;
} break;
case RS::GLOBAL_VAR_TYPE_VEC4: {
pinfo.type = Variant::VECTOR4;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2: {
pinfo.type = Variant::RECT2;
} break;
case RS::GLOBAL_VAR_TYPE_COLOR: {
pinfo.type = Variant::COLOR;
} break;
case RS::GLOBAL_VAR_TYPE_MAT2: {
pinfo.type = Variant::PACKED_FLOAT32_ARRAY;
} break;
case RS::GLOBAL_VAR_TYPE_MAT3: {
pinfo.type = Variant::BASIS;
} break;
case RS::GLOBAL_VAR_TYPE_MAT4: {
pinfo.type = Variant::PROJECTION;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
pinfo.type = Variant::TRANSFORM2D;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
pinfo.type = Variant::TRANSFORM3D;
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture2D";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture2DArray";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Texture3D";
} break;
case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
pinfo.hint_string = "Cubemap";
} break;
default: {
} break;
}
if (!overrides.has(variables[i])) {
Override o;
o.in_use = false;
Callable::CallError ce;
Variant::construct(pinfo.type, o.override, nullptr, 0, ce);
overrides[variables[i]] = o;
}
Override *o = overrides.getptr(variables[i]);
if (o->in_use && o->override.get_type() != Variant::NIL) {
pinfo.usage |= PROPERTY_USAGE_CHECKED;
pinfo.usage |= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(pinfo);
}
}
void ShaderGlobalsOverride::_activate() {
ERR_FAIL_NULL(get_tree());
List<Node *> nodes;
get_tree()->get_nodes_in_group(SceneStringName(shader_overrides_group_active), &nodes);
if (nodes.size() == 0) {
//good we are the only override, enable all
active = true;
add_to_group(SceneStringName(shader_overrides_group_active));
for (const KeyValue<StringName, Override> &E : overrides) {
const Override *o = &E.value;
if (o->in_use && o->override.get_type() != Variant::NIL) {
if (o->override.get_type() == Variant::OBJECT) {
RID tex_rid = o->override;
RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid);
} else {
RS::get_singleton()->global_shader_parameter_set_override(E.key, o->override);
}
}
update_configuration_warnings(); //may have activated
}
}
}
void ShaderGlobalsOverride::_notification(int p_what) {
switch (p_what) {
case Node::NOTIFICATION_ENTER_TREE: {
add_to_group(SceneStringName(shader_overrides_group));
_activate();
} break;
case Node::NOTIFICATION_EXIT_TREE: {
if (active) {
//remove overrides
for (const KeyValue<StringName, Override> &E : overrides) {
const Override *o = &E.value;
if (o->in_use) {
RS::get_singleton()->global_shader_parameter_set_override(E.key, Variant());
}
}
}
remove_from_group(SceneStringName(shader_overrides_group_active));
remove_from_group(SceneStringName(shader_overrides_group));
get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringName(shader_overrides_group), "_activate"); //another may want to activate when this is removed
active = false;
} break;
}
}
PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
if (!active) {
warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
}
return warnings;
}
void ShaderGlobalsOverride::_bind_methods() {
ClassDB::bind_method(D_METHOD("_activate"), &ShaderGlobalsOverride::_activate);
}
ShaderGlobalsOverride::ShaderGlobalsOverride() {}

View file

@ -0,0 +1,66 @@
/**************************************************************************/
/* shader_globals_override.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 SHADER_GLOBALS_OVERRIDE_H
#define SHADER_GLOBALS_OVERRIDE_H
#include "scene/main/node.h"
class ShaderGlobalsOverride : public Node {
GDCLASS(ShaderGlobalsOverride, Node);
struct Override {
bool in_use = false;
Variant override;
};
StringName *_remap(const StringName &p_name) const;
bool active = false;
mutable HashMap<StringName, Override> overrides;
mutable HashMap<StringName, StringName> param_remaps;
void _activate();
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;
void _notification(int p_what);
static void _bind_methods();
public:
PackedStringArray get_configuration_warnings() const override;
ShaderGlobalsOverride();
};
#endif // SHADER_GLOBALS_OVERRIDE_H

View file

@ -0,0 +1,192 @@
/**************************************************************************/
/* status_indicator.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "status_indicator.h"
#include "scene/gui/popup_menu.h"
void StatusIndicator::_notification(int p_what) {
ERR_MAIN_THREAD_GUARD;
#ifdef TOOLS_ENABLED
if (is_part_of_edited_scene()) {
return;
}
#endif
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
if (visible && iid == DisplayServer::INVALID_INDICATOR_ID) {
iid = DisplayServer::get_singleton()->create_status_indicator(icon, tooltip, callable_mp(this, &StatusIndicator::_callback));
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
RID menu_rid = pm->bind_global_menu();
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
}
}
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
pm->unbind_global_menu();
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
}
DisplayServer::get_singleton()->delete_status_indicator(iid);
iid = DisplayServer::INVALID_INDICATOR_ID;
}
}
} break;
default:
break;
}
}
void StatusIndicator::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tooltip", "tooltip"), &StatusIndicator::set_tooltip);
ClassDB::bind_method(D_METHOD("get_tooltip"), &StatusIndicator::get_tooltip);
ClassDB::bind_method(D_METHOD("set_icon", "texture"), &StatusIndicator::set_icon);
ClassDB::bind_method(D_METHOD("get_icon"), &StatusIndicator::get_icon);
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &StatusIndicator::set_visible);
ClassDB::bind_method(D_METHOD("is_visible"), &StatusIndicator::is_visible);
ClassDB::bind_method(D_METHOD("set_menu", "menu"), &StatusIndicator::set_menu);
ClassDB::bind_method(D_METHOD("get_menu"), &StatusIndicator::get_menu);
ClassDB::bind_method(D_METHOD("get_rect"), &StatusIndicator::get_rect);
ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::INT, "mouse_button"), PropertyInfo(Variant::VECTOR2I, "mouse_position")));
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "get_tooltip");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_icon", "get_icon");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "menu", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PopupMenu"), "set_menu", "get_menu");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
}
void StatusIndicator::_callback(MouseButton p_index, const Point2i &p_pos) {
emit_signal(SceneStringName(pressed), p_index, p_pos);
}
void StatusIndicator::set_icon(const Ref<Texture2D> &p_icon) {
ERR_MAIN_THREAD_GUARD;
icon = p_icon;
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
DisplayServer::get_singleton()->status_indicator_set_icon(iid, icon);
}
}
Ref<Texture2D> StatusIndicator::get_icon() const {
return icon;
}
void StatusIndicator::set_tooltip(const String &p_tooltip) {
ERR_MAIN_THREAD_GUARD;
tooltip = p_tooltip;
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
DisplayServer::get_singleton()->status_indicator_set_tooltip(iid, tooltip);
}
}
String StatusIndicator::get_tooltip() const {
return tooltip;
}
void StatusIndicator::set_menu(const NodePath &p_menu) {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
pm->unbind_global_menu();
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
}
}
menu = p_menu;
pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
RID menu_rid = pm->bind_global_menu();
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
}
}
}
NodePath StatusIndicator::get_menu() const {
return menu;
}
void StatusIndicator::set_visible(bool p_visible) {
ERR_MAIN_THREAD_GUARD;
if (visible == p_visible) {
return;
}
visible = p_visible;
if (!is_inside_tree()) {
return;
}
#ifdef TOOLS_ENABLED
if (is_part_of_edited_scene()) {
return;
}
#endif
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
if (visible && iid == DisplayServer::INVALID_INDICATOR_ID) {
iid = DisplayServer::get_singleton()->create_status_indicator(icon, tooltip, callable_mp(this, &StatusIndicator::_callback));
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
RID menu_rid = pm->bind_global_menu();
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
}
}
if (!visible && iid != DisplayServer::INVALID_INDICATOR_ID) {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
if (pm) {
pm->unbind_global_menu();
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
}
DisplayServer::get_singleton()->delete_status_indicator(iid);
iid = DisplayServer::INVALID_INDICATOR_ID;
}
}
}
bool StatusIndicator::is_visible() const {
return visible;
}
Rect2 StatusIndicator::get_rect() const {
if (iid == DisplayServer::INVALID_INDICATOR_ID) {
return Rect2();
}
return DisplayServer::get_singleton()->status_indicator_get_rect(iid);
}

View file

@ -0,0 +1,68 @@
/**************************************************************************/
/* status_indicator.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 STATUS_INDICATOR_H
#define STATUS_INDICATOR_H
#include "scene/main/node.h"
#include "servers/display_server.h"
class StatusIndicator : public Node {
GDCLASS(StatusIndicator, Node);
Ref<Texture2D> icon;
String tooltip;
bool visible = true;
DisplayServer::IndicatorID iid = DisplayServer::INVALID_INDICATOR_ID;
NodePath menu;
protected:
void _notification(int p_what);
static void _bind_methods();
void _callback(MouseButton p_index, const Point2i &p_pos);
public:
void set_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_icon() const;
void set_tooltip(const String &p_tooltip);
String get_tooltip() const;
void set_menu(const NodePath &p_menu);
NodePath get_menu() const;
void set_visible(bool p_visible);
bool is_visible() const;
Rect2 get_rect() const;
};
#endif // STATUS_INDICATOR_H

229
engine/scene/main/timer.cpp Normal file
View file

@ -0,0 +1,229 @@
/**************************************************************************/
/* timer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 "timer.h"
void Timer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
if (autostart) {
#ifdef TOOLS_ENABLED
if (is_part_of_edited_scene()) {
break;
}
#endif
start();
autostart = false;
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (!processing || timer_process_callback == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
return;
}
time_left -= get_process_delta_time();
if (time_left < 0) {
if (!one_shot) {
time_left += wait_time;
} else {
stop();
}
emit_signal(SNAME("timeout"));
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
return;
}
time_left -= get_physics_process_delta_time();
if (time_left < 0) {
if (!one_shot) {
time_left += wait_time;
} else {
stop();
}
emit_signal(SNAME("timeout"));
}
} break;
}
}
void Timer::set_wait_time(double p_time) {
ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero.");
wait_time = p_time;
update_configuration_warnings();
}
double Timer::get_wait_time() const {
return wait_time;
}
void Timer::set_one_shot(bool p_one_shot) {
one_shot = p_one_shot;
}
bool Timer::is_one_shot() const {
return one_shot;
}
void Timer::set_autostart(bool p_start) {
autostart = p_start;
}
bool Timer::has_autostart() const {
return autostart;
}
void Timer::start(double p_time) {
ERR_FAIL_COND_MSG(!is_inside_tree(), "Timer was not added to the SceneTree. Either add it or set autostart to true.");
if (p_time > 0) {
set_wait_time(p_time);
}
time_left = wait_time;
_set_process(true);
}
void Timer::stop() {
time_left = -1;
_set_process(false);
autostart = false;
}
void Timer::set_paused(bool p_paused) {
if (paused == p_paused) {
return;
}
paused = p_paused;
_set_process(processing);
}
bool Timer::is_paused() const {
return paused;
}
bool Timer::is_stopped() const {
return get_time_left() <= 0;
}
double Timer::get_time_left() const {
return time_left > 0 ? time_left : 0;
}
void Timer::set_timer_process_callback(TimerProcessCallback p_callback) {
if (timer_process_callback == p_callback) {
return;
}
switch (timer_process_callback) {
case TIMER_PROCESS_PHYSICS:
if (is_physics_processing_internal()) {
set_physics_process_internal(false);
set_process_internal(true);
}
break;
case TIMER_PROCESS_IDLE:
if (is_processing_internal()) {
set_process_internal(false);
set_physics_process_internal(true);
}
break;
}
timer_process_callback = p_callback;
}
Timer::TimerProcessCallback Timer::get_timer_process_callback() const {
return timer_process_callback;
}
void Timer::_set_process(bool p_process, bool p_force) {
switch (timer_process_callback) {
case TIMER_PROCESS_PHYSICS:
set_physics_process_internal(p_process && !paused);
break;
case TIMER_PROCESS_IDLE:
set_process_internal(p_process && !paused);
break;
}
processing = p_process;
}
PackedStringArray Timer::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
if (wait_time < 0.05 - CMP_EPSILON) {
warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
}
return warnings;
}
void Timer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time);
ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time);
ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &Timer::set_one_shot);
ClassDB::bind_method(D_METHOD("is_one_shot"), &Timer::is_one_shot);
ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart);
ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart);
ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("stop"), &Timer::stop);
ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused);
ClassDB::bind_method(D_METHOD("is_paused"), &Timer::is_paused);
ClassDB::bind_method(D_METHOD("is_stopped"), &Timer::is_stopped);
ClassDB::bind_method(D_METHOD("get_time_left"), &Timer::get_time_left);
ClassDB::bind_method(D_METHOD("set_timer_process_callback", "callback"), &Timer::set_timer_process_callback);
ClassDB::bind_method(D_METHOD("get_timer_process_callback"), &Timer::get_timer_process_callback);
ADD_SIGNAL(MethodInfo("timeout"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_timer_process_callback", "get_timer_process_callback");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_RANGE, "0.001,4096,0.001,or_greater,exp,suffix:s"), "set_wait_time", "get_wait_time");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paused", "is_paused");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_left", PROPERTY_HINT_NONE, "suffix:s", PROPERTY_USAGE_NONE), "", "get_time_left");
BIND_ENUM_CONSTANT(TIMER_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(TIMER_PROCESS_IDLE);
}
Timer::Timer() {}

89
engine/scene/main/timer.h Normal file
View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* timer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 TIMER_H
#define TIMER_H
#include "scene/main/node.h"
class Timer : public Node {
GDCLASS(Timer, Node);
double wait_time = 1.0;
bool one_shot = false;
bool autostart = false;
bool processing = false;
bool paused = false;
double time_left = -1.0;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
enum TimerProcessCallback {
TIMER_PROCESS_PHYSICS,
TIMER_PROCESS_IDLE,
};
void set_wait_time(double p_time);
double get_wait_time() const;
void set_one_shot(bool p_one_shot);
bool is_one_shot() const;
void set_autostart(bool p_start);
bool has_autostart() const;
void start(double p_time = -1);
void stop();
void set_paused(bool p_paused);
bool is_paused() const;
bool is_stopped() const;
double get_time_left() const;
PackedStringArray get_configuration_warnings() const override;
void set_timer_process_callback(TimerProcessCallback p_callback);
TimerProcessCallback get_timer_process_callback() const;
Timer();
private:
TimerProcessCallback timer_process_callback = TIMER_PROCESS_IDLE;
void _set_process(bool p_process, bool p_force = false);
};
VARIANT_ENUM_CAST(Timer::TimerProcessCallback);
#endif // TIMER_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,866 @@
/**************************************************************************/
/* viewport.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 VIEWPORT_H
#define VIEWPORT_H
#include "scene/main/node.h"
#include "scene/resources/texture.h"
#ifndef _3D_DISABLED
class Camera3D;
class CollisionObject3D;
class AudioListener3D;
class World3D;
#endif // _3D_DISABLED
class AudioListener2D;
class Camera2D;
class CanvasItem;
class CanvasLayer;
class Control;
class Label;
class SceneTreeTimer;
class Viewport;
class Window;
class World2D;
class ViewportTexture : public Texture2D {
GDCLASS(ViewportTexture, Texture2D);
NodePath path;
friend class Viewport;
Viewport *vp = nullptr;
bool vp_pending = false;
bool vp_changed = false;
void _setup_local_to_scene(const Node *p_loc_scene);
mutable RID proxy_ph;
mutable RID proxy;
protected:
static void _bind_methods();
virtual void reset_local_to_scene() override;
public:
void set_viewport_path_in_scene(const NodePath &p_path);
NodePath get_viewport_path_in_scene() const;
virtual void setup_local_to_scene() override;
virtual int get_width() const override;
virtual int get_height() const override;
virtual Size2 get_size() const override;
virtual RID get_rid() const override;
virtual bool has_alpha() const override;
virtual Ref<Image> get_image() const override;
ViewportTexture();
~ViewportTexture();
};
class Viewport : public Node {
GDCLASS(Viewport, Node);
public:
enum Scaling3DMode {
SCALING_3D_MODE_BILINEAR,
SCALING_3D_MODE_FSR,
SCALING_3D_MODE_FSR2,
SCALING_3D_MODE_MAX
};
enum PositionalShadowAtlasQuadrantSubdiv {
SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED,
SHADOW_ATLAS_QUADRANT_SUBDIV_1,
SHADOW_ATLAS_QUADRANT_SUBDIV_4,
SHADOW_ATLAS_QUADRANT_SUBDIV_16,
SHADOW_ATLAS_QUADRANT_SUBDIV_64,
SHADOW_ATLAS_QUADRANT_SUBDIV_256,
SHADOW_ATLAS_QUADRANT_SUBDIV_1024,
SHADOW_ATLAS_QUADRANT_SUBDIV_MAX,
};
enum MSAA {
MSAA_DISABLED,
MSAA_2X,
MSAA_4X,
MSAA_8X,
// 16x MSAA is not supported due to its high cost and driver bugs.
MSAA_MAX
};
enum ScreenSpaceAA {
SCREEN_SPACE_AA_DISABLED,
SCREEN_SPACE_AA_FXAA,
SCREEN_SPACE_AA_MAX
};
enum RenderInfo {
RENDER_INFO_OBJECTS_IN_FRAME,
RENDER_INFO_PRIMITIVES_IN_FRAME,
RENDER_INFO_DRAW_CALLS_IN_FRAME,
RENDER_INFO_MAX
};
enum RenderInfoType {
RENDER_INFO_TYPE_VISIBLE,
RENDER_INFO_TYPE_SHADOW,
RENDER_INFO_TYPE_CANVAS,
RENDER_INFO_TYPE_MAX
};
enum DebugDraw {
DEBUG_DRAW_DISABLED,
DEBUG_DRAW_UNSHADED,
DEBUG_DRAW_LIGHTING,
DEBUG_DRAW_OVERDRAW,
DEBUG_DRAW_WIREFRAME,
DEBUG_DRAW_NORMAL_BUFFER,
DEBUG_DRAW_VOXEL_GI_ALBEDO,
DEBUG_DRAW_VOXEL_GI_LIGHTING,
DEBUG_DRAW_VOXEL_GI_EMISSION,
DEBUG_DRAW_SHADOW_ATLAS,
DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
DEBUG_DRAW_SCENE_LUMINANCE,
DEBUG_DRAW_SSAO,
DEBUG_DRAW_SSIL,
DEBUG_DRAW_PSSM_SPLITS,
DEBUG_DRAW_DECAL_ATLAS,
DEBUG_DRAW_SDFGI,
DEBUG_DRAW_SDFGI_PROBES,
DEBUG_DRAW_GI_BUFFER,
DEBUG_DRAW_DISABLE_LOD,
DEBUG_DRAW_CLUSTER_OMNI_LIGHTS,
DEBUG_DRAW_CLUSTER_SPOT_LIGHTS,
DEBUG_DRAW_CLUSTER_DECALS,
DEBUG_DRAW_CLUSTER_REFLECTION_PROBES,
DEBUG_DRAW_OCCLUDERS,
DEBUG_DRAW_MOTION_VECTORS,
DEBUG_DRAW_INTERNAL_BUFFER,
};
enum DefaultCanvasItemTextureFilter {
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST,
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR,
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS,
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS,
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX
};
enum DefaultCanvasItemTextureRepeat {
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED,
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED,
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR,
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX,
};
enum SDFOversize {
SDF_OVERSIZE_100_PERCENT,
SDF_OVERSIZE_120_PERCENT,
SDF_OVERSIZE_150_PERCENT,
SDF_OVERSIZE_200_PERCENT,
SDF_OVERSIZE_MAX
};
enum SDFScale {
SDF_SCALE_100_PERCENT,
SDF_SCALE_50_PERCENT,
SDF_SCALE_25_PERCENT,
SDF_SCALE_MAX
};
enum {
SUBWINDOW_CANVAS_LAYER = 1024
};
enum VRSMode {
VRS_DISABLED,
VRS_TEXTURE,
VRS_XR,
VRS_MAX
};
enum VRSUpdateMode {
VRS_UPDATE_DISABLED,
VRS_UPDATE_ONCE,
VRS_UPDATE_ALWAYS,
VRS_UPDATE_MAX
};
private:
friend class ViewportTexture;
Viewport *parent = nullptr;
Viewport *gui_parent = nullptr; // Whose gui.tooltip_popup it is.
HashSet<CanvasLayer *> canvas_layers;
RID viewport;
RID current_canvas;
RID subwindow_canvas;
bool override_canvas_transform = false;
Transform2D canvas_transform_override;
Transform2D canvas_transform;
Transform2D global_canvas_transform;
Transform2D stretch_transform;
Size2i size = Size2i(512, 512);
Size2i size_2d_override;
bool size_allocated = false;
RID contact_2d_debug;
RID contact_3d_debug_multimesh;
RID contact_3d_debug_instance;
Rect2 last_vp_rect;
bool transparent_bg = false;
bool use_hdr_2d = false;
bool gen_mipmaps = false;
bool snap_controls_to_pixels = true;
bool snap_2d_transforms_to_pixel = false;
bool snap_2d_vertices_to_pixel = false;
bool physics_object_picking = false;
bool physics_object_picking_sort = false;
bool physics_object_picking_first_only = false;
List<Ref<InputEvent>> physics_picking_events;
ObjectID physics_object_capture;
ObjectID physics_object_over;
Transform3D physics_last_object_transform;
Transform3D physics_last_camera_transform;
ObjectID physics_last_id;
bool handle_input_locally = true;
bool local_input_handled = false;
Ref<World2D> world_2d;
StringName input_group;
StringName shortcut_input_group;
StringName unhandled_input_group;
StringName unhandled_key_input_group;
void _update_audio_listener_2d();
bool disable_3d = false;
void _propagate_viewport_notification(Node *p_node, int p_what);
void _update_global_transform();
RID texture_rid;
DebugDraw debug_draw = DEBUG_DRAW_DISABLED;
int positional_shadow_atlas_size = 2048;
bool positional_shadow_atlas_16_bits = true;
PositionalShadowAtlasQuadrantSubdiv positional_shadow_atlas_quadrant_subdiv[4];
MSAA msaa_2d = MSAA_DISABLED;
MSAA msaa_3d = MSAA_DISABLED;
ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED;
bool use_taa = false;
Scaling3DMode scaling_3d_mode = SCALING_3D_MODE_BILINEAR;
float scaling_3d_scale = 1.0;
float fsr_sharpness = 0.2f;
float texture_mipmap_bias = 0.0f;
bool use_debanding = false;
float mesh_lod_threshold = 1.0;
bool use_occlusion_culling = false;
Ref<ViewportTexture> default_texture;
HashSet<ViewportTexture *> viewport_textures;
void _update_viewport_path();
SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT;
SDFScale sdf_scale = SDF_SCALE_50_PERCENT;
uint32_t canvas_cull_mask = 0xffffffff; // by default show everything
enum SubWindowDrag {
SUB_WINDOW_DRAG_DISABLED,
SUB_WINDOW_DRAG_MOVE,
SUB_WINDOW_DRAG_CLOSE,
SUB_WINDOW_DRAG_RESIZE,
};
enum SubWindowResize {
SUB_WINDOW_RESIZE_DISABLED,
SUB_WINDOW_RESIZE_TOP_LEFT,
SUB_WINDOW_RESIZE_TOP,
SUB_WINDOW_RESIZE_TOP_RIGHT,
SUB_WINDOW_RESIZE_LEFT,
SUB_WINDOW_RESIZE_RIGHT,
SUB_WINDOW_RESIZE_BOTTOM_LEFT,
SUB_WINDOW_RESIZE_BOTTOM,
SUB_WINDOW_RESIZE_BOTTOM_RIGHT,
SUB_WINDOW_RESIZE_MAX
};
struct SubWindow {
Window *window = nullptr;
RID canvas_item;
Rect2i parent_safe_rect;
bool pending_window_update = false;
};
// VRS
VRSMode vrs_mode = VRS_DISABLED;
VRSUpdateMode vrs_update_mode = VRS_UPDATE_ONCE;
Ref<Texture2D> vrs_texture;
struct GUI {
bool forced_mouse_focus = false; //used for menu buttons
bool mouse_in_viewport = false;
bool key_event_accepted = false;
HashMap<int, ObjectID> touch_focus;
Control *mouse_focus = nullptr;
Control *mouse_click_grabber = nullptr;
BitField<MouseButtonMask> mouse_focus_mask;
Control *key_focus = nullptr;
Control *mouse_over = nullptr;
LocalVector<Control *> mouse_over_hierarchy;
bool sending_mouse_enter_exit_notifications = false;
Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
Window *windowmanager_window_over = nullptr; // Only used in root Viewport.
Control *drag_mouse_over = nullptr;
Vector2 drag_mouse_over_pos;
Control *tooltip_control = nullptr;
Window *tooltip_popup = nullptr;
Label *tooltip_label = nullptr;
String tooltip_text;
Point2 tooltip_pos;
Point2 last_mouse_pos;
Point2 drag_accum;
bool drag_attempted = false;
Variant drag_data;
ObjectID drag_preview_id;
Ref<SceneTreeTimer> tooltip_timer;
double tooltip_delay = 0.0;
bool roots_order_dirty = false;
List<Control *> roots;
HashSet<ObjectID> canvas_parents_with_dirty_order;
int canvas_sort_index = 0; //for sorting items with canvas as root
bool dragging = false;
bool drag_successful = false;
bool embed_subwindows_hint = false;
Window *subwindow_focused = nullptr;
Window *currently_dragged_subwindow = nullptr;
SubWindowDrag subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
Vector2 subwindow_drag_from;
Vector2 subwindow_drag_pos;
Rect2i subwindow_drag_close_rect;
bool subwindow_drag_close_inside = false;
SubWindowResize subwindow_resize_mode;
Rect2i subwindow_resize_from_rect;
Vector<SubWindow> sub_windows; // Don't obtain references or pointers to the elements, as their location can change.
} gui;
DefaultCanvasItemTextureFilter default_canvas_item_texture_filter = DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
bool disable_input = false;
bool _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
void _gui_call_notification(Control *p_control, int p_what);
void _gui_sort_roots();
Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform);
void _gui_input_event(Ref<InputEvent> p_event);
void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2());
void _gui_cleanup_internal_state(Ref<InputEvent> p_event);
void _push_unhandled_input_internal(const Ref<InputEvent> &p_event);
Ref<InputEvent> _make_input_local(const Ref<InputEvent> &ev);
friend class Control;
List<Control *>::Element *_gui_add_root_control(Control *p_control);
void _gui_remove_root_control(List<Control *>::Element *RI);
String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner = nullptr);
void _gui_cancel_tooltip();
void _gui_show_tooltip();
void _gui_remove_control(Control *p_control);
void _gui_hide_control(Control *p_control);
void _gui_update_mouse_over();
void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control);
void _gui_set_drag_preview(Control *p_base, Control *p_control);
Control *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window);
void _gui_unfocus_control(Control *p_control);
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
void _gui_grab_click_focus(Control *p_control);
void _post_gui_grab_click_focus();
void _gui_accept_event();
bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check);
friend class CanvasLayer;
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
void _drop_mouse_over(Control *p_until_control = nullptr);
void _drop_mouse_focus();
void _drop_physics_mouseover(bool p_paused_only = false);
void _update_canvas_items(Node *p_node);
friend class Window;
void _sub_window_update_order();
void _sub_window_register(Window *p_window);
void _sub_window_update(Window *p_window);
void _sub_window_grab_focus(Window *p_window);
void _sub_window_remove(Window *p_window);
int _sub_window_find(Window *p_window) const;
bool _sub_windows_forward_input(const Ref<InputEvent> &p_event);
SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point);
void _update_mouse_over();
virtual void _update_mouse_over(Vector2 p_pos);
virtual void _mouse_leave_viewport();
virtual bool _can_consume_input_events() const { return true; }
uint64_t event_count = 0;
void _process_dirty_canvas_parent_orders();
void _propagate_world_2d_changed(Node *p_node);
protected:
bool _set_size(const Size2i &p_size, const Size2i &p_size_2d_override, bool p_allocated);
Size2i _get_size() const;
Size2i _get_size_2d_override() const;
bool _is_size_allocated() const;
void _notification(int p_what);
void _process_picking();
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
public:
void canvas_parent_mark_dirty(Node *p_node);
void canvas_item_top_level_changed();
uint64_t get_processed_events_count() const { return event_count; }
void update_canvas_items();
Rect2 get_visible_rect() const;
RID get_viewport_rid() const;
void set_world_2d(const Ref<World2D> &p_world_2d);
Ref<World2D> get_world_2d() const;
Ref<World2D> find_world_2d() const;
void enable_canvas_transform_override(bool p_enable);
bool is_canvas_transform_override_enabled() const;
void set_canvas_transform_override(const Transform2D &p_transform);
Transform2D get_canvas_transform_override() const;
void set_canvas_transform(const Transform2D &p_transform);
Transform2D get_canvas_transform() const;
void set_global_canvas_transform(const Transform2D &p_transform);
Transform2D get_global_canvas_transform() const;
virtual Transform2D get_final_transform() const;
void gui_set_root_order_dirty();
void set_transparent_background(bool p_enable);
bool has_transparent_background() const;
void set_use_hdr_2d(bool p_enable);
bool is_using_hdr_2d() const;
Ref<ViewportTexture> get_texture() const;
void set_positional_shadow_atlas_size(int p_size);
int get_positional_shadow_atlas_size() const;
void set_positional_shadow_atlas_16_bits(bool p_16_bits);
bool get_positional_shadow_atlas_16_bits() const;
void set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv);
PositionalShadowAtlasQuadrantSubdiv get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const;
void set_msaa_2d(MSAA p_msaa);
MSAA get_msaa_2d() const;
void set_msaa_3d(MSAA p_msaa);
MSAA get_msaa_3d() const;
void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa);
ScreenSpaceAA get_screen_space_aa() const;
void set_use_taa(bool p_use_taa);
bool is_using_taa() const;
void set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode);
Scaling3DMode get_scaling_3d_mode() const;
void set_scaling_3d_scale(float p_scaling_3d_scale);
float get_scaling_3d_scale() const;
void set_fsr_sharpness(float p_fsr_sharpness);
float get_fsr_sharpness() const;
void set_texture_mipmap_bias(float p_texture_mipmap_bias);
float get_texture_mipmap_bias() const;
void set_use_debanding(bool p_use_debanding);
bool is_using_debanding() const;
void set_mesh_lod_threshold(float p_pixels);
float get_mesh_lod_threshold() const;
void set_use_occlusion_culling(bool p_us_occlusion_culling);
bool is_using_occlusion_culling() const;
Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const;
Vector2 get_camera_rect_size() const;
void push_text_input(const String &p_text);
void push_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
#ifndef DISABLE_DEPRECATED
void push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
#endif // DISABLE_DEPRECATED
void set_disable_input(bool p_disable);
bool is_input_disabled() const;
Vector2 get_mouse_position() const;
void warp_mouse(const Vector2 &p_position);
virtual void update_mouse_cursor_state();
void set_physics_object_picking(bool p_enable);
bool get_physics_object_picking();
void set_physics_object_picking_sort(bool p_enable);
bool get_physics_object_picking_sort();
void set_physics_object_picking_first_only(bool p_enable);
bool get_physics_object_picking_first_only();
Variant gui_get_drag_data() const;
void gui_reset_canvas_sort_index();
int gui_get_canvas_sort_index();
void gui_release_focus();
Control *gui_get_focus_owner() const;
Control *gui_get_hovered_control() const;
PackedStringArray get_configuration_warnings() const override;
void set_debug_draw(DebugDraw p_debug_draw);
DebugDraw get_debug_draw() const;
int get_render_info(RenderInfoType p_type, RenderInfo p_info);
void set_snap_controls_to_pixels(bool p_enable);
bool is_snap_controls_to_pixels_enabled() const;
void set_snap_2d_transforms_to_pixel(bool p_enable);
bool is_snap_2d_transforms_to_pixel_enabled() const;
void set_snap_2d_vertices_to_pixel(bool p_enable);
bool is_snap_2d_vertices_to_pixel_enabled() const;
void set_input_as_handled();
bool is_input_handled() const;
void set_handle_input_locally(bool p_enable);
bool is_handling_input_locally() const;
bool gui_is_dragging() const;
bool gui_is_drag_successful() const;
void gui_cancel_drag();
Control *gui_find_control(const Point2 &p_global);
void set_sdf_oversize(SDFOversize p_sdf_oversize);
SDFOversize get_sdf_oversize() const;
void set_sdf_scale(SDFScale p_sdf_scale);
SDFScale get_sdf_scale() const;
void set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter);
DefaultCanvasItemTextureFilter get_default_canvas_item_texture_filter() const;
void set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat);
DefaultCanvasItemTextureRepeat get_default_canvas_item_texture_repeat() const;
// VRS
void set_vrs_mode(VRSMode p_vrs_mode);
VRSMode get_vrs_mode() const;
void set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode);
VRSUpdateMode get_vrs_update_mode() const;
void set_vrs_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_vrs_texture() const;
virtual DisplayServer::WindowID get_window_id() const = 0;
void set_embedding_subwindows(bool p_embed);
bool is_embedding_subwindows() const;
TypedArray<Window> get_embedded_subwindows() const;
void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect);
Rect2i subwindow_get_popup_safe_rect(Window *p_window) const;
Viewport *get_parent_viewport() const;
Window *get_base_window() const;
void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
void set_canvas_cull_mask(uint32_t p_layers);
uint32_t get_canvas_cull_mask() const;
void set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable);
bool get_canvas_cull_mask_bit(uint32_t p_layer) const;
virtual bool is_size_2d_override_stretch_enabled() const { return true; }
Transform2D get_screen_transform() const;
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const;
virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
virtual bool is_directly_attached_to_screen() const { return false; };
virtual bool is_attached_in_viewport() const { return false; };
virtual bool is_sub_viewport() const { return false; };
private:
// 2D audio, camera, and physics. (don't put World2D here because World2D is needed for Control nodes).
friend class AudioListener2D; // Needs _audio_listener_2d_set and _audio_listener_2d_remove
AudioListener2D *audio_listener_2d = nullptr;
void _audio_listener_2d_set(AudioListener2D *p_audio_listener);
void _audio_listener_2d_remove(AudioListener2D *p_audio_listener);
bool is_audio_listener_2d_enabled = false;
RID internal_audio_listener_2d;
friend class Camera2D; // Needs _camera_2d_set
Camera2D *camera_2d = nullptr;
void _camera_2d_set(Camera2D *p_camera_2d);
// Collider to frame
HashMap<ObjectID, uint64_t> physics_2d_mouseover;
// Collider & shape to frame
HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>> physics_2d_shape_mouseover;
// Cleans up colliders corresponding to old frames or all of them.
void _cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference = 0);
public:
AudioListener2D *get_audio_listener_2d() const;
void set_as_audio_listener_2d(bool p_enable);
bool is_audio_listener_2d() const;
Camera2D *get_camera_2d() const;
void assign_next_enabled_camera_2d(const StringName &p_camera_group);
#ifndef _3D_DISABLED
private:
// 3D audio, camera, physics, and world.
bool use_xr = false;
friend class AudioListener3D;
AudioListener3D *audio_listener_3d = nullptr;
HashSet<AudioListener3D *> audio_listener_3d_set;
bool is_audio_listener_3d_enabled = false;
RID internal_audio_listener_3d;
void _update_audio_listener_3d();
void _listener_transform_3d_changed_notify();
void _audio_listener_3d_set(AudioListener3D *p_listener);
bool _audio_listener_3d_add(AudioListener3D *p_listener); //true if first
void _audio_listener_3d_remove(AudioListener3D *p_listener);
void _audio_listener_3d_make_next_current(AudioListener3D *p_exclude);
void _collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape);
struct Camera3DOverrideData {
Transform3D transform;
enum Projection {
PROJECTION_PERSPECTIVE,
PROJECTION_ORTHOGONAL
};
Projection projection = Projection::PROJECTION_PERSPECTIVE;
real_t fov = 0.0;
real_t size = 0.0;
real_t z_near = 0.0;
real_t z_far = 0.0;
RID rid;
operator bool() const {
return rid != RID();
}
} camera_3d_override;
friend class Camera3D;
Camera3D *camera_3d = nullptr;
HashSet<Camera3D *> camera_3d_set;
void _camera_3d_transform_changed_notify();
void _camera_3d_set(Camera3D *p_camera);
bool _camera_3d_add(Camera3D *p_camera); //true if first
void _camera_3d_remove(Camera3D *p_camera);
void _camera_3d_make_next_current(Camera3D *p_exclude);
Ref<World3D> world_3d;
Ref<World3D> own_world_3d;
void _own_world_3d_changed();
void _propagate_enter_world_3d(Node *p_node);
void _propagate_exit_world_3d(Node *p_node);
public:
AudioListener3D *get_audio_listener_3d() const;
void set_as_audio_listener_3d(bool p_enable);
bool is_audio_listener_3d() const;
Camera3D *get_camera_3d() const;
void enable_camera_3d_override(bool p_enable);
bool is_camera_3d_override_enabled() const;
void set_camera_3d_override_transform(const Transform3D &p_transform);
Transform3D get_camera_3d_override_transform() const;
void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
void set_disable_3d(bool p_disable);
bool is_3d_disabled() const;
void set_world_3d(const Ref<World3D> &p_world_3d);
Ref<World3D> get_world_3d() const;
Ref<World3D> find_world_3d() const;
void set_use_own_world_3d(bool p_use_own_world_3d);
bool is_using_own_world_3d() const;
void set_use_xr(bool p_use_xr);
bool is_using_xr();
#endif // _3D_DISABLED
Viewport();
~Viewport();
};
class SubViewport : public Viewport {
GDCLASS(SubViewport, Viewport);
public:
enum ClearMode {
CLEAR_MODE_ALWAYS,
CLEAR_MODE_NEVER,
CLEAR_MODE_ONCE
};
enum UpdateMode {
UPDATE_DISABLED,
UPDATE_ONCE, //then goes to disabled
UPDATE_WHEN_VISIBLE, // default
UPDATE_WHEN_PARENT_VISIBLE,
UPDATE_ALWAYS
};
private:
UpdateMode update_mode = UPDATE_WHEN_VISIBLE;
ClearMode clear_mode = CLEAR_MODE_ALWAYS;
bool size_2d_override_stretch = false;
void _internal_set_size(const Size2i &p_size, bool p_force = false);
protected:
static void _bind_methods();
virtual DisplayServer::WindowID get_window_id() const override;
void _notification(int p_what);
public:
void set_size(const Size2i &p_size);
Size2i get_size() const;
void set_size_force(const Size2i &p_size);
void set_size_2d_override(const Size2i &p_size);
Size2i get_size_2d_override() const;
void set_size_2d_override_stretch(bool p_enable);
bool is_size_2d_override_stretch_enabled() const override;
void set_update_mode(UpdateMode p_mode);
UpdateMode get_update_mode() const;
void set_clear_mode(ClearMode p_mode);
ClearMode get_clear_mode() const;
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
virtual bool is_directly_attached_to_screen() const override;
virtual bool is_attached_in_viewport() const override;
virtual bool is_sub_viewport() const override { return true; };
void _validate_property(PropertyInfo &p_property) const;
SubViewport();
~SubViewport();
};
VARIANT_ENUM_CAST(Viewport::Scaling3DMode);
VARIANT_ENUM_CAST(SubViewport::UpdateMode);
VARIANT_ENUM_CAST(Viewport::PositionalShadowAtlasQuadrantSubdiv);
VARIANT_ENUM_CAST(Viewport::MSAA);
VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA);
VARIANT_ENUM_CAST(Viewport::DebugDraw);
VARIANT_ENUM_CAST(Viewport::SDFScale);
VARIANT_ENUM_CAST(Viewport::SDFOversize);
VARIANT_ENUM_CAST(Viewport::VRSMode);
VARIANT_ENUM_CAST(Viewport::VRSUpdateMode);
VARIANT_ENUM_CAST(SubViewport::ClearMode);
VARIANT_ENUM_CAST(Viewport::RenderInfo);
VARIANT_ENUM_CAST(Viewport::RenderInfoType);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat);
#endif // VIEWPORT_H

View file

@ -0,0 +1,48 @@
/**************************************************************************/
/* window.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 DISABLE_DEPRECATED
void Window::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
}
#endif

3132
engine/scene/main/window.cpp Normal file

File diff suppressed because it is too large Load diff

496
engine/scene/main/window.h Normal file
View file

@ -0,0 +1,496 @@
/**************************************************************************/
/* window.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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 WINDOW_H
#define WINDOW_H
#include "scene/main/viewport.h"
#include "scene/resources/theme.h"
class Font;
class Shortcut;
class StyleBox;
class ThemeOwner;
class ThemeContext;
class Window : public Viewport {
GDCLASS(Window, Viewport);
public:
// Keep synced with enum hint for `mode` property.
enum Mode {
MODE_WINDOWED = DisplayServer::WINDOW_MODE_WINDOWED,
MODE_MINIMIZED = DisplayServer::WINDOW_MODE_MINIMIZED,
MODE_MAXIMIZED = DisplayServer::WINDOW_MODE_MAXIMIZED,
MODE_FULLSCREEN = DisplayServer::WINDOW_MODE_FULLSCREEN,
MODE_EXCLUSIVE_FULLSCREEN = DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
};
enum Flags {
FLAG_RESIZE_DISABLED = DisplayServer::WINDOW_FLAG_RESIZE_DISABLED,
FLAG_BORDERLESS = DisplayServer::WINDOW_FLAG_BORDERLESS,
FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP,
FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT,
FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS,
FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP,
FLAG_EXTEND_TO_TITLE = DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE,
FLAG_MOUSE_PASSTHROUGH = DisplayServer::WINDOW_FLAG_MOUSE_PASSTHROUGH,
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
};
enum ContentScaleMode {
CONTENT_SCALE_MODE_DISABLED,
CONTENT_SCALE_MODE_CANVAS_ITEMS,
CONTENT_SCALE_MODE_VIEWPORT,
};
enum ContentScaleAspect {
CONTENT_SCALE_ASPECT_IGNORE,
CONTENT_SCALE_ASPECT_KEEP,
CONTENT_SCALE_ASPECT_KEEP_WIDTH,
CONTENT_SCALE_ASPECT_KEEP_HEIGHT,
CONTENT_SCALE_ASPECT_EXPAND,
};
enum ContentScaleStretch {
CONTENT_SCALE_STRETCH_FRACTIONAL,
CONTENT_SCALE_STRETCH_INTEGER,
};
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
LAYOUT_DIRECTION_LTR,
LAYOUT_DIRECTION_RTL
};
enum {
DEFAULT_WINDOW_SIZE = 100,
};
// Keep synced with enum hint for `initial_position` property.
enum WindowInitialPosition {
WINDOW_INITIAL_POSITION_ABSOLUTE,
WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN,
WINDOW_INITIAL_POSITION_CENTER_MAIN_WINDOW_SCREEN,
WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN,
WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS,
WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS,
};
private:
DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
bool initialized = false;
String title;
String tr_title;
mutable int current_screen = 0;
mutable Vector2i position;
mutable Size2i size = Size2i(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE);
mutable Size2i min_size;
mutable Size2i max_size;
mutable Vector<Vector2> mpath;
mutable Mode mode = MODE_WINDOWED;
mutable bool flags[FLAG_MAX] = {};
bool visible = true;
bool focused = false;
WindowInitialPosition initial_position = WINDOW_INITIAL_POSITION_ABSOLUTE;
bool force_native = false;
bool use_font_oversampling = false;
bool transient = false;
bool transient_to_focused = false;
bool exclusive = false;
bool wrap_controls = false;
bool updating_child_controls = false;
bool updating_embedded_window = false;
bool clamp_to_embedder = false;
bool unparent_when_invisible = false;
bool keep_title_visible = false;
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
void _update_child_controls();
void _update_embedded_window();
Size2i content_scale_size;
ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED;
ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE;
ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL;
real_t content_scale_factor = 1.0;
void _make_window();
void _clear_window();
void _update_from_window();
bool _try_parent_dialog(Node *p_from_node);
Size2i max_size_used;
Size2i _clamp_limit_size(const Size2i &p_limit_size);
Size2i _clamp_window_size(const Size2i &p_size);
void _validate_limit_size();
void _update_viewport_size();
void _update_window_size();
void _propagate_window_notification(Node *p_node, int p_notification);
void _update_window_callbacks();
Window *transient_parent = nullptr;
Window *exclusive_child = nullptr;
HashSet<Window *> transient_children;
void _clear_transient();
void _make_transient();
void _set_transient_exclusive_child(bool p_clear_invalid = false);
ThemeOwner *theme_owner = nullptr;
Ref<Theme> theme;
StringName theme_type_variation;
bool bulk_theme_override = false;
Theme::ThemeIconMap theme_icon_override;
Theme::ThemeStyleMap theme_style_override;
Theme::ThemeFontMap theme_font_override;
Theme::ThemeFontSizeMap theme_font_size_override;
Theme::ThemeColorMap theme_color_override;
Theme::ThemeConstantMap theme_constant_override;
mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache;
mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache;
mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache;
mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache;
mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache;
mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache;
void _theme_changed();
void _notify_theme_override_changed();
void _invalidate_theme_cache();
struct ThemeCache {
Ref<StyleBox> embedded_border;
Ref<StyleBox> embedded_unfocused_border;
Ref<Font> title_font;
int title_font_size = 0;
Color title_color;
int title_height = 0;
Color title_outline_modulate;
int title_outline_size = 0;
Ref<Texture2D> close;
Ref<Texture2D> close_pressed;
int close_h_offset = 0;
int close_v_offset = 0;
int resize_margin = 0;
} theme_cache;
void _settings_changed();
Viewport *embedder = nullptr;
Transform2D window_transform;
friend class Viewport; //friend back, can call the methods below
void _window_input(const Ref<InputEvent> &p_ev);
void _window_input_text(const String &p_text);
void _window_drop_files(const Vector<String> &p_files);
void _rect_changed_callback(const Rect2i &p_callback);
void _event_callback(DisplayServer::WindowEvent p_event);
virtual bool _can_consume_input_events() const override;
bool mouse_in_window = false;
void _update_mouse_over(Vector2 p_pos) override;
void _mouse_leave_viewport() override;
Ref<Shortcut> debugger_stop_shortcut;
static int root_layout_direction;
protected:
virtual Rect2i _popup_adjust_rect() const { return Rect2i(); }
virtual void _post_popup() {}
virtual void _update_theme_item_cache();
virtual void _input_from_window(const Ref<InputEvent> &p_event) {}
void _notification(int p_what);
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
static void _bind_compatibility_methods();
#endif
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;
void _validate_property(PropertyInfo &p_property) const;
virtual void add_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
GDVIRTUAL0RC(Vector2, _get_contents_minimum_size)
public:
enum {
NOTIFICATION_VISIBILITY_CHANGED = 30,
NOTIFICATION_POST_POPUP = 31,
NOTIFICATION_THEME_CHANGED = 32
};
static void set_root_layout_direction(int p_root_dir);
void set_title(const String &p_title);
String get_title() const;
void set_initial_position(WindowInitialPosition p_initial_position);
WindowInitialPosition get_initial_position() const;
void set_force_native(bool p_force_native);
bool get_force_native() const;
void set_current_screen(int p_screen);
int get_current_screen() const;
void set_position(const Point2i &p_position);
Point2i get_position() const;
void move_to_center();
void set_size(const Size2i &p_size);
Size2i get_size() const;
void reset_size();
Point2i get_position_with_decorations() const;
Size2i get_size_with_decorations() const;
void set_max_size(const Size2i &p_max_size);
Size2i get_max_size() const;
void set_min_size(const Size2i &p_min_size);
Size2i get_min_size() const;
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_flag(Flags p_flag, bool p_enabled);
bool get_flag(Flags p_flag) const;
bool is_maximize_allowed() const;
void request_attention();
#ifndef DISABLE_DEPRECATED
void move_to_foreground();
#endif // DISABLE_DEPRECATED
virtual void set_visible(bool p_visible);
bool is_visible() const;
void update_mouse_cursor_state() override;
void show();
void hide();
void set_transient(bool p_transient);
bool is_transient() const;
void set_transient_to_focused(bool p_transient_to_focused);
bool is_transient_to_focused() const;
void set_exclusive(bool p_exclusive);
bool is_exclusive() const;
void set_clamp_to_embedder(bool p_enable);
bool is_clamped_to_embedder() const;
void set_unparent_when_invisible(bool p_unparent);
bool is_in_edited_scene_root() const;
bool can_draw() const;
void set_ime_active(bool p_active);
void set_ime_position(const Point2i &p_pos);
bool is_embedded() const;
Viewport *get_embedder() const;
void set_content_scale_size(const Size2i &p_size);
Size2i get_content_scale_size() const;
void set_content_scale_mode(ContentScaleMode p_mode);
ContentScaleMode get_content_scale_mode() const;
void set_content_scale_aspect(ContentScaleAspect p_aspect);
ContentScaleAspect get_content_scale_aspect() const;
void set_content_scale_stretch(ContentScaleStretch p_stretch);
ContentScaleStretch get_content_scale_stretch() const;
void set_keep_title_visible(bool p_title_visible);
bool get_keep_title_visible() const;
void set_content_scale_factor(real_t p_factor);
real_t get_content_scale_factor() const;
void set_use_font_oversampling(bool p_oversampling);
bool is_using_font_oversampling() const;
void set_mouse_passthrough_polygon(const Vector<Vector2> &p_region);
Vector<Vector2> get_mouse_passthrough_polygon() const;
void set_wrap_controls(bool p_enable);
bool is_wrapping_controls() const;
void child_controls_changed();
Window *get_exclusive_child() const { return exclusive_child; };
Window *get_parent_visible_window() const;
Viewport *get_parent_viewport() const;
virtual void popup(const Rect2i &p_screen_rect = Rect2i());
void popup_on_parent(const Rect2i &p_parent_rect);
void popup_centered(const Size2i &p_minsize = Size2i());
void popup_centered_ratio(float p_ratio = 0.8);
void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
void popup_exclusive(Node *p_from_node, const Rect2i &p_screen_rect = Rect2i());
void popup_exclusive_on_parent(Node *p_from_node, const Rect2i &p_parent_rect);
void popup_exclusive_centered(Node *p_from_node, const Size2i &p_minsize = Size2i());
void popup_exclusive_centered_ratio(Node *p_from_node, float p_ratio = 0.8);
void popup_exclusive_centered_clamped(Node *p_from_node, const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
Rect2i fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) const;
Size2 get_contents_minimum_size() const;
Size2 get_clamped_minimum_size() const;
void grab_focus();
bool has_focus() const;
Rect2i get_usable_parent_rect() const;
// Internationalization.
void set_layout_direction(LayoutDirection p_direction);
LayoutDirection get_layout_direction() const;
bool is_layout_rtl() const;
#ifndef DISABLE_DEPRECATED
void set_auto_translate(bool p_enable);
bool is_auto_translating() const;
#endif
// Theming.
void set_theme_owner_node(Node *p_node);
Node *get_theme_owner_node() const;
bool has_theme_owner_node() const;
void set_theme_context(ThemeContext *p_context, bool p_propagate = true);
void set_theme(const Ref<Theme> &p_theme);
Ref<Theme> get_theme() const;
void set_theme_type_variation(const StringName &p_theme_type);
StringName get_theme_type_variation() const;
void begin_bulk_theme_override();
void end_bulk_theme_override();
void add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon);
void add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style);
void add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font);
void add_theme_font_size_override(const StringName &p_name, int p_font_size);
void add_theme_color_override(const StringName &p_name, const Color &p_color);
void add_theme_constant_override(const StringName &p_name, int p_constant);
void remove_theme_icon_override(const StringName &p_name);
void remove_theme_style_override(const StringName &p_name);
void remove_theme_font_override(const StringName &p_name);
void remove_theme_font_size_override(const StringName &p_name);
void remove_theme_color_override(const StringName &p_name);
void remove_theme_constant_override(const StringName &p_name);
Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
int get_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const;
#ifdef TOOLS_ENABLED
Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const;
#endif
bool has_theme_icon_override(const StringName &p_name) const;
bool has_theme_stylebox_override(const StringName &p_name) const;
bool has_theme_font_override(const StringName &p_name) const;
bool has_theme_font_size_override(const StringName &p_name) const;
bool has_theme_color_override(const StringName &p_name) const;
bool has_theme_constant_override(const StringName &p_name) const;
bool has_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
float get_theme_default_base_scale() const;
Ref<Font> get_theme_default_font() const;
int get_theme_default_font_size() const;
//
virtual Transform2D get_final_transform() const override;
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
virtual bool is_directly_attached_to_screen() const override;
virtual bool is_attached_in_viewport() const override;
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
virtual Size2 _get_contents_minimum_size() const;
Window();
~Window();
};
VARIANT_ENUM_CAST(Window::Mode);
VARIANT_ENUM_CAST(Window::Flags);
VARIANT_ENUM_CAST(Window::ContentScaleMode);
VARIANT_ENUM_CAST(Window::ContentScaleAspect);
VARIANT_ENUM_CAST(Window::ContentScaleStretch);
VARIANT_ENUM_CAST(Window::LayoutDirection);
VARIANT_ENUM_CAST(Window::WindowInitialPosition);
#endif // WINDOW_H