summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/audio_stream_player_2d.cpp2
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/gradient_edit.cpp4
-rw-r--r--scene/gui/gradient_edit.h1
-rw-r--r--scene/gui/scroll_container.cpp28
-rw-r--r--scene/gui/video_stream_player.cpp2
-rw-r--r--scene/main/canvas_item.cpp15
-rw-r--r--scene/main/canvas_item.h2
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp17
-rw-r--r--scene/multiplayer/multiplayer_spawner.h3
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.cpp142
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.h25
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp25
-rw-r--r--scene/multiplayer/scene_cache_interface.h3
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp210
-rw-r--r--scene/multiplayer/scene_replication_interface.h7
-rw-r--r--scene/multiplayer/scene_replication_state.cpp81
-rw-r--r--scene/multiplayer/scene_replication_state.h23
-rw-r--r--scene/multiplayer/scene_rpc_interface.cpp6
-rw-r--r--scene/register_scene_types.cpp17
-rw-r--r--scene/resources/audio_stream_sample.cpp6
-rw-r--r--scene/resources/audio_stream_sample.h4
-rw-r--r--scene/resources/resource_format_text.cpp172
-rw-r--r--scene/resources/resource_format_text.h6
-rw-r--r--scene/resources/shader.cpp41
-rw-r--r--scene/resources/shader.h6
-rw-r--r--scene/resources/shader_include.cpp144
-rw-r--r--scene/resources/shader_include.h71
-rw-r--r--scene/resources/text_line.cpp2
-rw-r--r--scene/resources/text_paragraph.cpp4
-rw-r--r--scene/resources/texture.cpp1
-rw-r--r--scene/resources/video_stream.h2
36 files changed, 915 insertions, 167 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index eaab58c4ae..94d22111ea 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -69,7 +69,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {
if (setplay.get() >= 0 && stream.is_valid()) {
active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
stream_playbacks.push_back(new_playback);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 0ac94b9d45..012bf01df9 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -130,7 +130,7 @@ public:
}
String to_string() const {
- return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priotity:%d}", base_cell_coords, bit, terrain, priority);
+ return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority);
}
Vector2i get_base_cell_coords() const {
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 824ea0407e..65b00742ee 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -281,7 +281,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
if (setplay.get() >= 0 && stream.is_valid()) {
active.set();
- Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
HashMap<StringName, Vector<AudioFrame>> bus_map;
bus_map[_get_actual_bus()] = volume_vector;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index efb647af29..04debcab05 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -136,7 +136,7 @@ void AudioStreamPlayer::play(float p_from_pos) {
if (stream->is_monophonic() && is_playing()) {
stop();
}
- Ref<AudioStreamPlayback> stream_playback = stream->instance_playback();
+ Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 22f968eac7..8968c1cc17 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -407,7 +407,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
/* Ctrl + Hover symbols */
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
if (k->get_keycode() == Key::META) {
#else
if (k->get_keycode() == Key::CTRL) {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 20fcdf1136..686045901c 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -3300,7 +3300,7 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
- const String anchors_presets_options = "Custom:-1,PresetWide:15,"
+ const String anchors_presets_options = "Custom:-1,PresetFullRect:15,"
"PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2,"
"PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8,"
"PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14";
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 9459bed63b..cc27a6b7c2 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -437,6 +437,10 @@ ColorPicker *GradientEdit::get_picker() {
return picker;
}
+PopupPanel *GradientEdit::get_popup() {
+ return popup;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 3badcd45ba..b7c99f1f1c 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -75,6 +75,7 @@ public:
void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
Gradient::InterpolationMode get_interpolation_mode();
ColorPicker *get_picker();
+ PopupPanel *get_popup();
virtual Size2 get_minimum_size() const override;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 871cc520e9..9efab27e3a 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -37,10 +37,11 @@
Size2 ScrollContainer::get_minimum_size() const {
Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
Size2 min_size;
+ Size2 content_min_size;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
+ if (!c || !c->is_visible()) {
continue;
}
if (c->is_set_as_top_level()) {
@@ -51,20 +52,26 @@ Size2 ScrollContainer::get_minimum_size() const {
}
Size2 minsize = c->get_combined_minimum_size();
- if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
- min_size.x = MAX(min_size.x, minsize.x);
- }
- if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
- min_size.y = MAX(min_size.y, minsize.y);
- }
+ content_min_size.x = MAX(content_min_size.x, minsize.x);
+ content_min_size.y = MAX(content_min_size.y, minsize.y);
+ }
+
+ if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
+ min_size.x = MAX(min_size.x, content_min_size.x);
+ }
+ if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
+ min_size.y = MAX(min_size.y, content_min_size.y);
}
- if (h_scroll->is_visible_in_tree()) {
+ bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && content_min_size.x > min_size.x);
+ bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && content_min_size.y > min_size.y);
+ if (h_scroll_show) {
min_size.y += h_scroll->get_minimum_size().y;
}
- if (v_scroll->is_visible_in_tree()) {
+ if (v_scroll_show) {
min_size.x += v_scroll->get_minimum_size().x;
}
+
min_size += sb->get_minimum_size();
return min_size;
}
@@ -274,7 +281,7 @@ void ScrollContainer::_update_dimensions() {
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
+ if (!c || !c->is_visible()) {
continue;
}
if (c->is_set_as_top_level()) {
@@ -312,6 +319,7 @@ void ScrollContainer::_update_dimensions() {
fit_child_in_rect(c, r);
}
+ update_scrollbars();
update();
}
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 86334882fa..f20a2ad67b 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -225,7 +225,7 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stream = p_stream;
if (stream.is_valid()) {
stream->set_audio_track(audio_track);
- playback = stream->instance_playback();
+ playback = stream->instantiate_playback();
} else {
playback = Ref<VideoStreamPlayback>();
}
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 2cd7cf5648..e298805aca 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -230,16 +230,16 @@ void CanvasItem::_enter_canvas() {
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
- group = "root_canvas" + itos(canvas.get_id());
+ canvas_group = "root_canvas" + itos(canvas.get_id());
- add_to_group(group);
+ add_to_group(canvas_group);
if (canvas_layer) {
canvas_layer->reset_sort_index();
} else {
get_viewport()->gui_reset_canvas_sort_index();
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, SNAME("_top_level_raise_self"));
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self"));
} else {
CanvasItem *parent = get_parent_item();
@@ -258,7 +258,10 @@ void CanvasItem::_exit_canvas() {
notification(NOTIFICATION_EXIT_CANVAS, true); //reverse the notification
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, RID());
canvas_layer = nullptr;
- group = StringName();
+ if (canvas_group != StringName()) {
+ remove_from_group(canvas_group);
+ canvas_group = StringName();
+ }
}
void CanvasItem::_notification(int p_what) {
@@ -319,8 +322,8 @@ void CanvasItem::_notification(int p_what) {
break;
}
- if (group != StringName()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, "_top_level_raise_self");
+ if (canvas_group != StringName()) {
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self");
} else {
CanvasItem *p = get_parent_item();
ERR_FAIL_COND(!p);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index a4574dce61..f5df6512ee 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -70,7 +70,7 @@ private:
mutable SelfList<Node> xform_change;
RID canvas_item;
- StringName group;
+ StringName canvas_group;
CanvasLayer *canvas_layer = nullptr;
diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp
index ddd01d0a43..8363d05e54 100644
--- a/scene/multiplayer/multiplayer_spawner.cpp
+++ b/scene/multiplayer/multiplayer_spawner.cpp
@@ -71,7 +71,7 @@ bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const {
}
void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Scenes,scenes/"));
+ p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/"));
List<String> exts;
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
String ext_hint;
@@ -144,10 +144,6 @@ void MultiplayerSpawner::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
- ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning);
- ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning");
-
GDVIRTUAL_BIND(_spawn_custom, "data");
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
@@ -169,7 +165,7 @@ void MultiplayerSpawner::_update_spawn_node() {
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
if (node) {
spawn_node = node->get_instance_id();
- if (auto_spawn) {
+ if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
}
} else {
@@ -221,15 +217,6 @@ void MultiplayerSpawner::_node_added(Node *p_node) {
_track(p_node, Variant(), id);
}
-void MultiplayerSpawner::set_auto_spawning(bool p_enabled) {
- auto_spawn = p_enabled;
- _update_spawn_node();
-}
-
-bool MultiplayerSpawner::is_auto_spawning() const {
- return auto_spawn;
-}
-
NodePath MultiplayerSpawner::get_spawn_path() const {
return spawn_path;
}
diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h
index e8abe702a0..2c0eb9a2f0 100644
--- a/scene/multiplayer/multiplayer_spawner.h
+++ b/scene/multiplayer/multiplayer_spawner.h
@@ -69,7 +69,6 @@ private:
ObjectID spawn_node;
HashMap<ObjectID, SpawnInfo> tracked_nodes;
- bool auto_spawn = false;
uint32_t spawn_limit = 0;
void _update_spawn_node();
@@ -102,8 +101,6 @@ public:
void set_spawn_path(const NodePath &p_path);
uint32_t get_spawn_limit() const { return spawn_limit; }
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
- bool is_auto_spawning() const;
- void set_auto_spawning(bool p_enabled);
const Variant get_spawn_argument(const ObjectID &p_id) const;
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp
index 68f6e54fa8..e1b7433968 100644
--- a/scene/multiplayer/multiplayer_synchronizer.cpp
+++ b/scene/multiplayer/multiplayer_synchronizer.cpp
@@ -43,6 +43,11 @@ Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath
}
void MultiplayerSynchronizer::_stop() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->replication_stop(node, this);
@@ -50,9 +55,42 @@ void MultiplayerSynchronizer::_stop() {
}
void MultiplayerSynchronizer::_start() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->replication_start(node, this);
+ _update_process();
+ }
+}
+
+void MultiplayerSynchronizer::_update_process() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (!node) {
+ return;
+ }
+ set_process_internal(false);
+ set_physics_process_internal(false);
+ if (!visibility_filters.size()) {
+ return;
+ }
+ switch (visibility_update_mode) {
+ case VISIBILITY_PROCESS_IDLE:
+ set_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_PHYSICS:
+ set_physics_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_NONE:
+ break;
}
}
@@ -85,6 +123,66 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj
return OK;
}
+bool MultiplayerSynchronizer::is_visibility_public() const {
+ return peer_visibility.has(0);
+}
+
+void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
+ set_visibility_for(0, p_visible);
+}
+
+bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
+ if (visibility_filters.size()) {
+ Variant arg = p_peer;
+ const Variant *argv[1] = { &arg };
+ for (Callable filter : visibility_filters) {
+ Variant ret;
+ Callable::CallError err;
+ filter.call(argv, 1, ret, err);
+ ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
+ if (!ret.operator bool()) {
+ return false;
+ }
+ }
+ }
+ return peer_visibility.has(0) || peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
+ visibility_filters.insert(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
+ visibility_filters.erase(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
+ if (peer_visibility.has(p_peer) == p_visible) {
+ return;
+ }
+ if (p_visible) {
+ peer_visibility.insert(p_peer);
+ } else {
+ peer_visibility.erase(p_peer);
+ }
+ update_visibility(p_peer);
+}
+
+bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
+ return peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
+ visibility_update_mode = p_mode;
+ _update_process();
+}
+
+MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
+ return visibility_update_mode;
+}
+
void MultiplayerSynchronizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
@@ -95,9 +193,29 @@ void MultiplayerSynchronizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
+ ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
+ ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
+
+ ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
+ ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
+ ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
+ ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
+
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
+
+ ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
}
void MultiplayerSynchronizer::_notification(int p_what) {
@@ -118,6 +236,11 @@ void MultiplayerSynchronizer::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
_stop();
} break;
+
+ case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ update_visibility(0);
+ } break;
}
}
@@ -142,6 +265,18 @@ Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
return replication_config;
}
+void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
+ emit_signal(SNAME("visibility_changed"), p_for_peer);
+ }
+}
+
void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
_stop();
root_path = p_path;
@@ -162,3 +297,8 @@ void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_re
Node::set_multiplayer_authority(p_peer_id, p_recursive);
get_multiplayer()->replication_start(node, this);
}
+
+MultiplayerSynchronizer::MultiplayerSynchronizer() {
+ // Publicly visible by default.
+ peer_visibility.insert(0);
+}
diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h
index f61ef459da..59f02b84c1 100644
--- a/scene/multiplayer/multiplayer_synchronizer.h
+++ b/scene/multiplayer/multiplayer_synchronizer.h
@@ -38,14 +38,25 @@
class MultiplayerSynchronizer : public Node {
GDCLASS(MultiplayerSynchronizer, Node);
+public:
+ enum VisibilityUpdateMode {
+ VISIBILITY_PROCESS_IDLE,
+ VISIBILITY_PROCESS_PHYSICS,
+ VISIBILITY_PROCESS_NONE,
+ };
+
private:
Ref<SceneReplicationConfig> replication_config;
NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
uint64_t interval_msec = 0;
+ VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
+ HashSet<Callable> visibility_filters;
+ HashSet<int> peer_visibility;
static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
void _start();
void _stop();
+ void _update_process();
protected:
static void _bind_methods();
@@ -66,7 +77,19 @@ public:
NodePath get_root_path() const;
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
- MultiplayerSynchronizer() {}
+ bool is_visibility_public() const;
+ void set_visibility_public(bool p_public);
+ bool is_visible_to(int p_peer);
+ void set_visibility_for(int p_peer, bool p_visible);
+ bool get_visibility_for(int p_peer) const;
+ void update_visibility(int p_for_peer);
+ void set_visibility_update_mode(VisibilityUpdateMode p_mode);
+ void add_visibility_filter(Callable p_callback);
+ void remove_visibility_filter(Callable p_callback);
+ VisibilityUpdateMode get_visibility_update_mode() const;
+
+ MultiplayerSynchronizer();
};
+VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
index 7c271341db..79a7dc2d5a 100644
--- a/scene/multiplayer/scene_cache_interface.cpp
+++ b/scene/multiplayer/scene_cache_interface.cpp
@@ -187,18 +187,29 @@ bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
return F->value;
}
-bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
+int SceneCacheInterface::make_object_cache(Object *p_obj) {
Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, false);
+ ERR_FAIL_COND_V(!node, -1);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
// See if the path is cached.
- PathSentCache *psc = path_send_cache.getptr(p_path);
+ PathSentCache *psc = path_send_cache.getptr(for_path);
if (!psc) {
// Path is not cached, create.
- path_send_cache[p_path] = PathSentCache();
- psc = path_send_cache.getptr(p_path);
+ path_send_cache[for_path] = PathSentCache();
+ psc = path_send_cache.getptr(for_path);
psc->id = last_send_cache_id++;
}
- r_id = psc->id;
+ return psc->id;
+}
+
+bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, false);
+
+ r_id = make_object_cache(p_obj);
+ ERR_FAIL_COND_V(r_id < 0, false);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
+ PathSentCache *psc = path_send_cache.getptr(for_path);
bool has_all_peers = true;
List<int> peers_to_add; // If one is missing, take note to add it.
@@ -233,7 +244,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int
}
if (peers_to_add.size()) {
- _send_confirm_path(node, p_path, psc, peers_to_add);
+ _send_confirm_path(node, for_path, psc, peers_to_add);
}
return has_all_peers;
diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h
index 3116233b5b..6bfd683cf4 100644
--- a/scene/multiplayer/scene_cache_interface.h
+++ b/scene/multiplayer/scene_cache_interface.h
@@ -72,7 +72,8 @@ public:
virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
// Returns true if all peers have cached path.
- virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) override;
+ virtual bool send_object_cache(Object *p_obj, int p_target, int &p_id) override;
+ virtual int make_object_cache(Object *p_obj) override;
virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override;
virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override;
diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp
index e4715ceb88..c616c5bb85 100644
--- a/scene/multiplayer/scene_replication_interface.cpp
+++ b/scene/multiplayer/scene_replication_interface.cpp
@@ -60,14 +60,13 @@ void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
if (p_connected) {
rep_state->on_peer_change(p_id, p_connected);
for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
- _send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id);
+ _update_spawn_visibility(p_id, oid);
}
- for (const ObjectID &oid : rep_state->get_path_only_nodes()) {
- Node *node = rep_state->get_node(oid);
+ for (const ObjectID &oid : rep_state->get_synced_nodes()) {
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!node || !sync);
+ ERR_CONTINUE(!sync); // ERR_BUG
if (sync->is_multiplayer_authority()) {
- rep_state->peer_add_node(p_id, oid);
+ _update_sync_visibility(p_id, oid);
}
}
} else {
@@ -97,7 +96,13 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
Error err = rep_state->config_add_spawn(node, spawner);
ERR_FAIL_COND_V(err != OK, err);
- return _send_spawn(node, spawner, 0);
+ const ObjectID oid = node->get_instance_id();
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ rep_state->ensure_net_id(oid);
+ _update_spawn_visibility(0, oid);
+ }
+ ERR_FAIL_COND_V(err != OK, err);
+ return OK;
}
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
@@ -105,9 +110,19 @@ Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER);
- Error err = rep_state->config_del_spawn(node, spawner);
- ERR_FAIL_COND_V(err != OK, err);
- return _send_despawn(node, 0);
+ // Forcibly despawn to all peers that knowns me.
+ int len = 0;
+ Error err = _make_despawn_packet(node, len);
+ ERR_FAIL_COND_V(err != OK, ERR_BUG);
+ const ObjectID oid = p_obj->get_instance_id();
+ for (int pid : rep_state->get_peers()) {
+ if (!rep_state->is_peer_spawn(pid, oid)) {
+ continue;
+ }
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ // Also remove spawner tracking from the replication state.
+ return rep_state->config_del_spawn(node, spawner);
}
Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
@@ -115,7 +130,15 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+
+ // Add to synchronizer list and setup visibility.
rep_state->config_add_sync(node, sync);
+ const ObjectID oid = node->get_instance_id();
+ sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed), varray(oid));
+ if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
+ _update_sync_visibility(0, oid);
+ }
+
// Try to apply initial state if spawning (hack to apply if before ready).
if (pending_spawn == p_obj->get_instance_id()) {
pending_spawn = ObjectID(); // Make sure this only happens once.
@@ -127,9 +150,6 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
ERR_FAIL_COND_V(err, err);
err = MultiplayerSynchronizer::set_state(props, node, vars);
ERR_FAIL_COND_V(err, err);
- } else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
- // Either it's a spawn or a static sync, in any case add it to the list of known nodes.
- rep_state->peer_add_node(0, p_obj->get_instance_id());
}
return OK;
}
@@ -138,10 +158,103 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
Node *node = Object::cast_to<Node>(p_obj);
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!p_obj || !sync, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+ sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
return rep_state->config_del_sync(node, sync);
}
+void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) {
+ if (rep_state->is_spawned_node(p_oid)) {
+ _update_spawn_visibility(p_peer, p_oid);
+ }
+ if (rep_state->is_synced_node(p_oid)) {
+ _update_sync_visibility(p_peer, p_oid);
+ }
+}
+
+Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = sync->is_visible_to(p_peer);
+ if (p_peer == 0) {
+ for (int pid : rep_state->get_peers()) {
+ // Might be visible to this specific peer.
+ is_visible = is_visible || sync->is_visible_to(pid);
+ if (rep_state->is_peer_sync(pid, p_oid) == is_visible) {
+ continue;
+ }
+ if (is_visible) {
+ rep_state->peer_add_sync(pid, p_oid);
+ } else {
+ rep_state->peer_del_sync(pid, p_oid);
+ }
+ }
+ return OK;
+ } else {
+ if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ return rep_state->peer_add_sync(p_peer, p_oid);
+ } else {
+ return rep_state->peer_del_sync(p_peer, p_oid);
+ }
+ }
+}
+
+Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid);
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid));
+ ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = !sync || sync->is_visible_to(p_peer);
+ // Spawn (and despawn) when needed.
+ HashSet<int> to_spawn;
+ HashSet<int> to_despawn;
+ if (p_peer) {
+ if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ to_spawn.insert(p_peer);
+ } else {
+ to_despawn.insert(p_peer);
+ }
+ } else {
+ // Check visibility for each peers.
+ for (int pid : rep_state->get_peers()) {
+ bool peer_visible = is_visible || sync->is_visible_to(pid);
+ if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) {
+ continue;
+ }
+ if (peer_visible) {
+ to_spawn.insert(pid);
+ } else {
+ to_despawn.insert(pid);
+ }
+ }
+ }
+ if (to_spawn.size()) {
+ int len = 0;
+ _make_spawn_packet(node, len);
+ for (int pid : to_spawn) {
+ int path_id;
+ multiplayer->send_object_cache(spawner, pid, path_id);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ rep_state->peer_add_spawn(pid, p_oid);
+ }
+ }
+ if (to_despawn.size()) {
+ int len = 0;
+ _make_despawn_packet(node, len);
+ for (int pid : to_despawn) {
+ rep_state->peer_del_spawn(pid, p_oid);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ }
+ return OK;
+}
+
Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
@@ -158,18 +271,20 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
return peer->put_packet(p_buffer, p_size);
}
-Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) {
- ERR_FAIL_COND_V(p_peer < 0, ERR_BUG);
+Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
- ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG);
const ObjectID oid = p_node->get_instance_id();
- uint32_t nid = rep_state->ensure_net_id(oid);
+ MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
+ ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
+
+ uint32_t nid = rep_state->get_net_id(oid);
+ ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
// Prepare custom arg and scene_id
- uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid);
+ uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
- Variant spawn_arg = p_spawner->get_spawn_argument(oid);
+ Variant spawn_arg = spawner->get_spawn_argument(oid);
int spawn_arg_size = 0;
if (is_custom) {
Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
@@ -181,7 +296,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
Vector<Variant> state_vars;
Vector<const Variant *> state_varp;
MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
- if (synchronizer && synchronizer->get_replication_config().is_valid()) {
+ if (synchronizer) {
+ ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG);
const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
@@ -189,13 +305,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
}
- // Prepare simplified path.
- NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path());
-
- int path_id = 0;
- multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id);
-
- // Encode name and parent ID.
+ // Encode scene ID, path ID, net ID, node name.
+ int path_id = multiplayer->make_object_cache(spawner);
CharString cname = p_node->get_name().operator String().utf8();
int nlen = encode_cstring(cname.get_data(), nullptr);
MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
@@ -220,12 +331,11 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
ERR_FAIL_COND_V(err, err);
ofs += state_size;
}
- Error err = _send_raw(ptr, ofs, p_peer, true);
- ERR_FAIL_COND_V(err, err);
- return rep_state->peer_add_node(p_peer, oid);
+ r_len = ofs;
+ return OK;
}
-Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
+Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
const ObjectID oid = p_node->get_instance_id();
MAKE_ROOM(5);
uint8_t *ptr = packet_cache.ptrw();
@@ -233,9 +343,8 @@ Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
int ofs = 1;
uint32_t nid = rep_state->get_net_id(oid);
ofs += encode_uint32(nid, &ptr[ofs]);
- Error err = _send_raw(ptr, ofs, p_peer, true);
- ERR_FAIL_COND_V(err, err);
- return rep_state->peer_del_node(p_peer, oid);
+ r_len = ofs;
+ return OK;
}
Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
@@ -316,8 +425,8 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
}
void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
- const HashSet<ObjectID> &known = rep_state->get_known_nodes(p_peer);
- if (known.is_empty()) {
+ const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
+ if (to_sync.is_empty()) {
return;
}
MAKE_ROOM(sync_mtu);
@@ -327,14 +436,29 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
// Can only send updates for already notified nodes.
// This is a lazy implementation, we could optimize much more here with by grouping by replication config.
- for (const ObjectID &oid : known) {
+ for (const ObjectID &oid : to_sync) {
if (!rep_state->update_sync_time(oid, p_msec)) {
continue; // nothing to sync.
}
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!sync);
+ ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
Node *node = rep_state->get_node(oid);
ERR_CONTINUE(!node);
+ uint32_t net_id = rep_state->get_net_id(oid);
+ if (net_id == 0 || (net_id & 0x80000000)) {
+ int path_id = 0;
+ bool verified = multiplayer->send_object_cache(sync, p_peer, path_id);
+ ERR_CONTINUE_MSG(path_id < 0, "This should never happen!");
+ if (net_id == 0) {
+ // First time path based ID.
+ net_id = path_id | 0x80000000;
+ rep_state->set_net_id(oid, net_id | 0x80000000);
+ }
+ if (!verified) {
+ // The path based sync is not yet confirmed, skipping.
+ continue;
+ }
+ }
int size;
Vector<Variant> vars;
Vector<const Variant *> varp;
@@ -351,16 +475,6 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
ofs = 3;
}
if (size) {
- uint32_t net_id = rep_state->get_net_id(oid);
- if (net_id == 0 || (net_id & 0x80000000)) {
- // First time path based ID.
- NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path());
- int path_id = 0;
- multiplayer->send_object_cache(sync, rel_path, p_peer, path_id);
- ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!");
- net_id = path_id;
- rep_state->set_net_id(oid, net_id | 0x80000000);
- }
ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
ofs += encode_uint32(size, &ptr[ofs]);
MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h
index 60ac95c93c..ad3a3be979 100644
--- a/scene/multiplayer/scene_replication_interface.h
+++ b/scene/multiplayer/scene_replication_interface.h
@@ -40,10 +40,13 @@ class SceneReplicationInterface : public MultiplayerReplicationInterface {
private:
void _send_sync(int p_peer, uint64_t p_msec);
- Error _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer);
- Error _send_despawn(Node *p_node, int p_peer);
+ Error _make_spawn_packet(Node *p_node, int &r_len);
+ Error _make_despawn_packet(Node *p_node, int &r_len);
Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+ void _visibility_changed(int p_peer, ObjectID p_oid);
+ Error _update_sync_visibility(int p_peer, const ObjectID &p_oid);
+ Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
void _free_remotes(int p_peer);
Ref<SceneReplicationState> rep_state;
diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp
index 937b30cb36..f6a51ff9c7 100644
--- a/scene/multiplayer/scene_replication_state.cpp
+++ b/scene/multiplayer/scene_replication_state.cpp
@@ -56,7 +56,8 @@ void SceneReplicationState::_untrack(const ObjectID &p_id) {
// If we spawned or synced it, we need to remove it from any peer it was sent to.
if (net_id || peer == 0) {
for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.erase(p_id);
+ E.value.sync_nodes.erase(p_id);
+ E.value.spawn_nodes.erase(p_id);
}
}
}
@@ -93,11 +94,6 @@ bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_ms
return false;
}
-const HashSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
- return peers_info[p_peer].known_nodes;
-}
-
uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
const TrackedNode *tnode = tracked_nodes.getptr(p_id);
ERR_FAIL_COND_V(!tnode, 0);
@@ -147,8 +143,6 @@ Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
tobj.spawner = p_spawner->get_instance_id();
spawned_nodes.insert(oid);
- // The spawner may be notified after the synchronizer.
- path_only_nodes.erase(oid);
return OK;
}
@@ -159,6 +153,9 @@ Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *
ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
tobj.spawner = ObjectID();
spawned_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.spawn_nodes.erase(oid);
+ }
return OK;
}
@@ -167,10 +164,7 @@ Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchroniz
TrackedNode &tobj = _track(oid);
ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
tobj.synchronizer = p_sync->get_instance_id();
- // If it doesn't have a spawner, we might need to assign ID for this node using it's path.
- if (tobj.spawner.is_null()) {
- path_only_nodes.insert(oid);
- }
+ synced_nodes.insert(oid);
return OK;
}
@@ -180,38 +174,57 @@ Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchroniz
TrackedNode &tobj = _track(oid);
ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
tobj.synchronizer = ObjectID();
- if (path_only_nodes.has(oid)) {
- p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack));
- _untrack(oid);
- path_only_nodes.erase(oid);
+ synced_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.sync_nodes.erase(oid);
}
return OK;
}
-Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
- if (p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].known_nodes.insert(p_id);
- } else {
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.insert(p_id);
- }
- }
+Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.insert(p_id);
return OK;
}
-Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
- if (p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].known_nodes.erase(p_id);
- } else {
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.erase(p_id);
- }
- }
+Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.erase(p_id);
return OK;
}
+const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].sync_nodes;
+}
+
+bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].sync_nodes.has(p_id);
+}
+
+Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.insert(p_id);
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.erase(p_id);
+ return OK;
+}
+
+const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].spawn_nodes;
+}
+
+bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].spawn_nodes.has(p_id);
+}
+
Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
PeerInfo *info = peers_info.getptr(p_peer);
return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h
index 60a6c5d70c..7973b5c904 100644
--- a/scene/multiplayer/scene_replication_state.h
+++ b/scene/multiplayer/scene_replication_state.h
@@ -62,7 +62,8 @@ private:
};
struct PeerInfo {
- HashSet<ObjectID> known_nodes;
+ HashSet<ObjectID> sync_nodes;
+ HashSet<ObjectID> spawn_nodes;
HashMap<uint32_t, ObjectID> recv_nodes;
uint16_t last_sent_sync = 0;
uint16_t last_recv_sync = 0;
@@ -73,7 +74,7 @@ private:
HashMap<ObjectID, TrackedNode> tracked_nodes;
HashMap<int, PeerInfo> peers_info;
HashSet<ObjectID> spawned_nodes;
- HashSet<ObjectID> path_only_nodes;
+ HashSet<ObjectID> synced_nodes;
TrackedNode &_track(const ObjectID &p_id);
void _untrack(const ObjectID &p_id);
@@ -82,7 +83,9 @@ private:
public:
const HashSet<int> get_peers() const { return known_peers; }
const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
- const HashSet<ObjectID> &get_path_only_nodes() const { return path_only_nodes; }
+ bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); }
+ const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; }
+ bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); }
MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
@@ -90,7 +93,6 @@ public:
bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
- const HashSet<ObjectID> get_known_nodes(int p_peer);
uint32_t get_net_id(const ObjectID &p_id) const;
void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
uint32_t ensure_net_id(const ObjectID &p_id);
@@ -104,8 +106,17 @@ public:
Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
- Error peer_add_node(int p_peer, const ObjectID &p_id);
- Error peer_del_node(int p_peer, const ObjectID &p_id);
+ Error peer_add_sync(int p_peer, const ObjectID &p_id);
+ Error peer_del_sync(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_sync_nodes(int p_peer);
+ bool is_peer_sync(int p_peer, const ObjectID &p_id) const;
+
+ Error peer_add_spawn(int p_peer, const ObjectID &p_id);
+ Error peer_del_spawn(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer);
+ bool is_peer_spawn(int p_peer, const ObjectID &p_id) const;
const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
Node *peer_get_remote(int p_peer, uint32_t p_net_id);
diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp
index 84700a82f3..144a10c665 100644
--- a/scene/multiplayer/scene_rpc_interface.cpp
+++ b/scene/multiplayer/scene_rpc_interface.cpp
@@ -302,12 +302,9 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
}
- NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
- ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
-
// See if all peers have cached path (if so, call can be fast).
int psc_id;
- const bool has_all_peers = multiplayer->send_object_cache(p_from, from_path, p_to, psc_id);
+ const bool has_all_peers = multiplayer->send_object_cache(p_from, p_to, psc_id);
// Create base packet, lots of hardcode because it must be tight.
@@ -414,6 +411,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
// Not all verified path, so send one by one.
// Append path at the end, since we will need it for some packets.
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
CharString pname = String(from_path).utf8();
int path_len = encode_cstring(pname.get_data(), nullptr);
MAKE_ROOM(ofs + path_len);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index b1ef3d0f6f..09a283ea53 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -174,6 +174,7 @@
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/separation_ray_shape_2d.h"
#include "scene/resources/separation_ray_shape_3d.h"
+#include "scene/resources/shader_include.h"
#include "scene/resources/skeleton_modification_2d.h"
#include "scene/resources/skeleton_modification_2d_ccdik.h"
#include "scene/resources/skeleton_modification_2d_fabrik.h"
@@ -273,6 +274,9 @@ static Ref<ResourceFormatLoaderCompressedTexture3D> resource_loader_texture_3d;
static Ref<ResourceFormatSaverShader> resource_saver_shader;
static Ref<ResourceFormatLoaderShader> resource_loader_shader;
+static Ref<ResourceFormatSaverShaderInclude> resource_saver_shader_include;
+static Ref<ResourceFormatLoaderShaderInclude> resource_loader_shader_include;
+
void register_scene_types() {
SceneStringNames::create();
@@ -301,6 +305,12 @@ void register_scene_types() {
resource_loader_shader.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_shader, true);
+ resource_saver_shader_include.instantiate();
+ ResourceSaver::add_resource_format_saver(resource_saver_shader_include, true);
+
+ resource_loader_shader_include.instantiate();
+ ResourceLoader::add_resource_format_loader(resource_loader_shader_include, true);
+
OS::get_singleton()->yield(); // may take time to init
GDREGISTER_CLASS(Object);
@@ -569,6 +579,7 @@ void register_scene_types() {
GDREGISTER_CLASS(Shader);
GDREGISTER_CLASS(VisualShader);
+ GDREGISTER_CLASS(ShaderInclude);
GDREGISTER_ABSTRACT_CLASS(VisualShaderNode);
GDREGISTER_CLASS(VisualShaderNodeCustom);
GDREGISTER_CLASS(VisualShaderNodeInput);
@@ -1185,6 +1196,12 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_shader);
resource_loader_shader.unref();
+ ResourceSaver::remove_resource_format_saver(resource_saver_shader_include);
+ resource_saver_shader_include.unref();
+
+ ResourceLoader::remove_resource_format_loader(resource_loader_shader_include);
+ resource_loader_shader_include.unref();
+
// StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either
#ifndef _3D_DISABLED
BaseMaterial3D::finish_shaders();
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 30c222bdff..dcd36284d4 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -406,6 +406,10 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
return p_frames;
}
+void AudioStreamPlaybackSample::tag_used_streams() {
+ base->tag_used(get_playback_position());
+}
+
AudioStreamPlaybackSample::AudioStreamPlaybackSample() {}
/////////////////////
@@ -599,7 +603,7 @@ Error AudioStreamSample::save_to_wav(const String &p_path) {
return OK;
}
-Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamSample::instantiate_playback() {
Ref<AudioStreamPlaybackSample> sample;
sample.instantiate();
sample->base = Ref<AudioStreamSample>(this);
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 357febc27a..2e694cffe2 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -75,6 +75,8 @@ public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+ virtual void tag_used_streams() override;
+
AudioStreamPlaybackSample();
};
@@ -144,7 +146,7 @@ public:
Error save_to_wav(const String &p_path);
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
AudioStreamSample();
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 66afb001fb..100e8ea7c6 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -65,14 +65,18 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia
return ERR_PARSE_ERROR;
}
- String unique_id = token.value;
+ if (p_data->no_placeholders) {
+ r_res.unref();
+ } else {
+ String unique_id = token.value;
- if (!p_data->resource_map.has(unique_id)) {
- r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file";
- return ERR_PARSE_ERROR;
- }
+ if (!p_data->resource_map.has(unique_id)) {
+ r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file";
+ return ERR_PARSE_ERROR;
+ }
- r_res = p_data->resource_map[unique_id];
+ r_res = p_data->resource_map[unique_id];
+ }
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
@@ -91,11 +95,15 @@ Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, Varia
return ERR_PARSE_ERROR;
}
- String id = token.value;
+ if (p_data->no_placeholders) {
+ r_res.unref();
+ } else {
+ String id = token.value;
- ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
- r_res = p_data->rev_external_resources[id];
+ r_res = p_data->rev_external_resources[id];
+ }
VariantParser::get_token(p_stream, token, line, r_err_str);
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
@@ -1066,7 +1074,7 @@ static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string,
p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
}
-Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_path) {
+Error ResourceLoaderText::save_as_binary(const String &p_path) {
if (error) {
return error;
}
@@ -1271,7 +1279,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
}
if (next_tag.name == "node") {
- //this is a node, must save one more!
+ // This is a node, must save one more!
if (!is_scene) {
error_text += "found the 'node' tag on a resource file!";
@@ -1346,6 +1354,126 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
return OK;
}
+Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) {
+ if (error) {
+ return error;
+ }
+
+ ignore_resource_parsing = true;
+
+ DummyReadData dummy_read;
+ dummy_read.no_placeholders = true;
+ VariantParser::ResourceParser rp;
+ rp.ext_func = _parse_ext_resource_dummys;
+ rp.sub_func = _parse_sub_resource_dummys;
+ rp.userdata = &dummy_read;
+
+ while (next_tag.name == "ext_resource") {
+ error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
+
+ if (error) {
+ _printerr();
+ return error;
+ }
+ }
+
+ while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
+ if (next_tag.name == "sub_resource") {
+ if (!next_tag.fields.has("type")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'type' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ r_classes->insert(next_tag.fields["type"]);
+
+ } else {
+ r_classes->insert(next_tag.fields["res_type"]);
+ }
+
+ while (true) {
+ String assign;
+ Variant value;
+
+ error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
+
+ if (error) {
+ if (error == ERR_FILE_EOF) {
+ return OK;
+ }
+
+ _printerr();
+ return error;
+ }
+
+ if (!assign.is_empty()) {
+ continue;
+ } else if (!next_tag.name.is_empty()) {
+ error = OK;
+ break;
+ } else {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Premature end of file while parsing [sub_resource]";
+ _printerr();
+ return error;
+ }
+ }
+ }
+
+ while (next_tag.name == "node") {
+ // This is a node, must save one more!
+
+ if (!is_scene) {
+ error_text += "found the 'node' tag on a resource file!";
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
+
+ if (!next_tag.fields.has("type")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'type' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ r_classes->insert(next_tag.fields["type"]);
+
+ while (true) {
+ String assign;
+ Variant value;
+
+ error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
+
+ if (error) {
+ if (error == ERR_FILE_MISSING_DEPENDENCIES) {
+ // Resource loading error, just skip it.
+ } else if (error != ERR_FILE_EOF) {
+ _printerr();
+ return error;
+ } else {
+ return OK;
+ }
+ }
+
+ if (!assign.is_empty()) {
+ continue;
+ } else if (!next_tag.name.is_empty()) {
+ error = OK;
+ break;
+ } else {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Premature end of file while parsing [sub_resource]";
+ _printerr();
+ return error;
+ }
+ }
+ }
+
+ return OK;
+}
+
String ResourceLoaderText::recognize(Ref<FileAccess> p_f) {
error = OK;
@@ -1473,6 +1601,26 @@ bool ResourceFormatLoaderText::handles_type(const String &p_type) const {
return true;
}
+void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
+ String ext = p_path.get_extension().to_lower();
+ if (ext == "tscn") {
+ r_classes->insert("PackedScene");
+ }
+
+ // ...for anything else must test...
+
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ return; // Could not read.
+ }
+
+ ResourceLoaderText loader;
+ loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ loader.res_path = loader.local_path;
+ loader.open(f);
+ loader.get_classes_used(r_classes);
+}
+
String ResourceFormatLoaderText::get_resource_type(const String &p_path) const {
String ext = p_path.get_extension().to_lower();
if (ext == "tscn") {
@@ -1561,7 +1709,7 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.res_path = loader.local_path;
loader.open(f);
- return loader.save_as_binary(f, p_dst_path);
+ return loader.save_as_binary(p_dst_path);
}
/*****************************************************************************************************/
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 5c6a937bf2..69bb40502f 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -90,6 +90,7 @@ class ResourceLoaderText {
};
struct DummyReadData {
+ bool no_placeholders = false;
HashMap<Ref<Resource>, int> external_resources;
HashMap<String, Ref<Resource>> rev_external_resources;
HashMap<Ref<Resource>, int> resource_index_map;
@@ -125,8 +126,9 @@ public:
ResourceUID::ID get_uid(Ref<FileAccess> p_f);
void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map);
+ Error get_classes_used(HashSet<StringName> *r_classes);
- Error save_as_binary(Ref<FileAccess> p_f, const String &p_path);
+ Error save_as_binary(const String &p_path);
ResourceLoaderText();
};
@@ -137,6 +139,8 @@ public:
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
+ virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
+
virtual String get_resource_type(const String &p_path) const;
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index d49157b1b8..16117986fe 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -33,6 +33,7 @@
#include "core/io/file_access.h"
#include "scene/scene_string_names.h"
#include "servers/rendering/shader_language.h"
+#include "servers/rendering/shader_preprocessor.h"
#include "servers/rendering_server.h"
#include "texture.h"
@@ -40,7 +41,23 @@ Shader::Mode Shader::get_mode() const {
return mode;
}
+void Shader::_dependency_changed() {
+ RenderingServer::get_singleton()->shader_set_code(shader, RenderingServer::get_singleton()->shader_get_code(shader));
+ params_cache_dirty = true;
+
+ emit_changed();
+}
+
+void Shader::set_path(const String &p_path, bool p_take_over) {
+ Resource::set_path(p_path, p_take_over);
+ RS::get_singleton()->shader_set_path_hint(shader, p_path);
+}
+
void Shader::set_code(const String &p_code) {
+ for (Ref<ShaderInclude> E : include_dependencies) {
+ E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed));
+ }
+
String type = ShaderLanguage::get_shader_type(p_code);
if (type == "canvas_item") {
@@ -55,7 +72,27 @@ void Shader::set_code(const String &p_code) {
mode = MODE_SPATIAL;
}
- RenderingServer::get_singleton()->shader_set_code(shader, p_code);
+ code = p_code;
+ String pp_code = p_code;
+
+ HashSet<Ref<ShaderInclude>> new_include_dependencies;
+
+ {
+ // Preprocessor must run here and not in the server because:
+ // 1) Need to keep track of include dependencies at resource level
+ // 2) Server does not do interaction with Resource filetypes, this is a scene level feature.
+ ShaderPreprocessor preprocessor;
+ preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_include_dependencies);
+ }
+
+ // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
+ include_dependencies = new_include_dependencies;
+
+ for (Ref<ShaderInclude> E : include_dependencies) {
+ E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed));
+ }
+
+ RenderingServer::get_singleton()->shader_set_code(shader, pp_code);
params_cache_dirty = true;
emit_changed();
@@ -63,7 +100,7 @@ void Shader::set_code(const String &p_code) {
String Shader::get_code() const {
_update_shader();
- return RenderingServer::get_singleton()->shader_get_code(shader);
+ return code;
}
void Shader::get_param_list(List<PropertyInfo> *p_params) const {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 11c9f60ce8..5de8ad5518 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -35,6 +35,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "scene/resources/texture.h"
+#include "shader_include.h"
class Shader : public Resource {
GDCLASS(Shader, Resource);
@@ -53,6 +54,8 @@ public:
private:
RID shader;
Mode mode = MODE_SPATIAL;
+ HashSet<Ref<ShaderInclude>> include_dependencies;
+ String code;
// hack the name of performance
// shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make
@@ -61,6 +64,7 @@ private:
mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param..
HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures;
+ void _dependency_changed();
virtual void _update_shader() const; //used for visual shader
protected:
static void _bind_methods();
@@ -69,6 +73,8 @@ public:
//void set_mode(Mode p_mode);
virtual Mode get_mode() const;
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
void set_code(const String &p_code);
String get_code() const;
diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp
new file mode 100644
index 0000000000..b819128af3
--- /dev/null
+++ b/scene/resources/shader_include.cpp
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* shader_include.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "shader_include.h"
+#include "servers/rendering/shader_language.h"
+#include "servers/rendering/shader_preprocessor.h"
+
+void ShaderInclude::_dependency_changed() {
+ emit_changed();
+}
+
+void ShaderInclude::set_code(const String &p_code) {
+ HashSet<Ref<ShaderInclude>> new_dependencies;
+ code = p_code;
+
+ for (Ref<ShaderInclude> E : dependencies) {
+ E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed));
+ }
+
+ {
+ String pp_code;
+ ShaderPreprocessor preprocessor;
+ preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_dependencies);
+ }
+
+ // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
+ dependencies = new_dependencies;
+
+ for (Ref<ShaderInclude> E : dependencies) {
+ E->connect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed));
+ }
+
+ emit_changed();
+}
+
+String ShaderInclude::get_code() const {
+ return code;
+}
+
+void ShaderInclude::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_code", "code"), &ShaderInclude::set_code);
+ ClassDB::bind_method(D_METHOD("get_code"), &ShaderInclude::get_code);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code");
+}
+
+// ResourceFormatLoaderShaderInclude
+
+Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ if (r_error) {
+ *r_error = ERR_FILE_CANT_OPEN;
+ }
+
+ Ref<ShaderInclude> shader_inc;
+ shader_inc.instantiate();
+
+ Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path);
+
+ String str;
+ str.parse_utf8((const char *)buffer.ptr(), buffer.size());
+
+ shader_inc->set_code(str);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return shader_inc;
+}
+
+void ResourceFormatLoaderShaderInclude::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("gdshaderinc");
+}
+
+bool ResourceFormatLoaderShaderInclude::handles_type(const String &p_type) const {
+ return (p_type == "ShaderInclude");
+}
+
+String ResourceFormatLoaderShaderInclude::get_resource_type(const String &p_path) const {
+ String extension = p_path.get_extension().to_lower();
+ if (extension == "gdshaderinc") {
+ return "ShaderInclude";
+ }
+ return "";
+}
+
+// ResourceFormatSaverShaderInclude
+
+Error ResourceFormatSaverShaderInclude::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ Ref<ShaderInclude> shader_inc = p_resource;
+ ERR_FAIL_COND_V(shader_inc.is_null(), ERR_INVALID_PARAMETER);
+
+ String source = shader_inc->get_code();
+
+ Error error;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &error);
+
+ ERR_FAIL_COND_V_MSG(error, error, "Cannot save shader include '" + p_path + "'.");
+
+ file->store_string(source);
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+void ResourceFormatSaverShaderInclude::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ const ShaderInclude *shader_inc = Object::cast_to<ShaderInclude>(*p_resource);
+ if (shader_inc != nullptr) {
+ p_extensions->push_back("gdshaderinc");
+ }
+}
+
+bool ResourceFormatSaverShaderInclude::recognize(const Ref<Resource> &p_resource) const {
+ return p_resource->get_class_name() == "ShaderInclude"; //only shader, not inherited
+}
diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h
new file mode 100644
index 0000000000..6f0deeef4e
--- /dev/null
+++ b/scene/resources/shader_include.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* shader_include.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SHADER_INCLUDE_H
+#define SHADER_INCLUDE_H
+
+#include "core/io/resource.h"
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
+#include "core/templates/hash_set.h"
+
+class ShaderInclude : public Resource {
+ GDCLASS(ShaderInclude, Resource);
+ OBJ_SAVE_TYPE(ShaderInclude);
+
+private:
+ String code;
+ HashSet<Ref<ShaderInclude>> dependencies;
+ void _dependency_changed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_code(const String &p_text);
+ String get_code() const;
+};
+
+class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader {
+public:
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+class ResourceFormatSaverShaderInclude : public ResourceFormatSaver {
+public:
+ virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+};
+
+#endif // SHADER_INCLUDE_H
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 4e7ec9315a..823d742d72 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -74,7 +74,7 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags);
ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justication,Trim Edge Spaces After Justication,Justify Only After Last Tab,Constrain Ellipsis"), "set_flags", "get_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_flags", "get_flags");
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextLine::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextLine::get_text_overrun_behavior);
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 2d9e0066e1..43d3f329fa 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -77,12 +77,12 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags);
ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bouns,Grapheme Bound,Adaptive"), "set_break_flags", "get_break_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive"), "set_break_flags", "get_break_flags");
ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags);
ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justication,Trim Edge Spaces After Justication,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags");
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 21ae62c92e..0aefe34f7d 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -323,6 +323,7 @@ void ImageTexture::_bind_methods() {
ClassDB::bind_static_method("ImageTexture", D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image);
ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format);
+ ClassDB::bind_method(D_METHOD("set_image", "image"), &ImageTexture::set_image);
ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update);
ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override);
}
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index 3e154d539b..e368ffc030 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -72,7 +72,7 @@ class VideoStream : public Resource {
public:
virtual void set_audio_track(int p_track) = 0;
- virtual Ref<VideoStreamPlayback> instance_playback() = 0;
+ virtual Ref<VideoStreamPlayback> instantiate_playback() = 0;
};
#endif