diff options
83 files changed, 1437 insertions, 659 deletions
diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 5c42213e61..eec9caf149 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -775,7 +775,7 @@ Basis::operator String() const { Quaternion Basis::get_quaternion() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() instead."); + ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors."); #endif /* Allow getting a quaternion from an unnormalized transform */ Basis m = *this; diff --git a/core/multiplayer/multiplayer.h b/core/multiplayer/multiplayer.h index 8ddad61dd0..00c81d3c9a 100644 --- a/core/multiplayer/multiplayer.h +++ b/core/multiplayer/multiplayer.h @@ -45,8 +45,8 @@ enum TransferMode { enum RPCMode { RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) - RPC_MODE_ANY, // Any peer can call this rpc() - RPC_MODE_AUTHORITY, // / Only the node's network authority (server by default) can call this rpc() + RPC_MODE_ANY, // Any peer can call this RPC + RPC_MODE_AUTHORITY, // / Only the node's multiplayer authority (server by default) can call this RPC }; struct RPCConfig { diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp index 1130fcf9cb..9543f77c1e 100644 --- a/core/multiplayer/multiplayer_api.cpp +++ b/core/multiplayer/multiplayer_api.cpp @@ -56,22 +56,22 @@ void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) { #endif void MultiplayerAPI::poll() { - if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { + if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { return; } - network_peer->poll(); + multiplayer_peer->poll(); - if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. + if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. return; } - while (network_peer->get_available_packet_count()) { - int sender = network_peer->get_packet_peer(); + while (multiplayer_peer->get_available_packet_count()) { + int sender = multiplayer_peer->get_packet_peer(); const uint8_t *packet; int len; - Error err = network_peer->get_packet(&packet, len); + Error err = multiplayer_peer->get_packet(&packet, len); if (err != OK) { ERR_PRINT("Error getting packet!"); break; // Something is wrong! @@ -81,11 +81,11 @@ void MultiplayerAPI::poll() { _process_packet(sender, packet, len); remote_sender_id = 0; - if (!network_peer.is_valid()) { + if (!multiplayer_peer.is_valid()) { break; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } - if (network_peer.is_valid() && network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { + if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { replicator->poll(); } } @@ -107,36 +107,36 @@ Node *MultiplayerAPI::get_root_node() { return root_node; } -void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) { - if (p_peer == network_peer) { +void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { + if (p_peer == multiplayer_peer) { return; // Nothing to do } ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied MultiplayerPeer must be connecting or connected."); - if (network_peer.is_valid()) { - network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); - network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); - network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); - network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); - network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); + if (multiplayer_peer.is_valid()) { + multiplayer_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + multiplayer_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); clear(); } - network_peer = p_peer; + multiplayer_peer = p_peer; - if (network_peer.is_valid()) { - network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); - network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); - network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); - network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); - network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); + if (multiplayer_peer.is_valid()) { + multiplayer_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + multiplayer_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + multiplayer_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); } } -Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const { - return network_peer; +Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { + return multiplayer_peer; } void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -221,10 +221,10 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, packet.write[1] = valid_rpc_checksum; encode_cstring(pname.get_data(), &packet.write[2]); - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - network_peer->set_target_peer(p_from); - network_peer->put_packet(packet.ptr(), packet.size()); + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + multiplayer_peer->set_target_peer(p_from); + multiplayer_peer->put_packet(packet.ptr(), packet.size()); } void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -300,10 +300,10 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC ofs += encode_cstring(path.get_data(), &packet.write[ofs]); for (int &E : peers_to_add) { - network_peer->set_target_peer(E); // To all of you. - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - network_peer->put_packet(packet.ptr(), packet.size()); + multiplayer_peer->set_target_peer(E); // To all of you. + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + multiplayer_peer->put_packet(packet.ptr(), packet.size()); psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. } @@ -470,10 +470,10 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); - if (is_network_server()) { + if (is_server()) { replicator->spawn_all(p_id); } - emit_signal(SNAME("network_peer_connected"), p_id); + emit_signal(SNAME("peer_connected"), p_id); } void MultiplayerAPI::_del_peer(int p_id) { @@ -488,7 +488,7 @@ void MultiplayerAPI::_del_peer(int p_id) { PathSentCache *psc = path_send_cache.getptr(E); psc->confirmed_peers.erase(p_id); } - emit_signal(SNAME("network_peer_disconnected"), p_id); + emit_signal(SNAME("peer_disconnected"), p_id); } void MultiplayerAPI::_connected_to_server() { @@ -505,8 +505,8 @@ void MultiplayerAPI::_server_disconnected() { Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer::TransferMode p_mode, int p_channel) { ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no multiplayer peer is active."); + ERR_FAIL_COND_V_MSG(multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a multiplayer peer which is not connected."); if (packet_cache.size() < p_data.size() + 1) { packet_cache.resize(p_data.size() + 1); @@ -516,11 +516,11 @@ Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer:: packet_cache.write[0] = NETWORK_COMMAND_RAW; memcpy(&packet_cache.write[1], &r[0], p_data.size()); - network_peer->set_target_peer(p_to); - network_peer->set_transfer_channel(p_channel); - network_peer->set_transfer_mode(p_mode); + multiplayer_peer->set_target_peer(p_to); + multiplayer_peer->set_transfer_channel(p_channel); + multiplayer_peer->set_transfer_mode(p_mode); - return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); + return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); } void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -533,7 +533,7 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac uint8_t *w = out.ptrw(); memcpy(&w[0], &p_packet[1], len); } - emit_signal(SNAME("network_peer_packet"), p_from, out); + emit_signal(SNAME("peer_packet"), p_from, out); } bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) { @@ -574,27 +574,27 @@ Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) { return node; } -int MultiplayerAPI::get_network_unique_id() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); - return network_peer->get_unique_id(); +int MultiplayerAPI::get_unique_id() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), 0, "No multiplayer peer is assigned. Unable to get unique ID."); + return multiplayer_peer->get_unique_id(); } -bool MultiplayerAPI::is_network_server() const { - return network_peer.is_valid() && network_peer->is_server(); +bool MultiplayerAPI::is_server() const { + return multiplayer_peer.is_valid() && multiplayer_peer->is_server(); } -void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); - network_peer->set_refuse_new_connections(p_refuse); +void MultiplayerAPI::set_refuse_new_connections(bool p_refuse) { + ERR_FAIL_COND_MSG(!multiplayer_peer.is_valid(), "No multiplayer peer is assigned. Unable to set 'refuse_new_connections'."); + multiplayer_peer->set_refuse_new_connections(p_refuse); } -bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); - return network_peer->is_refusing_new_connections(); +bool MultiplayerAPI::is_refusing_new_connections() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), false, "No multiplayer peer is assigned. Unable to get 'refuse_new_connections'."); + return multiplayer_peer->is_refusing_new_connections(); } -Vector<int> MultiplayerAPI::get_network_connected_peers() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); +Vector<int> MultiplayerAPI::get_peer_ids() const { + ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected."); Vector<int> ret; for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { @@ -624,32 +624,32 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); - ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); - ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); - ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); + 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("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); - ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); - ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); - ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); + ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids); + ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &MultiplayerAPI::set_refuse_new_connections); + ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections); ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); - ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); + ADD_PROPERTY_DEFAULT("refuse_new_connections", false); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); - ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); + ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h index acabda62e2..1fb0318403 100644 --- a/core/multiplayer/multiplayer_api.h +++ b/core/multiplayer/multiplayer_api.h @@ -82,7 +82,7 @@ private: Map<int, NodeInfo> nodes; }; - Ref<MultiplayerPeer> network_peer; + Ref<MultiplayerPeer> multiplayer_peer; Set<int> connected_peers; int remote_sender_id = 0; int remote_sender_override = 0; @@ -112,8 +112,8 @@ public: void clear(); void set_root_node(Node *p_node); Node *get_root_node(); - void set_network_peer(const Ref<MultiplayerPeer> &p_peer); - Ref<MultiplayerPeer> get_network_peer() const; + void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer); + Ref<MultiplayerPeer> get_multiplayer_peer() const; Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); @@ -135,15 +135,15 @@ public: void _connection_failed(); void _server_disconnected(); - bool has_network_peer() const { return network_peer.is_valid(); } - Vector<int> get_network_connected_peers() const; + bool has_multiplayer_peer() const { return multiplayer_peer.is_valid(); } + Vector<int> get_peer_ids() const; const Set<int> get_connected_peers() const { return connected_peers; } int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; } void set_remote_sender_override(int p_id) { remote_sender_override = p_id; } - int get_network_unique_id() const; - bool is_network_server() const; - void set_refuse_new_network_connections(bool p_refuse); - bool is_refusing_new_network_connections() const; + int get_unique_id() const; + bool is_server() const; + void set_refuse_new_connections(bool p_refuse); + bool is_refusing_new_connections() const; void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp index 0340e11288..17af2c5ef8 100644 --- a/core/multiplayer/multiplayer_replicator.cpp +++ b/core/multiplayer/multiplayer_replicator.cpp @@ -113,18 +113,18 @@ Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id ERR_CONTINUE(err); ofs += size; } - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - network_peer->set_target_peer(p_peer); - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE); - return network_peer->put_packet(ptr, ofs); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE); + return peer->put_packet(ptr, ofs); } void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) { ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received"); ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id)); SceneConfig &cfg = replications[p_id]; - ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_network_server(), "The defualt implementation only allows sync packets from the server"); + ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The defualt implementation only allows sync packets from the server"); const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG; int ofs = SYNC_CMD_OFFSET; int time = p_packet[ofs]; @@ -233,11 +233,11 @@ Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const Re memcpy(&ptr[ofs], pba.ptr(), state_len); } - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - network_peer->set_target_peer(p_peer_id); - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - return network_peer->put_packet(ptr, ofs + state_len); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + return peer->put_packet(ptr, ofs + state_len); } void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { @@ -513,21 +513,21 @@ Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUI } else if (data_size) { encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size); } - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - network_peer->set_target_peer(p_peer_id); - network_peer->set_transfer_channel(0); - network_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - return network_peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(0); + peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size); } Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { - ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); const SceneConfig &cfg = replications[p_scene_id]; if (cfg.on_spawn_despawn_send.is_valid()) { return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true); } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); + ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); NodePath path = p_path; Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; if (path.is_empty() && obj) { @@ -542,13 +542,13 @@ Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID & } Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) { - ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); const SceneConfig &cfg = replications[p_scene_id]; if (cfg.on_spawn_despawn_send.is_valid()) { return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false); } else { - ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); + ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests."); NodePath path = p_path; Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr; ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object."); @@ -619,7 +619,7 @@ Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Obj } void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { - if (!multiplayer->has_network_peer()) { + if (!multiplayer->has_multiplayer_peer()) { return; } Node *root_node = multiplayer->get_root_node(); @@ -634,14 +634,14 @@ void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node } const SceneConfig &cfg = replications[id]; if (p_enter) { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server()) { + if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) { replicated_nodes[p_node->get_instance_id()] = id; _track(id, p_node); spawn(id, p_node, 0); } emit_signal(SNAME("replicated_instance_added"), id, p_node); } else { - if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_network_server() && replicated_nodes.has(p_node->get_instance_id())) { + if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) { replicated_nodes.erase(p_node->get_instance_id()); _untrack(id, p_node); despawn(id, p_node, 0); @@ -666,7 +666,7 @@ void MultiplayerReplicator::poll() { if (!E.value.sync_interval) { continue; } - if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_network_server()) { + if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) { continue; } uint64_t time = OS::get_singleton()->get_ticks_usec(); @@ -741,7 +741,7 @@ Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_p } Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) { - ERR_FAIL_COND_V(!multiplayer->has_network_peer(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER); const SceneConfig &cfg = replications[p_scene_id]; ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions"); @@ -749,11 +749,11 @@ Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_s uint8_t *ptr = packet_cache.ptrw(); ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC; encode_uint64(p_scene_id, &ptr[1]); - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - network_peer->set_target_peer(p_peer_id); - network_peer->set_transfer_channel(p_channel); - network_peer->set_transfer_mode(p_transfer_mode); - return network_peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size()); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + peer->set_target_peer(p_peer_id); + peer->set_transfer_channel(p_channel); + peer->set_transfer_mode(p_transfer_mode); + return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size()); } void MultiplayerReplicator::clear() { diff --git a/core/multiplayer/rpc_manager.cpp b/core/multiplayer/rpc_manager.cpp index 135dc4a187..915571375e 100644 --- a/core/multiplayer/rpc_manager.cpp +++ b/core/multiplayer/rpc_manager.cpp @@ -103,7 +103,7 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int return true; } break; case Multiplayer::RPC_MODE_AUTHORITY: { - return !p_node->is_network_authority() && p_remote_id == p_node->get_network_authority(); + return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); } break; } @@ -232,7 +232,7 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int ERR_FAIL_COND(config.name == StringName()); bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_network_authority()) + "."); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + "."); int argc = 0; bool byte_only = false; @@ -294,19 +294,19 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int } void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); + ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); + ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected."); - ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); + ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)."); - if (p_to != 0 && !multiplayer->get_network_connected_peers().has(ABS(p_to))) { - ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + "."); + if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { + ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + "."); - ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + "."); + ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); } NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path()); @@ -416,13 +416,13 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult #endif // Take chance and set transfer mode, since all send methods will use it. - network_peer->set_transfer_channel(p_config.channel); - network_peer->set_transfer_mode(p_config.transfer_mode); + peer->set_transfer_channel(p_config.channel); + peer->set_transfer_mode(p_config.transfer_mode); if (has_all_peers) { // They all have verified paths, so send fast. - network_peer->set_target_peer(p_to); // To all of you. - network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. + peer->set_target_peer(p_to); // To all of you. + peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { // Unreachable because the node ID is never compressed if the peers doesn't know it. CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); @@ -446,28 +446,28 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult bool confirmed = multiplayer->is_cache_confirmed(from_path, P); - network_peer->set_target_peer(P); // To this one specifically. + peer->set_target_peer(P); // To this one specifically. if (confirmed) { // This one confirmed path, so use id. encode_uint32(psc_id, &(packet_cache.write[1])); - network_peer->put_packet(packet_cache.ptr(), ofs); + peer->put_packet(packet_cache.ptr(), ofs); } else { // This one did not confirm path yet, so use entire path (sorry!). encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. - network_peer->put_packet(packet_cache.ptr(), ofs + path_len); + peer->put_packet(packet_cache.ptr(), ofs + path_len); } } } } void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> network_peer = multiplayer->get_network_peer(); - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); + Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); + ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected."); - int node_id = network_peer->get_unique_id(); + int node_id = peer->get_unique_id(); bool call_local_native = false; bool call_local_script = false; uint16_t rpc_id = UINT16_MAX; @@ -493,7 +493,7 @@ void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, c if (call_local_native) { Callable::CallError ce; - multiplayer->set_remote_sender_override(network_peer->get_unique_id()); + multiplayer->set_remote_sender_override(peer->get_unique_id()); p_node->call(p_method, p_arg, p_argcount, ce); multiplayer->set_remote_sender_override(0); @@ -509,7 +509,7 @@ void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, c Callable::CallError ce; ce.error = Callable::CallError::CALL_OK; - multiplayer->set_remote_sender_override(network_peer->get_unique_id()); + multiplayer->set_remote_sender_override(peer->get_unique_id()); p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); multiplayer->set_remote_sender_override(0); diff --git a/core/string/ustring.h b/core/string/ustring.h index 6a4b40da60..24da6b82af 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -523,10 +523,10 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St #define TTRGET(m_value) TTR(m_value) #else -#define TTR(m_value) (String()) -#define TTRN(m_value) (String()) -#define DTR(m_value) (String()) -#define DTRN(m_value) (String()) +#define TTR(m_value) String() +#define TTRN(m_value) String() +#define DTR(m_value) String() +#define DTRN(m_value) String() #define TTRC(m_value) (m_value) #define TTRGET(m_value) (m_value) #endif diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 0300511eb1..755902c709 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2472,7 +2472,7 @@ Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc(any)[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. </constant> <constant name="RPC_MODE_AUTH" value="2" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely only by the current network authority (which is the server by default). Analogous to the [code]@rpc(auth)[/code] annotation. See [method Node.set_network_authority]. + Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc(auth)[/code] annotation. See [method Node.set_multiplayer_authority]. </constant> <constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode"> Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [constant TRANSFER_MODE_ORDERED]. Use for non-critical data, and always consider whether the order matters. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 5392189f6a..6602764cd4 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -921,7 +921,8 @@ Anchors the top edge of the node to the origin, the center or the end of its parent control. It changes how the top offset updates when the node moves or changes size. You can use one of the [enum Anchor] constants for convenience. </member> <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true"> - Toggles if any text should automatically change to its translated version depending on the current locale. + Toggles if any text should automatically change to its translated version depending on the current locale. Note that this will not affect any internal nodes (e.g. the popup of a [MenuButton]). + Also decides if the node's strings should be parsed for POT generation. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode" default="0"> The focus access mode for the control (None, Click or All). Only one Control can be focused at the same time, and it will receive keyboard signals. diff --git a/doc/classes/JNISingleton.xml b/doc/classes/JNISingleton.xml index 84ab1a49c1..fbf18ddc03 100644 --- a/doc/classes/JNISingleton.xml +++ b/doc/classes/JNISingleton.xml @@ -1,10 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="JNISingleton" inherits="Object" version="4.0"> <brief_description> + Singleton that connects the engine with Android plugins to interface with native Android code. </brief_description> <description> + The JNISingleton is implemented only in the Android export. It's used to call methods and connect signals from an Android plugin written in Java or Kotlin. Methods and signals can be called and connected to the JNISingleton as if it is a Node. See [url=https://en.wikipedia.org/wiki/Java_Native_Interface]Java Native Interface - Wikipedia[/url] for more information. </description> <tutorials> + <link title="Creating Android plugins">https://docs.godotengine.org/en/latest/tutorials/platform/android/android_plugin.html#doc-android-plugin</link> </tutorials> <methods> </methods> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index cd2f4eca18..52359b0ede 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -47,7 +47,8 @@ The light's strength multiplier (this is not a physical unit). For [OmniLight3D] and [SpotLight3D], changing this value will only change the light color's intensity, not the light's radius. </member> <member name="light_indirect_energy" type="float" setter="set_param" getter="get_param" default="1.0"> - Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI]. + Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). + [b]Note:[/b] This property is ignored if [member light_energy] is equal to [code]0.0[/code], as the light won't be present at all in the GI shader. </member> <member name="light_negative" type="bool" setter="set_negative" getter="is_negative" default="false"> If [code]true[/code], the light's effect is reversed, darkening areas and casting bright shadows. diff --git a/doc/classes/Listener2D.xml b/doc/classes/Listener2D.xml new file mode 100644 index 0000000000..27ee63d201 --- /dev/null +++ b/doc/classes/Listener2D.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="Listener2D" inherits="Node2D" version="4.0"> + <brief_description> + Overrides the location sounds are heard from. + </brief_description> + <description> + Once added to the scene tree and enabled using [method make_current], this node will override the location sounds are heard from. Only one [Listener2D] can be current. Using [method make_current] will disable the previous [Listener2D]. + If there is no active [Listener2D] in the current [Viewport], center of the screen will be used as a hearing point for the audio. [Listener2D] needs to be inside [SceneTree] to function. + </description> + <tutorials> + </tutorials> + <methods> + <method name="clear_current"> + <return type="void" /> + <description> + Disables the [Listener2D]. If it's not set as current, this method will have no effect. + </description> + </method> + <method name="is_current" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this [Listener2D] is currently active. + </description> + </method> + <method name="make_current"> + <return type="void" /> + <description> + Makes the [Listener2D] active, setting it as the hearing point for the sounds. If there is already another active [Listener2D], it will be disabled. + This method will have no effect if the [Listener2D] is not added to [SceneTree]. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/Listener3D.xml b/doc/classes/Listener3D.xml index 9cc803f241..5e1b2ce7fc 100644 --- a/doc/classes/Listener3D.xml +++ b/doc/classes/Listener3D.xml @@ -5,7 +5,6 @@ </brief_description> <description> Once added to the scene tree and enabled using [method make_current], this node will override the location sounds are heard from. This can be used to listen from a location different from the [Camera3D]. - [b]Note:[/b] There is no 2D equivalent for this node yet. </description> <tutorials> </tutorials> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 9c688f7b8b..647233f679 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -4,8 +4,8 @@ High-level multiplayer API. </brief_description> <description> - This class implements most of the logic behind the high-level multiplayer API. See also [MultiplayerPeer]. - By default, [SceneTree] has a reference to this class that is used to provide multiplayer capabilities (i.e. RPC/RSET) across the whole scene. + This class implements the high-level multiplayer API. See also [MultiplayerPeer]. + By default, [SceneTree] has a reference to this class that is used to provide multiplayer capabilities (i.e. RPCs) across the whole scene. It is possible to override the MultiplayerAPI instance used by specific Nodes by setting the [member Node.custom_multiplayer] property, effectively allowing to run both client and server in the same scene. [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. </description> @@ -18,42 +18,42 @@ Clears the current MultiplayerAPI network state (you shouldn't call this unless you know what you are doing). </description> </method> - <method name="get_network_connected_peers" qualifiers="const"> + <method name="get_peers" qualifiers="const"> <return type="PackedInt32Array" /> <description> - Returns the peer IDs of all connected peers of this MultiplayerAPI's [member network_peer]. + Returns the peer IDs of all connected peers of this MultiplayerAPI's [member multiplayer_peer]. </description> </method> - <method name="get_network_unique_id" qualifiers="const"> + <method name="get_remote_sender_id" qualifiers="const"> <return type="int" /> <description> - Returns the unique peer ID of this MultiplayerAPI's [member network_peer]. + Returns the sender's peer ID for the RPC currently being executed. + [b]Note:[/b] If not inside an RPC this method will return 0. </description> </method> - <method name="get_remote_sender_id" qualifiers="const"> + <method name="get_unique_id" qualifiers="const"> <return type="int" /> <description> - Returns the sender's peer ID for the RPC currently being executed. - [b]Note:[/b] If not inside an RPC this method will return 0. + Returns the unique peer ID of this MultiplayerAPI's [member multiplayer_peer]. </description> </method> - <method name="has_network_peer" qualifiers="const"> + <method name="has_multiplayer_peer" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if there is a [member network_peer] set. + Returns [code]true[/code] if there is a [member multiplayer_peer] set. </description> </method> - <method name="is_network_server" qualifiers="const"> + <method name="is_server" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if this MultiplayerAPI's [member network_peer] is in server mode (listening for connections). + Returns [code]true[/code] if this MultiplayerAPI's [member multiplayer_peer] is valid and in server mode (listening for connections). </description> </method> <method name="poll"> <return type="void" /> <description> Method used for polling the MultiplayerAPI. You only need to worry about this if you are using [member Node.custom_multiplayer] override or you set [member SceneTree.multiplayer_poll] to [code]false[/code]. By default, [SceneTree] will poll its MultiplayerAPI for you. - [b]Note:[/b] This method results in RPCs and RSETs being called, so they will be executed in the same context of this function (e.g. [code]_process[/code], [code]physics[/code], [Thread]). + [b]Note:[/b] This method results in RPCs being called, so they will be executed in the same context of this function (e.g. [code]_process[/code], [code]physics[/code], [Thread]). </description> </method> <method name="send_bytes"> @@ -69,14 +69,14 @@ </methods> <members> <member name="allow_object_decoding" type="bool" setter="set_allow_object_decoding" getter="is_object_decoding_allowed" default="false"> - If [code]true[/code], the MultiplayerAPI will allow encoding and decoding of object during RPCs/RSETs. + If [code]true[/code], the MultiplayerAPI will allow encoding and decoding of object during RPCs. [b]Warning:[/b] Deserialized objects can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats such as remote code execution. </member> - <member name="network_peer" type="MultiplayerPeer" setter="set_network_peer" getter="get_network_peer"> - The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_network_server]) and will set root node's network mode to authority, or it will become a regular client peer. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals. + <member name="multiplayer_peer" type="MultiplayerPeer" setter="set_multiplayer_peer" getter="get_multiplayer_peer"> + The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_server]) and will set root node's network mode to authority, or it will become a regular client peer. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals. </member> - <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections" default="false"> - If [code]true[/code], the MultiplayerAPI's [member network_peer] refuses new incoming connections. + <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false"> + If [code]true[/code], the MultiplayerAPI's [member multiplayer_peer] refuses new incoming connections. </member> <member name="replicator" type="MultiplayerReplicator" setter="" getter="get_replicator"> </member> @@ -88,36 +88,36 @@ <signals> <signal name="connected_to_server"> <description> - Emitted when this MultiplayerAPI's [member network_peer] successfully connected to a server. Only emitted on clients. + Emitted when this MultiplayerAPI's [member multiplayer_peer] successfully connected to a server. Only emitted on clients. </description> </signal> <signal name="connection_failed"> <description> - Emitted when this MultiplayerAPI's [member network_peer] fails to establish a connection to a server. Only emitted on clients. + Emitted when this MultiplayerAPI's [member multiplayer_peer] fails to establish a connection to a server. Only emitted on clients. </description> </signal> - <signal name="network_peer_connected"> + <signal name="peer_connected"> <argument index="0" name="id" type="int" /> <description> - Emitted when this MultiplayerAPI's [member network_peer] connects with a new peer. ID is the peer ID of the new peer. Clients get notified when other clients connect to the same server. Upon connecting to a server, a client also receives this signal for the server (with ID being 1). + Emitted when this MultiplayerAPI's [member multiplayer_peer] connects with a new peer. ID is the peer ID of the new peer. Clients get notified when other clients connect to the same server. Upon connecting to a server, a client also receives this signal for the server (with ID being 1). </description> </signal> - <signal name="network_peer_disconnected"> + <signal name="peer_disconnected"> <argument index="0" name="id" type="int" /> <description> - Emitted when this MultiplayerAPI's [member network_peer] disconnects from a peer. Clients get notified when other clients disconnect from the same server. + Emitted when this MultiplayerAPI's [member multiplayer_peer] disconnects from a peer. Clients get notified when other clients disconnect from the same server. </description> </signal> - <signal name="network_peer_packet"> + <signal name="peer_packet"> <argument index="0" name="id" type="int" /> <argument index="1" name="packet" type="PackedByteArray" /> <description> - Emitted when this MultiplayerAPI's [member network_peer] receive a [code]packet[/code] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet. + Emitted when this MultiplayerAPI's [member multiplayer_peer] receives a [code]packet[/code] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet. </description> </signal> <signal name="server_disconnected"> <description> - Emitted when this MultiplayerAPI's [member network_peer] disconnects from server. Only emitted on clients. + Emitted when this MultiplayerAPI's [member multiplayer_peer] disconnects from server. Only emitted on clients. </description> </signal> </signals> diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 6ee6fce49f..411317cdc8 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -4,7 +4,7 @@ A high-level network interface to simplify multiplayer interactions. </brief_description> <description> - Manages the connection to network peers. Assigns unique IDs to each client connected to the server. See also [MultiplayerAPI]. + Manages the connection to multiplayer peers. Assigns unique IDs to each client connected to the server. See also [MultiplayerAPI]. [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. </description> <tutorials> diff --git a/doc/classes/MultiplayerReplicator.xml b/doc/classes/MultiplayerReplicator.xml index 2657f835c4..e0c309ef39 100644 --- a/doc/classes/MultiplayerReplicator.xml +++ b/doc/classes/MultiplayerReplicator.xml @@ -44,7 +44,7 @@ <argument index="2" name="data" type="Variant" default="null" /> <argument index="3" name="path" type="NodePath" default="NodePath("")" /> <description> - Sends a despawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_network_server]), the receiving peer(s) will automatically queue for deletion the node at [code]path[/code] and emit the signal [signal despawned]. In all other cases no deletion happens, and the signal [signal despawn_requested] is emitted instead. + Sends a despawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_server]), the receiving peer(s) will automatically queue for deletion the node at [code]path[/code] and emit the signal [signal despawned]. In all other cases no deletion happens, and the signal [signal despawn_requested] is emitted instead. </description> </method> <method name="send_spawn"> @@ -54,7 +54,7 @@ <argument index="2" name="data" type="Variant" default="null" /> <argument index="3" name="path" type="NodePath" default="NodePath("")" /> <description> - Sends a spawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_network_server]), the receiving peer(s) will automatically instantiate that scene, add it to the [SceneTree] at the given [code]path[/code] and emit the signal [signal spawned]. In all other cases no instantiation happens, and the signal [signal spawn_requested] is emitted instead. + Sends a spawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_server]), the receiving peer(s) will automatically instantiate that scene, add it to the [SceneTree] at the given [code]path[/code] and emit the signal [signal spawned]. In all other cases no instantiation happens, and the signal [signal spawn_requested] is emitted instead. </description> </method> <method name="send_sync"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 1f5823c37c..608d76cd9f 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -153,6 +153,7 @@ <description> Adds the node to a group. Groups are helpers to name and organize a subset of nodes, for example "enemies" or "collectables". A node can be in any number of groups. Nodes can be assigned a group at any time, but will not be added until they are inside the scene tree (see [method is_inside_tree]). See notes in the description, and the group methods in [SceneTree]. The [code]persistent[/code] option is used when packing node to [PackedScene] and saving to file. Non-persistent groups aren't stored. + [b]Note:[/b] For performance reasons, the order of node groups is [i]not[/i] guaranteed. The order of node groups should not be relied upon as it can vary across project runs. </description> </method> <method name="can_process" qualifiers="const"> @@ -236,6 +237,7 @@ <return type="Array" /> <description> Returns an array listing the groups that the node is a member of. + [b]Note:[/b] For performance reasons, the order of node groups is [i]not[/i] guaranteed. The order of node groups should not be relied upon as it can vary across project runs. </description> </method> <method name="get_index" qualifiers="const"> @@ -246,10 +248,10 @@ If [code]include_internal[/code] is [code]false[/code], the index won't take internal children into account, i.e. first non-internal child will have index of 0 (see [code]internal[/code] parameter in [method add_child]). </description> </method> - <method name="get_network_authority" qualifiers="const"> + <method name="get_multiplayer_authority" qualifiers="const"> <return type="int" /> <description> - Returns the peer ID of the network authority for this node. See [method set_network_authority]. + Returns the peer ID of the multiplayer authority for this node. See [method set_multiplayer_authority]. </description> </method> <method name="get_node" qualifiers="const"> @@ -417,10 +419,10 @@ Returns [code]true[/code] if this node is currently inside a [SceneTree]. </description> </method> - <method name="is_network_authority" qualifiers="const"> + <method name="is_multiplayer_authority" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the local system is the authority of this node. + Returns [code]true[/code] if the local system is the multiplayer authority of this node. </description> </method> <method name="is_physics_processing" qualifiers="const"> @@ -578,7 +580,7 @@ <argument index="0" name="method" type="StringName" /> <description> Sends a remote procedure call request for the given [code]method[/code] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behaviour depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Returns an empty [Variant]. - [b]Note:[/b] You can only safely use RPCs on clients after you received the [code]connected_to_server[/code] signal from the [MultiplayerAPI]. You also need to keep track of the connection state, either by the [MultiplayerAPI] signals like [code]server_disconnected[/code] or by checking [code]get_multiplayer().network_peer.get_connection_status() == CONNECTION_CONNECTED[/code]. + [b]Note:[/b] You can only safely use RPCs on clients after you received the [code]connected_to_server[/code] signal from the [MultiplayerAPI]. You also need to keep track of the connection state, either by the [MultiplayerAPI] signals like [code]server_disconnected[/code] or by checking [code]get_multiplayer().peer.get_connection_status() == CONNECTION_CONNECTED[/code]. </description> </method> <method name="rpc_config"> @@ -620,12 +622,12 @@ <description> </description> </method> - <method name="set_network_authority"> + <method name="set_multiplayer_authority"> <return type="void" /> <argument index="0" name="id" type="int" /> <argument index="1" name="recursive" type="bool" default="true" /> <description> - Sets the node's network authority to the peer with the given peer ID. The network authority is the peer that has authority over the node on the network. Useful in conjunction with [method rpc_config] and the [MultiplayerAPI]. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [code]recursive[/code], the given peer is recursively set as the authority for all children of this node. + Sets the node's multiplayer authority to the peer with the given peer ID. The multiplayer authority is the peer that has authority over the node on the network. Useful in conjunction with [method rpc_config] and the [MultiplayerAPI]. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [code]recursive[/code], the given peer is recursively set as the authority for all children of this node. </description> </method> <method name="set_physics_process"> diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml index 8698fbec61..01c8933b51 100644 --- a/doc/classes/PhysicsDirectBodyState2D.xml +++ b/doc/classes/PhysicsDirectBodyState2D.xml @@ -7,6 +7,7 @@ Provides direct access to a physics body in the [PhysicsServer2D], allowing safe changes to physics properties. This object is passed via the direct state callback of dynamic bodies, and is intended for changing the direct state of that body. See [method RigidBody2D._integrate_forces]. </description> <tutorials> + <link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> <link title="Ray-casting">https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> diff --git a/doc/classes/PhysicsDirectBodyState3D.xml b/doc/classes/PhysicsDirectBodyState3D.xml index 4599f86423..839a83cfc3 100644 --- a/doc/classes/PhysicsDirectBodyState3D.xml +++ b/doc/classes/PhysicsDirectBodyState3D.xml @@ -7,6 +7,8 @@ Provides direct access to a physics body in the [PhysicsServer3D], allowing safe changes to physics properties. This object is passed via the direct state callback of dynamic bodies, and is intended for changing the direct state of that body. See [method RigidBody3D._integrate_forces]. </description> <tutorials> + <link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> + <link title="Ray-casting">https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> <method name="add_central_force"> diff --git a/doc/classes/PhysicsDirectSpaceState2D.xml b/doc/classes/PhysicsDirectSpaceState2D.xml index 64e0fa4050..536c7e4e04 100644 --- a/doc/classes/PhysicsDirectSpaceState2D.xml +++ b/doc/classes/PhysicsDirectSpaceState2D.xml @@ -7,7 +7,8 @@ Direct access object to a space in the [PhysicsServer2D]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> - <link title="Ray-Casting">https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> + <link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> + <link title="Ray-casting">https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> <method name="cast_motion"> diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index 360d6e8e92..4e6bd8456f 100644 --- a/doc/classes/PhysicsDirectSpaceState3D.xml +++ b/doc/classes/PhysicsDirectSpaceState3D.xml @@ -7,6 +7,7 @@ Direct access object to a space in the [PhysicsServer3D]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> + <link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> <link title="Ray-casting">https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 24f7f4274e..428fa2575c 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -359,6 +359,14 @@ <description> </description> </method> + <method name="get_driver_resource"> + <return type="int" /> + <argument index="0" name="resource" type="int" enum="RenderingDevice.DriverResource" /> + <argument index="1" name="rid" type="RID" /> + <argument index="2" name="index" type="int" /> + <description> + </description> + </method> <method name="get_frame_delay" qualifiers="const"> <return type="int" /> <description> @@ -646,6 +654,32 @@ </constant> <constant name="BARRIER_MASK_NO_BARRIER" value="8"> </constant> + <constant name="DRIVER_RESOURCE_VULKAN_DEVICE" value="0" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE" value="1" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_INSTANCE" value="2" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_QUEUE" value="3" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX" value="4" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_IMAGE" value="5" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_IMAGE_VIEW" value="6" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT" value="7" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_SAMPLER" value="8" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET" value="9" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_BUFFER" value="10" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE" value="11" enum="DriverResource"> + </constant> + <constant name="DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE" value="12" enum="DriverResource"> + </constant> <constant name="DATA_FORMAT_R4G4_UNORM_PACK8" value="0" enum="DataFormat"> </constant> <constant name="DATA_FORMAT_R4G4B4A4_UNORM_PACK16" value="1" enum="DataFormat"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 638c657492..c0d7cca840 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1156,6 +1156,11 @@ <description> </description> </method> + <method name="get_rendering_device" qualifiers="const"> + <return type="RenderingDevice" /> + <description> + </description> + </method> <method name="get_rendering_info"> <return type="int" /> <argument index="0" name="info" type="int" enum="RenderingServer.RenderingInfo" /> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index 59e3190213..e81eff35ac 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -215,7 +215,7 @@ </member> <member name="multiplayer_poll" type="bool" setter="set_multiplayer_poll_enabled" getter="is_multiplayer_poll_enabled" default="true"> If [code]true[/code] (default value), enables automatic polling of the [MultiplayerAPI] for this SceneTree during [signal process_frame]. - If [code]false[/code], you need to manually call [method MultiplayerAPI.poll] to process network packets and deliver RPCs/RSETs. This allows running RPCs/RSETs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protection when accessing the [MultiplayerAPI] from threads. + If [code]false[/code], you need to manually call [method MultiplayerAPI.poll] to process network packets and deliver RPCs. This allows running RPCs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protection when accessing the [MultiplayerAPI] from threads. </member> <member name="paused" type="bool" setter="set_pause" getter="is_paused" default="false"> If [code]true[/code], the [SceneTree] is paused. Doing so will have the following behavior: diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index b553aad518..9c9119c664 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -40,6 +40,8 @@ <return type="Variant" /> <description> Joins the [Thread] and waits for it to finish. Returns what the method called returned. + Should either be used when you want to retrieve the value returned from the method called by the [Thread] or before freeing the instance that contains the [Thread]. + [b]Note:[/b] After the [Thread] finishes joining it will be disposed. If you want to use it again you will have to create a new instance of it. </description> </method> </methods> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 46f38e91f7..a02a23517f 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -134,6 +134,7 @@ <return type="void" /> <argument index="0" name="text" type="String" /> <description> + Returns [code]true[/code] if the viewport is currently embedding windows. </description> </method> <method name="push_unhandled_input"> diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index 66440d4bb4..fb79926043 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -121,6 +121,12 @@ Blits our render results to screen optionally applying lens distortion. This can only be called while processing [code]_commit_views[/code]. </description> </method> + <method name="get_render_target_texture"> + <return type="RID" /> + <argument index="0" name="render_target" type="RID" /> + <description> + </description> + </method> </methods> <constants> </constants> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 6ad24f255f..a4324f0a2c 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -7588,7 +7588,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 VkCommandPoolCreateInfo cmd_pool_info; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue(); + cmd_pool_info.queueFamilyIndex = context->get_graphics_queue_family_index(); cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool); @@ -8846,7 +8846,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de VkCommandPoolCreateInfo cmd_pool_info; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.pNext = nullptr; - cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue(); + cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue_family_index(); cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &frames[i].command_pool); @@ -9016,6 +9016,92 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { frames[frame].timestamp_count++; } +uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) { + _THREAD_SAFE_METHOD_ + + switch (p_resource) { + case DRIVER_RESOURCE_VULKAN_DEVICE: { + return (uint64_t)context->get_device(); + }; break; + case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: { + return (uint64_t)context->get_physical_device(); + }; break; + case DRIVER_RESOURCE_VULKAN_INSTANCE: { + return (uint64_t)context->get_instance(); + }; break; + case DRIVER_RESOURCE_VULKAN_QUEUE: { + return (uint64_t)context->get_graphics_queue(); + }; break; + case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: { + return context->get_graphics_queue_family_index(); + }; break; + case DRIVER_RESOURCE_VULKAN_IMAGE: { + Texture *tex = texture_owner.getornull(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return (uint64_t)tex->image; + }; break; + case DRIVER_RESOURCE_VULKAN_IMAGE_VIEW: { + Texture *tex = texture_owner.getornull(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return (uint64_t)tex->view; + }; break; + case DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT: { + Texture *tex = texture_owner.getornull(p_rid); + ERR_FAIL_NULL_V(tex, 0); + + return vulkan_formats[tex->format]; + }; break; + case DRIVER_RESOURCE_VULKAN_SAMPLER: { + VkSampler *sampler = sampler_owner.getornull(p_rid); + ERR_FAIL_NULL_V(sampler, 0); + + return uint64_t(*sampler); + }; break; + case DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET: { + UniformSet *uniform_set = uniform_set_owner.getornull(p_rid); + ERR_FAIL_NULL_V(uniform_set, 0); + + return uint64_t(uniform_set->descriptor_set); + }; break; + case DRIVER_RESOURCE_VULKAN_BUFFER: { + Buffer *buffer = nullptr; + if (vertex_buffer_owner.owns(p_rid)) { + buffer = vertex_buffer_owner.getornull(p_rid); + } else if (index_buffer_owner.owns(p_rid)) { + buffer = index_buffer_owner.getornull(p_rid); + } else if (uniform_buffer_owner.owns(p_rid)) { + buffer = uniform_buffer_owner.getornull(p_rid); + } else if (texture_buffer_owner.owns(p_rid)) { + buffer = &texture_buffer_owner.getornull(p_rid)->buffer; + } else if (storage_buffer_owner.owns(p_rid)) { + buffer = storage_buffer_owner.getornull(p_rid); + } + + ERR_FAIL_NULL_V(buffer, 0); + + return uint64_t(buffer->buffer); + }; break; + case DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE: { + ComputePipeline *compute_pipeline = compute_pipeline_owner.getornull(p_rid); + ERR_FAIL_NULL_V(compute_pipeline, 0); + + return uint64_t(compute_pipeline->pipeline); + }; break; + case DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE: { + RenderPipeline *render_pipeline = render_pipeline_owner.getornull(p_rid); + ERR_FAIL_NULL_V(render_pipeline, 0); + + return uint64_t(render_pipeline->pipeline); + }; break; + default: { + // not supported for this driver + return 0; + }; break; + } +} + uint32_t RenderingDeviceVulkan::get_captured_timestamps_count() const { return frames[frame].timestamp_result_count; } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index dc1b78c1d5..38ec79c45f 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1227,6 +1227,8 @@ public: virtual String get_device_name() const; virtual String get_device_pipeline_cache_uuid() const; + virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); + RenderingDeviceVulkan(); ~RenderingDeviceVulkan(); }; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 625c222b67..c14e3f0e93 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -2028,7 +2028,11 @@ int VulkanContext::get_swapchain_image_count() const { return swapchainImageCount; } -uint32_t VulkanContext::get_graphics_queue() const { +VkQueue VulkanContext::get_graphics_queue() const { + return graphics_queue; +} + +uint32_t VulkanContext::get_graphics_queue_family_index() const { return graphics_queue_family_index; } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index fe09d4c497..19ea806616 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -243,7 +243,8 @@ public: VkPhysicalDevice get_physical_device(); VkInstance get_instance() { return inst; } int get_swapchain_image_count() const; - uint32_t get_graphics_queue() const; + VkQueue get_graphics_queue() const; + uint32_t get_graphics_queue_family_index() const; void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); int window_get_width(DisplayServer::WindowID p_window = 0); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index daee61c2dd..5dd5c050e0 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2158,6 +2158,14 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da return true; } + if (drag_data.has("type") && String(drag_data["type"]) == "nodes") { + // Save branch as scene. + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); + return !favorite && Array(drag_data["nodes"]).size() == 1; + } + return false; } @@ -2296,6 +2304,13 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, _update_tree(_compute_uncollapsed_paths()); } } + + if (drag_data.has("type") && String(drag_data["type"]) == "nodes") { + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); + EditorNode::get_singleton()->get_scene_tree_dock()->save_branch_to_file(to_dir); + } } void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const { diff --git a/editor/icons/Listener2D.svg b/editor/icons/Listener2D.svg new file mode 100644 index 0000000000..db84dcfed7 --- /dev/null +++ b/editor/icons/Listener2D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1a5 5 0 0 0 -5 5h2a3 3 0 0 1 3-3 3 3 0 0 1 3 3c0 1.75-.54175 2.3583-1.1406 2.8574-.29944.2495-.62954.44071-.97656.69141-.17351.1253-.35729.26529-.53711.49219-.17982.227-.3457.58398-.3457.95898 0 1.2778-.31632 1.5742-.63867 1.7676-.32236.1934-.86133.23242-1.3613.23242h-1v2h1c.5 0 1.461.038922 2.3887-.51758.87316-.5239 1.4826-1.6633 1.5566-3.2266.011365-.0098.027247-.024684.10938-.083984.21547-.1556.63537-.40194 1.0859-.77734.90112-.751 1.8594-2.1445 1.8594-4.3945a5 5 0 0 0 -5-5zm7.9277 1-1.7383 1.0039a6 6 0 0 1 .81055 2.9961 6 6 0 0 1 -.80859 2.998l1.7363 1.002a8 8 0 0 0 0-8z" fill="#a5b7f3"/></svg> diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index d04e88e915..d20f3d105b 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -2097,7 +2097,6 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); - //LocalVector<int> bones_to_process = skel->get_parentless_bones(); LocalVector<int> bones_to_process; bones_to_process = skel->get_parentless_bones(); @@ -2109,19 +2108,18 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { child_bones_vector = skel->get_bone_children(current_bone_idx); int child_bones_size = child_bones_vector.size(); - // You have children but no parent, then you must be a root/parentless bone. - if (child_bones_size >= 0 && skel->get_bone_parent(current_bone_idx) <= 0) { - grests[current_bone_idx] = skel->global_pose_to_local_pose(current_bone_idx, skel->get_bone_global_pose(current_bone_idx)); + if (skel->get_bone_parent(current_bone_idx) < 0) { + grests[current_bone_idx] = skel->get_bone_rest(current_bone_idx); } for (int i = 0; i < child_bones_size; i++) { int child_bone_idx = child_bones_vector[i]; - grests[child_bone_idx] = skel->global_pose_to_local_pose(child_bone_idx, skel->get_bone_global_pose(child_bone_idx)); + grests[child_bone_idx] = grests[current_bone_idx] * skel->get_bone_rest(child_bone_idx); Vector3 v0 = grests[current_bone_idx].origin; Vector3 v1 = grests[child_bone_idx].origin; - Vector3 d = skel->get_bone_rest(child_bone_idx).origin.normalized(); - real_t dist = skel->get_bone_rest(child_bone_idx).origin.length(); + Vector3 d = (v1 - v0).normalized(); + real_t dist = v0.distance_to(v1); // Find closest axis. int closest = -1; diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp index 0a949c8610..53c5b8dd70 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.cpp +++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp @@ -31,6 +31,7 @@ #include "packed_scene_translation_parser_plugin.h" #include "core/io/resource_loader.h" +#include "scene/gui/option_button.h" #include "scene/resources/packed_scene.h" void PackedSceneEditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const { @@ -50,21 +51,31 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, Ref<SceneState> state = Ref<PackedScene>(loaded_res)->get_state(); Vector<String> parsed_strings; - String property_name; - Variant property_value; for (int i = 0; i < state->get_node_count(); i++) { - if (!ClassDB::is_parent_class(state->get_node_type(i), "Control") && !ClassDB::is_parent_class(state->get_node_type(i), "Viewport")) { + String node_type = state->get_node_type(i); + if (!ClassDB::is_parent_class(node_type, "Control") && !ClassDB::is_parent_class(node_type, "Window")) { continue; } + // Find the `auto_translate` property, and abort the string parsing of the node if disabled. + bool auto_translating = true; for (int j = 0; j < state->get_node_property_count(i); j++) { - property_name = state->get_node_property_name(i, j); - if (!lookup_properties.has(property_name)) { - continue; + if (state->get_node_property_name(i, j) == "auto_translate" && (bool)state->get_node_property_value(i, j) == false) { + auto_translating = false; + break; } + } + if (!auto_translating) { + continue; + } - property_value = state->get_node_property_value(i, j); + for (int j = 0; j < state->get_node_property_count(i); j++) { + String property_name = state->get_node_property_name(i, j); + if (!lookup_properties.has(property_name) || (exception_list.has(node_type) && exception_list[node_type].has(property_name))) { + continue; + } + Variant property_value = state->get_node_property_value(i, j); if (property_name == "script" && property_value.get_type() == Variant::OBJECT && !property_value.is_null()) { // Parse built-in script. Ref<Script> s = Object::cast_to<Script>(property_value); @@ -76,7 +87,16 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, parsed_strings.append_array(temp); r_ids_ctx_plural->append_array(ids_context_plural); } - } else if (property_name == "filters") { + } else if ((node_type == "MenuButton" || node_type == "OptionButton") && property_name == "items") { + Vector<String> str_values = property_value; + int incr_value = node_type == "MenuButton" ? PopupMenu::ITEM_PROPERTY_SIZE : OptionButton::ITEM_PROPERTY_SIZE; + for (int k = 0; k < str_values.size(); k += incr_value) { + String desc = str_values[k].get_slice(";", 1).strip_edges(); + if (!desc.is_empty()) { + parsed_strings.push_back(desc); + } + } + } else if (node_type == "FileDialog" && property_name == "filters") { // Extract FileDialog's filters property with values in format "*.png ; PNG Images","*.gd ; GDScript Files". Vector<String> str_values = property_value; for (int k = 0; k < str_values.size(); k++) { @@ -105,12 +125,17 @@ PackedSceneEditorTranslationParserPlugin::PackedSceneEditorTranslationParserPlug lookup_properties.insert("text"); lookup_properties.insert("hint_tooltip"); lookup_properties.insert("placeholder_text"); + lookup_properties.insert("items"); + lookup_properties.insert("title"); lookup_properties.insert("dialog_text"); lookup_properties.insert("filters"); lookup_properties.insert("script"); - //Add exception list (to prevent false positives) - //line edit, text edit, richtextlabel - //Set<String> exception_list; - //exception_list.insert("RichTextLabel"); + // Exception list (to prevent false positives). + exception_list.insert("LineEdit", Vector<StringName>()); + exception_list["LineEdit"].append("text"); + exception_list.insert("TextEdit", Vector<StringName>()); + exception_list["TextEdit"].append("text"); + exception_list.insert("CodeEdit", Vector<StringName>()); + exception_list["CodeEdit"].append("text"); } diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h index e51d65414e..af0291b69c 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.h +++ b/editor/plugins/packed_scene_translation_parser_plugin.h @@ -37,7 +37,9 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP GDCLASS(PackedSceneEditorTranslationParserPlugin, EditorTranslationParserPlugin); // Scene Node's properties that contain translation strings. - Set<String> lookup_properties; + Set<StringName> lookup_properties; + // Properties from specific Nodes that should be ignored. + Map<StringName, Vector<StringName>> exception_list; public: virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index c44760807f..48239a5d99 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -158,7 +158,6 @@ void ScriptTextEditor::enable_editor() { editor_enabled = true; _enable_code_editor(); - _set_theme_for_script(); _validate_script(); } diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index b58b7e4cac..d57345cac1 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -108,7 +108,6 @@ void POTGenerator::_write_to_pot(const String &p_file) { const String header = "# LANGUAGE translation for " + project_name + " for the following files:\n" + extracted_files + "#\n" - "#\n" "# FIRST AUTHOR < EMAIL @ADDRESS>, YEAR.\n" "#\n" "#, fuzzy\n" @@ -116,8 +115,9 @@ void POTGenerator::_write_to_pot(const String &p_file) { "msgstr \"\"\n" "\"Project-Id-Version: " + project_name + "\\n\"\n" + "\"MIME-Version: 1.0\\n\"\n" "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" - "\"Content-Transfer-Encoding: 8-bit\\n\"\n\n"; + "\"Content-Transfer-Encoding: 8-bit\\n\"\n"; file->store_string(header); @@ -129,6 +129,9 @@ void POTGenerator::_write_to_pot(const String &p_file) { String plural = v_msgid_data[i].plural; const Set<String> &locations = v_msgid_data[i].locations; + // Put the blank line at the start, to avoid a double at the end when closing the file. + file->store_line(""); + // Write file locations. for (Set<String>::Element *E = locations.front(); E; E = E->next()) { file->store_line("#: " + E->get().trim_prefix("res://")); @@ -142,13 +145,13 @@ void POTGenerator::_write_to_pot(const String &p_file) { // Write msgid. _write_msgid(file, msgid, false); - // Write msgid_plural + // Write msgid_plural. if (!plural.is_empty()) { _write_msgid(file, plural, true); file->store_line("msgstr[0] \"\""); - file->store_line("msgstr[1] \"\"\n"); + file->store_line("msgstr[1] \"\""); } else { - file->store_line("msgstr \"\"\n"); + file->store_line("msgstr \"\""); } } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2ec4a088a2..a08a628216 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2837,6 +2837,11 @@ void SceneTreeDock::set_filter(const String &p_filter) { scene_tree->set_filter(p_filter); } +void SceneTreeDock::save_branch_to_file(String p_directory) { + new_scene_from_dialog->set_current_dir(p_directory); + _tool_selected(TOOL_NEW_SCENE_FROM); +} + void SceneTreeDock::_focus_node() { Node *node = scene_tree->get_selected(); ERR_FAIL_COND(!node); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 387a35acbb..1086e8615f 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -269,6 +269,7 @@ protected: public: String get_filter(); void set_filter(const String &p_filter); + void save_branch_to_file(String p_directory); void _focus_node(); diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 79dc77d41d..43e1d40e47 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -4,7 +4,7 @@ A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library. </brief_description> <description> - A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.network_peer] after being initialized as either a client, server, or mesh. Events can then be handled by connecting to [MultiplayerAPI] signals. See [ENetConnection] for more information on the ENet library wrapper. + A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.multiplayer_peer] after being initialized as either a client, server, or mesh. Events can then be handled by connecting to [MultiplayerAPI] signals. See [ENetConnection] for more information on the ENet library wrapper. [b]Note:[/b] ENet only uses UDP, not TCP. When forwarding the server port to make your server accessible on the public Internet, you only need to forward the server port in UDP. You can use the [UPNP] class to try to forward the server port automatically when starting the server. </description> <tutorials> @@ -44,7 +44,7 @@ <return type="int" enum="Error" /> <argument index="0" name="unique_id" type="int" /> <description> - Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.network_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server). + Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server). </description> </method> <method name="create_server"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 5409b3ff79..bc8801b8b9 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -87,6 +87,19 @@ Object *GDScriptNativeClass::instantiate() { return ClassDB::instantiate(name); } +GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) { + if (p_script->initializer) { + return p_script->initializer; + } else { + GDScript *base = p_script->_base; + if (base != nullptr) { + return _super_constructor(base); + } else { + return nullptr; + } + } +} + void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) { GDScript *base = p_script->_base; if (base != nullptr) { @@ -135,6 +148,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco if (p_argcount < 0) { return instance; } + + initializer = _super_constructor(this); if (initializer != nullptr) { initializer->call(instance, p_args, p_argcount, r_error); if (r_error.error != Callable::CallError::CALL_OK) { diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index a6fd7b3b5d..791f8a1431 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -130,6 +130,7 @@ class GDScript : public Script { SelfList<GDScriptFunctionState>::List pending_func_states; + GDScriptFunction *_super_constructor(GDScript *p_script); void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error); GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f2b601dc2c..fc0bef3ba2 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2837,6 +2837,9 @@ void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { } void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) { + if (p_subscript->base == nullptr) { + return; + } if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) { reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 64a393f90c..6c3d4367e4 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1682,6 +1682,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) { MatchBranchNode *branch = parse_match_branch(); if (branch == nullptr) { + advance(); continue; } @@ -1745,7 +1746,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { push_error(R"(No pattern found for "match" branch.)"); } - consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)"); + if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) { + return nullptr; + } // Save continue state. bool could_continue = can_continue; @@ -1778,15 +1781,6 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ PatternNode *pattern = alloc_node<PatternNode>(); switch (current.type) { - case GDScriptTokenizer::Token::LITERAL: - advance(); - pattern->pattern_type = PatternNode::PT_LITERAL; - pattern->literal = parse_literal(); - if (pattern->literal == nullptr) { - // Error happened. - return nullptr; - } - break; case GDScriptTokenizer::Token::VAR: { // Bind. advance(); @@ -1849,44 +1843,44 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ // Dictionary. advance(); pattern->pattern_type = PatternNode::PT_DICTIONARY; - - if (!check(GDScriptTokenizer::Token::BRACE_CLOSE) && !is_at_end()) { - do { - if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) { - // Rest. + do { + if (check(GDScriptTokenizer::Token::BRACE_CLOSE) || is_at_end()) { + break; + } + if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) { + // Rest. + if (pattern->rest_used) { + push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)"); + } else { + PatternNode *sub_pattern = alloc_node<PatternNode>(); + sub_pattern->pattern_type = PatternNode::PT_REST; + pattern->dictionary.push_back({ nullptr, sub_pattern }); + pattern->rest_used = true; + } + } else { + ExpressionNode *key = parse_expression(false); + if (key == nullptr) { + push_error(R"(Expected expression as key for dictionary pattern.)"); + } + if (match(GDScriptTokenizer::Token::COLON)) { + // Value pattern. + PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern); + if (sub_pattern == nullptr) { + continue; + } if (pattern->rest_used) { push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)"); + } else if (sub_pattern->pattern_type == PatternNode::PT_REST) { + push_error(R"(The ".." pattern cannot be used as a value.)"); } else { - PatternNode *sub_pattern = alloc_node<PatternNode>(); - sub_pattern->pattern_type = PatternNode::PT_REST; - pattern->dictionary.push_back({ nullptr, sub_pattern }); - pattern->rest_used = true; + pattern->dictionary.push_back({ key, sub_pattern }); } } else { - ExpressionNode *key = parse_expression(false); - if (key == nullptr) { - push_error(R"(Expected expression as key for dictionary pattern.)"); - } - if (match(GDScriptTokenizer::Token::COLON)) { - // Value pattern. - PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern); - if (sub_pattern == nullptr) { - continue; - } - if (pattern->rest_used) { - push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)"); - } else if (sub_pattern->pattern_type == PatternNode::PT_REST) { - push_error(R"(The ".." pattern cannot be used as a value.)"); - } else { - pattern->dictionary.push_back({ key, sub_pattern }); - } - } else { - // Key match only. - pattern->dictionary.push_back({ key, nullptr }); - } + // Key match only. + pattern->dictionary.push_back({ key, nullptr }); } - } while (match(GDScriptTokenizer::Token::COMMA)); - } + } + } while (match(GDScriptTokenizer::Token::COMMA)); consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)"); break; } @@ -1895,8 +1889,13 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ ExpressionNode *expression = parse_expression(false); if (expression == nullptr) { push_error(R"(Expected expression for match pattern.)"); + return nullptr; } else { - pattern->pattern_type = PatternNode::PT_EXPRESSION; + if (expression->type == GDScriptParser::Node::LITERAL) { + pattern->pattern_type = PatternNode::PT_LITERAL; + } else { + pattern->pattern_type = PatternNode::PT_EXPRESSION; + } pattern->expression = expression; } break; @@ -2386,7 +2385,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr } await->to_await = element; - current_function->is_coroutine = true; + if (current_function) { // Might be null in a getter or setter. + current_function->is_coroutine = true; + } return await; } @@ -2463,8 +2464,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode push_error(R"(Expected "=" after dictionary key.)"); } } - key->is_constant = true; - key->reduced_value = static_cast<IdentifierNode *>(key)->name; + if (key != nullptr) { + key->is_constant = true; + key->reduced_value = static_cast<IdentifierNode *>(key)->name; + } break; case DictionaryNode::PYTHON_DICT: if (!match(GDScriptTokenizer::Token::COLON)) { diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd new file mode 100644 index 0000000000..92dfb2366d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd @@ -0,0 +1,2 @@ +func test(): + var dictionary = { hello = "world",, } diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out new file mode 100644 index 0000000000..d1dcd1cb4b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected expression as dictionary key. diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd new file mode 100644 index 0000000000..43b513045b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd @@ -0,0 +1,34 @@ +func foo(x): + match x: + 1 + 1: + print("1+1") + [1,2,[1,{1:2,2:var z,..}]]: + print("[1,2,[1,{1:2,2:var z,..}]]") + print(z) + 1 if true else 2: + print("1 if true else 2") + 1 < 2: + print("1 < 2") + 1 or 2 and 1: + print("1 or 2 and 1") + 6 | 1: + print("1 | 1") + 1 >> 1: + print("1 >> 1") + 1, 2 or 3, 4: + print("1, 2 or 3, 4") + _: + print("wildcard") + +func test(): + foo(6 | 1) + foo(1 >> 1) + foo(2) + foo(1) + foo(1+1) + foo(1 < 2) + foo([2, 1]) + foo(4) + foo([1, 2, [1, {1 : 2, 2:3}]]) + foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]]) + foo([1, 2, [1, {1 : 2}]]) diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out new file mode 100644 index 0000000000..67c7e28046 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out @@ -0,0 +1,14 @@ +GDTEST_OK +1 | 1 +1 >> 1 +1+1 +1 if true else 2 +1+1 +1 < 2 +wildcard +1, 2 or 3, 4 +[1,2,[1,{1:2,2:var z,..}]] +3 +[1,2,[1,{1:2,2:var z,..}]] +[1, 3, 5, 123] +wildcard diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd new file mode 100644 index 0000000000..2b46f1e88a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd @@ -0,0 +1,27 @@ +func foo(x): + match x: + 1: + print("1") + 2: + print("2") + [1, 2]: + print("[1, 2]") + 3 or 4: + print("3 or 4") + 4: + print("4") + {1 : 2, 2 : 3}: + print("{1 : 2, 2 : 3}") + _: + print("wildcard") + +func test(): + foo(0) + foo(1) + foo(2) + foo([1, 2]) + foo(3) + foo(4) + foo([4,4]) + foo({1 : 2, 2 : 3}) + foo({1 : 2, 4 : 3}) diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out new file mode 100644 index 0000000000..46ee4b04da --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out @@ -0,0 +1,10 @@ +GDTEST_OK +wildcard +1 +2 +[1, 2] +wildcard +4 +wildcard +{1 : 2, 2 : 3} +wildcard diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 98519707a9..3437dbd194 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -337,7 +337,7 @@ void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const { void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("' '"); // character literal p_delimiters->push_back("\" \""); // regular string literal - // Verbatim string literals (`@" "`) don't render correctly, so don't highlight them. + p_delimiters->push_back("@\" \""); // verbatim string literal // Generic string highlighting suffices as a workaround for now. } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 67bcb34b1c..e03c5fd248 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2610,7 +2610,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { Map<StringName, StringName> accessor_methods; for (const PropertyInfo &property : property_list) { - if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY) { + if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) { continue; } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 78a87be971..19e94adf68 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -440,266 +440,260 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) { } } -struct FeatureInfo { - int32_t tag; - String name; -}; +static Map<StringName, int32_t> feature_sets; -static FeatureInfo feature_set[] = { +static void _insert_feature_sets() { // Registered OpenType feature tags. - { HB_TAG('a', 'a', 'l', 't'), "access_all_alternates" }, - { HB_TAG('a', 'b', 'v', 'f'), "above_base_forms" }, - { HB_TAG('a', 'b', 'v', 'm'), "above_base_mark_positioning" }, - { HB_TAG('a', 'b', 'v', 's'), "above_base_substitutions" }, - { HB_TAG('a', 'f', 'r', 'c'), "alternative_fractions" }, - { HB_TAG('a', 'k', 'h', 'n'), "akhands" }, - { HB_TAG('b', 'l', 'w', 'f'), "below_base_forms" }, - { HB_TAG('b', 'l', 'w', 'm'), "below_base_mark_positioning" }, - { HB_TAG('b', 'l', 'w', 's'), "below_base_substitutions" }, - { HB_TAG('c', 'a', 'l', 't'), "contextual_alternates" }, - { HB_TAG('c', 'a', 's', 'e'), "case_sensitive_forms" }, - { HB_TAG('c', 'c', 'm', 'p'), "glyph_composition" }, - { HB_TAG('c', 'f', 'a', 'r'), "conjunct_form_after_ro" }, - { HB_TAG('c', 'j', 'c', 't'), "conjunct_forms" }, - { HB_TAG('c', 'l', 'i', 'g'), "contextual_ligatures" }, - { HB_TAG('c', 'p', 'c', 't'), "centered_cjk_punctuation" }, - { HB_TAG('c', 'p', 's', 'p'), "capital_spacing" }, - { HB_TAG('c', 's', 'w', 'h'), "contextual_swash" }, - { HB_TAG('c', 'u', 'r', 's'), "cursive_positioning" }, - { HB_TAG('c', 'v', '0', '1'), "character_variant_01" }, - { HB_TAG('c', 'v', '0', '2'), "character_variant_02" }, - { HB_TAG('c', 'v', '0', '3'), "character_variant_03" }, - { HB_TAG('c', 'v', '0', '4'), "character_variant_04" }, - { HB_TAG('c', 'v', '0', '5'), "character_variant_05" }, - { HB_TAG('c', 'v', '0', '6'), "character_variant_06" }, - { HB_TAG('c', 'v', '0', '7'), "character_variant_07" }, - { HB_TAG('c', 'v', '0', '8'), "character_variant_08" }, - { HB_TAG('c', 'v', '0', '9'), "character_variant_09" }, - { HB_TAG('c', 'v', '1', '0'), "character_variant_10" }, - { HB_TAG('c', 'v', '1', '1'), "character_variant_11" }, - { HB_TAG('c', 'v', '1', '2'), "character_variant_12" }, - { HB_TAG('c', 'v', '1', '3'), "character_variant_13" }, - { HB_TAG('c', 'v', '1', '4'), "character_variant_14" }, - { HB_TAG('c', 'v', '1', '5'), "character_variant_15" }, - { HB_TAG('c', 'v', '1', '6'), "character_variant_16" }, - { HB_TAG('c', 'v', '1', '7'), "character_variant_17" }, - { HB_TAG('c', 'v', '1', '8'), "character_variant_18" }, - { HB_TAG('c', 'v', '1', '9'), "character_variant_19" }, - { HB_TAG('c', 'v', '2', '0'), "character_variant_20" }, - { HB_TAG('c', 'v', '2', '1'), "character_variant_21" }, - { HB_TAG('c', 'v', '2', '2'), "character_variant_22" }, - { HB_TAG('c', 'v', '2', '3'), "character_variant_23" }, - { HB_TAG('c', 'v', '2', '4'), "character_variant_24" }, - { HB_TAG('c', 'v', '2', '5'), "character_variant_25" }, - { HB_TAG('c', 'v', '2', '6'), "character_variant_26" }, - { HB_TAG('c', 'v', '2', '7'), "character_variant_27" }, - { HB_TAG('c', 'v', '2', '8'), "character_variant_28" }, - { HB_TAG('c', 'v', '2', '9'), "character_variant_29" }, - { HB_TAG('c', 'v', '3', '0'), "character_variant_30" }, - { HB_TAG('c', 'v', '3', '1'), "character_variant_31" }, - { HB_TAG('c', 'v', '3', '2'), "character_variant_32" }, - { HB_TAG('c', 'v', '3', '3'), "character_variant_33" }, - { HB_TAG('c', 'v', '3', '4'), "character_variant_34" }, - { HB_TAG('c', 'v', '3', '5'), "character_variant_35" }, - { HB_TAG('c', 'v', '3', '6'), "character_variant_36" }, - { HB_TAG('c', 'v', '3', '7'), "character_variant_37" }, - { HB_TAG('c', 'v', '3', '8'), "character_variant_38" }, - { HB_TAG('c', 'v', '3', '9'), "character_variant_39" }, - { HB_TAG('c', 'v', '4', '0'), "character_variant_40" }, - { HB_TAG('c', 'v', '4', '1'), "character_variant_41" }, - { HB_TAG('c', 'v', '4', '2'), "character_variant_42" }, - { HB_TAG('c', 'v', '4', '3'), "character_variant_43" }, - { HB_TAG('c', 'v', '4', '4'), "character_variant_44" }, - { HB_TAG('c', 'v', '4', '5'), "character_variant_45" }, - { HB_TAG('c', 'v', '4', '6'), "character_variant_46" }, - { HB_TAG('c', 'v', '4', '7'), "character_variant_47" }, - { HB_TAG('c', 'v', '4', '8'), "character_variant_48" }, - { HB_TAG('c', 'v', '4', '9'), "character_variant_49" }, - { HB_TAG('c', 'v', '5', '0'), "character_variant_50" }, - { HB_TAG('c', 'v', '5', '1'), "character_variant_51" }, - { HB_TAG('c', 'v', '5', '2'), "character_variant_52" }, - { HB_TAG('c', 'v', '5', '3'), "character_variant_53" }, - { HB_TAG('c', 'v', '5', '4'), "character_variant_54" }, - { HB_TAG('c', 'v', '5', '5'), "character_variant_55" }, - { HB_TAG('c', 'v', '5', '6'), "character_variant_56" }, - { HB_TAG('c', 'v', '5', '7'), "character_variant_57" }, - { HB_TAG('c', 'v', '5', '8'), "character_variant_58" }, - { HB_TAG('c', 'v', '5', '9'), "character_variant_59" }, - { HB_TAG('c', 'v', '6', '0'), "character_variant_60" }, - { HB_TAG('c', 'v', '6', '1'), "character_variant_61" }, - { HB_TAG('c', 'v', '6', '2'), "character_variant_62" }, - { HB_TAG('c', 'v', '6', '3'), "character_variant_63" }, - { HB_TAG('c', 'v', '6', '4'), "character_variant_64" }, - { HB_TAG('c', 'v', '6', '5'), "character_variant_65" }, - { HB_TAG('c', 'v', '6', '6'), "character_variant_66" }, - { HB_TAG('c', 'v', '6', '7'), "character_variant_67" }, - { HB_TAG('c', 'v', '6', '8'), "character_variant_68" }, - { HB_TAG('c', 'v', '6', '9'), "character_variant_69" }, - { HB_TAG('c', 'v', '7', '0'), "character_variant_70" }, - { HB_TAG('c', 'v', '7', '1'), "character_variant_71" }, - { HB_TAG('c', 'v', '7', '2'), "character_variant_72" }, - { HB_TAG('c', 'v', '7', '3'), "character_variant_73" }, - { HB_TAG('c', 'v', '7', '4'), "character_variant_74" }, - { HB_TAG('c', 'v', '7', '5'), "character_variant_75" }, - { HB_TAG('c', 'v', '7', '6'), "character_variant_76" }, - { HB_TAG('c', 'v', '7', '7'), "character_variant_77" }, - { HB_TAG('c', 'v', '7', '8'), "character_variant_78" }, - { HB_TAG('c', 'v', '7', '9'), "character_variant_79" }, - { HB_TAG('c', 'v', '8', '0'), "character_variant_80" }, - { HB_TAG('c', 'v', '8', '1'), "character_variant_81" }, - { HB_TAG('c', 'v', '8', '2'), "character_variant_82" }, - { HB_TAG('c', 'v', '8', '3'), "character_variant_83" }, - { HB_TAG('c', 'v', '8', '4'), "character_variant_84" }, - { HB_TAG('c', 'v', '8', '5'), "character_variant_85" }, - { HB_TAG('c', 'v', '8', '6'), "character_variant_86" }, - { HB_TAG('c', 'v', '8', '7'), "character_variant_87" }, - { HB_TAG('c', 'v', '8', '8'), "character_variant_88" }, - { HB_TAG('c', 'v', '8', '9'), "character_variant_89" }, - { HB_TAG('c', 'v', '9', '0'), "character_variant_90" }, - { HB_TAG('c', 'v', '9', '1'), "character_variant_91" }, - { HB_TAG('c', 'v', '9', '2'), "character_variant_92" }, - { HB_TAG('c', 'v', '9', '3'), "character_variant_93" }, - { HB_TAG('c', 'v', '9', '4'), "character_variant_94" }, - { HB_TAG('c', 'v', '9', '5'), "character_variant_95" }, - { HB_TAG('c', 'v', '9', '6'), "character_variant_96" }, - { HB_TAG('c', 'v', '9', '7'), "character_variant_97" }, - { HB_TAG('c', 'v', '9', '8'), "character_variant_98" }, - { HB_TAG('c', 'v', '9', '9'), "character_variant_99" }, - { HB_TAG('c', '2', 'p', 'c'), "petite_capitals_from_capitals" }, - { HB_TAG('c', '2', 's', 'c'), "small_capitals_from_capitals" }, - { HB_TAG('d', 'i', 's', 't'), "distances" }, - { HB_TAG('d', 'l', 'i', 'g'), "discretionary_ligatures" }, - { HB_TAG('d', 'n', 'o', 'm'), "denominators" }, - { HB_TAG('d', 't', 'l', 's'), "dotless_forms" }, - { HB_TAG('e', 'x', 'p', 't'), "expert_forms" }, - { HB_TAG('f', 'a', 'l', 't'), "final_glyph_on_line_alternates" }, - { HB_TAG('f', 'i', 'n', '2'), "terminal_forms_2" }, - { HB_TAG('f', 'i', 'n', '3'), "terminal_forms_3" }, - { HB_TAG('f', 'i', 'n', 'a'), "terminal_forms" }, - { HB_TAG('f', 'l', 'a', 'c'), "flattened_accent_forms" }, - { HB_TAG('f', 'r', 'a', 'c'), "fractions" }, - { HB_TAG('f', 'w', 'i', 'd'), "full_widths" }, - { HB_TAG('h', 'a', 'l', 'f'), "half_forms" }, - { HB_TAG('h', 'a', 'l', 'n'), "halant_forms" }, - { HB_TAG('h', 'a', 'l', 't'), "alternate_half_widths" }, - { HB_TAG('h', 'i', 's', 't'), "historical_forms" }, - { HB_TAG('h', 'k', 'n', 'a'), "horizontal_kana_alternates" }, - { HB_TAG('h', 'l', 'i', 'g'), "historical_ligatures" }, - { HB_TAG('h', 'n', 'g', 'l'), "hangul" }, - { HB_TAG('h', 'o', 'j', 'o'), "hojo_kanji_forms" }, - { HB_TAG('h', 'w', 'i', 'd'), "half_widths" }, - { HB_TAG('i', 'n', 'i', 't'), "initial_forms" }, - { HB_TAG('i', 's', 'o', 'l'), "isolated_forms" }, - { HB_TAG('i', 't', 'a', 'l'), "italics" }, - { HB_TAG('j', 'a', 'l', 't'), "justification_alternates" }, - { HB_TAG('j', 'p', '7', '8'), "jis78_forms" }, - { HB_TAG('j', 'p', '8', '3'), "jis83_forms" }, - { HB_TAG('j', 'p', '9', '0'), "jis90_forms" }, - { HB_TAG('j', 'p', '0', '4'), "jis2004_forms" }, - { HB_TAG('k', 'e', 'r', 'n'), "kerning" }, - { HB_TAG('l', 'f', 'b', 'd'), "left_bounds" }, - { HB_TAG('l', 'i', 'g', 'a'), "standard_ligatures" }, - { HB_TAG('l', 'j', 'm', 'o'), "leading_jamo_forms" }, - { HB_TAG('l', 'n', 'u', 'm'), "lining_figures" }, - { HB_TAG('l', 'o', 'c', 'l'), "localized_forms" }, - { HB_TAG('l', 't', 'r', 'a'), "left_to_right_alternates" }, - { HB_TAG('l', 't', 'r', 'm'), "left_to_right_mirrored_forms" }, - { HB_TAG('m', 'a', 'r', 'k'), "mark_positioning" }, - { HB_TAG('m', 'e', 'd', '2'), "medial_forms_2" }, - { HB_TAG('m', 'e', 'd', 'i'), "medial_forms" }, - { HB_TAG('m', 'g', 'r', 'k'), "mathematical_greek" }, - { HB_TAG('m', 'k', 'm', 'k'), "mark_to_mark_positioning" }, - { HB_TAG('m', 's', 'e', 't'), "mark_positioning_via_substitution" }, - { HB_TAG('n', 'a', 'l', 't'), "alternate_annotation_forms" }, - { HB_TAG('n', 'l', 'c', 'k'), "nlc_kanji_forms" }, - { HB_TAG('n', 'u', 'k', 't'), "nukta_forms" }, - { HB_TAG('n', 'u', 'm', 'r'), "numerators" }, - { HB_TAG('o', 'n', 'u', 'm'), "oldstyle_figures" }, - { HB_TAG('o', 'p', 'b', 'd'), "optical_bounds" }, - { HB_TAG('o', 'r', 'd', 'n'), "ordinals" }, - { HB_TAG('o', 'r', 'n', 'm'), "ornaments" }, - { HB_TAG('p', 'a', 'l', 't'), "proportional_alternate_widths" }, - { HB_TAG('p', 'c', 'a', 'p'), "petite_capitals" }, - { HB_TAG('p', 'k', 'n', 'a'), "proportional_kana" }, - { HB_TAG('p', 'n', 'u', 'm'), "proportional_figures" }, - { HB_TAG('p', 'r', 'e', 'f'), "pre_base_forms" }, - { HB_TAG('p', 'r', 'e', 's'), "pre_base_substitutions" }, - { HB_TAG('p', 's', 't', 'f'), "post_base_forms" }, - { HB_TAG('p', 's', 't', 's'), "post_base_substitutions" }, - { HB_TAG('p', 'w', 'i', 'd'), "proportional_widths" }, - { HB_TAG('q', 'w', 'i', 'd'), "quarter_widths" }, - { HB_TAG('r', 'a', 'n', 'd'), "randomize" }, - { HB_TAG('r', 'c', 'l', 't'), "required_contextual_alternates" }, - { HB_TAG('r', 'k', 'r', 'f'), "rakar_forms" }, - { HB_TAG('r', 'l', 'i', 'g'), "required_ligatures" }, - { HB_TAG('r', 'p', 'h', 'f'), "reph_forms" }, - { HB_TAG('r', 't', 'b', 'd'), "right_bounds" }, - { HB_TAG('r', 't', 'l', 'a'), "right_to_left_alternates" }, - { HB_TAG('r', 't', 'l', 'm'), "right_to_left_mirrored_forms" }, - { HB_TAG('r', 'u', 'b', 'y'), "ruby_notation_forms" }, - { HB_TAG('r', 'v', 'r', 'n'), "required_variation_alternates" }, - { HB_TAG('s', 'a', 'l', 't'), "stylistic_alternates" }, - { HB_TAG('s', 'i', 'n', 'f'), "scientific_inferiors" }, - { HB_TAG('s', 'i', 'z', 'e'), "optical_size" }, - { HB_TAG('s', 'm', 'c', 'p'), "small_capitals" }, - { HB_TAG('s', 'm', 'p', 'l'), "simplified_forms" }, - { HB_TAG('s', 's', '0', '1'), "stylistic_set_01" }, - { HB_TAG('s', 's', '0', '2'), "stylistic_set_02" }, - { HB_TAG('s', 's', '0', '3'), "stylistic_set_03" }, - { HB_TAG('s', 's', '0', '4'), "stylistic_set_04" }, - { HB_TAG('s', 's', '0', '5'), "stylistic_set_05" }, - { HB_TAG('s', 's', '0', '6'), "stylistic_set_06" }, - { HB_TAG('s', 's', '0', '7'), "stylistic_set_07" }, - { HB_TAG('s', 's', '0', '8'), "stylistic_set_08" }, - { HB_TAG('s', 's', '0', '9'), "stylistic_set_09" }, - { HB_TAG('s', 's', '1', '0'), "stylistic_set_10" }, - { HB_TAG('s', 's', '1', '1'), "stylistic_set_11" }, - { HB_TAG('s', 's', '1', '2'), "stylistic_set_12" }, - { HB_TAG('s', 's', '1', '3'), "stylistic_set_13" }, - { HB_TAG('s', 's', '1', '4'), "stylistic_set_14" }, - { HB_TAG('s', 's', '1', '5'), "stylistic_set_15" }, - { HB_TAG('s', 's', '1', '6'), "stylistic_set_16" }, - { HB_TAG('s', 's', '1', '7'), "stylistic_set_17" }, - { HB_TAG('s', 's', '1', '8'), "stylistic_set_18" }, - { HB_TAG('s', 's', '1', '9'), "stylistic_set_19" }, - { HB_TAG('s', 's', '2', '0'), "stylistic_set_20" }, - { HB_TAG('s', 's', 't', 'y'), "math_script_style_alternates" }, - { HB_TAG('s', 't', 'c', 'h'), "stretching_glyph_decomposition" }, - { HB_TAG('s', 'u', 'b', 's'), "subscript" }, - { HB_TAG('s', 'u', 'p', 's'), "superscript" }, - { HB_TAG('s', 'w', 's', 'h'), "swash" }, - { HB_TAG('t', 'i', 't', 'l'), "titling" }, - { HB_TAG('t', 'j', 'm', 'o'), "trailing_jamo_forms" }, - { HB_TAG('t', 'n', 'a', 'm'), "traditional_name_forms" }, - { HB_TAG('t', 'n', 'u', 'm'), "tabular_figures" }, - { HB_TAG('t', 'r', 'a', 'd'), "traditional_forms" }, - { HB_TAG('t', 'w', 'i', 'd'), "third_widths" }, - { HB_TAG('u', 'n', 'i', 'c'), "unicase" }, - { HB_TAG('v', 'a', 'l', 't'), "alternate_vertical_metrics" }, - { HB_TAG('v', 'a', 't', 'u'), "vattu_variants" }, - { HB_TAG('v', 'e', 'r', 't'), "vertical_writing" }, - { HB_TAG('v', 'h', 'a', 'l'), "alternate_vertical_half_metrics" }, - { HB_TAG('v', 'j', 'm', 'o'), "vowel_jamo_forms" }, - { HB_TAG('v', 'k', 'n', 'a'), "vertical_kana_alternates" }, - { HB_TAG('v', 'k', 'r', 'n'), "vertical_kerning" }, - { HB_TAG('v', 'p', 'a', 'l'), "proportional_alternate_vertical_metrics" }, - { HB_TAG('v', 'r', 't', '2'), "vertical_alternates_and_rotation" }, - { HB_TAG('v', 'r', 't', 'r'), "vertical_alternates_for_rotation" }, - { HB_TAG('z', 'e', 'r', 'o'), "slashed_zero" }, - // Registered OpenType variation tags. - { HB_TAG('i', 't', 'a', 'l'), "italic" }, - { HB_TAG('o', 'p', 's', 'z'), "optical_size" }, - { HB_TAG('s', 'l', 'n', 't'), "slant" }, - { HB_TAG('w', 'd', 't', 'h'), "width" }, - { HB_TAG('w', 'g', 'h', 't'), "weight" }, - { 0, String() }, -}; + feature_sets.insert("access_all_alternates", HB_TAG('a', 'a', 'l', 't')); + feature_sets.insert("above_base_forms", HB_TAG('a', 'b', 'v', 'f')); + feature_sets.insert("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm')); + feature_sets.insert("above_base_substitutions", HB_TAG('a', 'b', 'v', 's')); + feature_sets.insert("alternative_fractions", HB_TAG('a', 'f', 'r', 'c')); + feature_sets.insert("akhands", HB_TAG('a', 'k', 'h', 'n')); + feature_sets.insert("below_base_forms", HB_TAG('b', 'l', 'w', 'f')); + feature_sets.insert("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm')); + feature_sets.insert("below_base_substitutions", HB_TAG('b', 'l', 'w', 's')); + feature_sets.insert("contextual_alternates", HB_TAG('c', 'a', 'l', 't')); + feature_sets.insert("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e')); + feature_sets.insert("glyph_composition", HB_TAG('c', 'c', 'm', 'p')); + feature_sets.insert("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r')); + feature_sets.insert("conjunct_forms", HB_TAG('c', 'j', 'c', 't')); + feature_sets.insert("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g')); + feature_sets.insert("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't')); + feature_sets.insert("capital_spacing", HB_TAG('c', 'p', 's', 'p')); + feature_sets.insert("contextual_swash", HB_TAG('c', 's', 'w', 'h')); + feature_sets.insert("cursive_positioning", HB_TAG('c', 'u', 'r', 's')); + feature_sets.insert("character_variant_01", HB_TAG('c', 'v', '0', '1')); + feature_sets.insert("character_variant_02", HB_TAG('c', 'v', '0', '2')); + feature_sets.insert("character_variant_03", HB_TAG('c', 'v', '0', '3')); + feature_sets.insert("character_variant_04", HB_TAG('c', 'v', '0', '4')); + feature_sets.insert("character_variant_05", HB_TAG('c', 'v', '0', '5')); + feature_sets.insert("character_variant_06", HB_TAG('c', 'v', '0', '6')); + feature_sets.insert("character_variant_07", HB_TAG('c', 'v', '0', '7')); + feature_sets.insert("character_variant_08", HB_TAG('c', 'v', '0', '8')); + feature_sets.insert("character_variant_09", HB_TAG('c', 'v', '0', '9')); + feature_sets.insert("character_variant_10", HB_TAG('c', 'v', '1', '0')); + feature_sets.insert("character_variant_11", HB_TAG('c', 'v', '1', '1')); + feature_sets.insert("character_variant_12", HB_TAG('c', 'v', '1', '2')); + feature_sets.insert("character_variant_13", HB_TAG('c', 'v', '1', '3')); + feature_sets.insert("character_variant_14", HB_TAG('c', 'v', '1', '4')); + feature_sets.insert("character_variant_15", HB_TAG('c', 'v', '1', '5')); + feature_sets.insert("character_variant_16", HB_TAG('c', 'v', '1', '6')); + feature_sets.insert("character_variant_17", HB_TAG('c', 'v', '1', '7')); + feature_sets.insert("character_variant_18", HB_TAG('c', 'v', '1', '8')); + feature_sets.insert("character_variant_19", HB_TAG('c', 'v', '1', '9')); + feature_sets.insert("character_variant_20", HB_TAG('c', 'v', '2', '0')); + feature_sets.insert("character_variant_21", HB_TAG('c', 'v', '2', '1')); + feature_sets.insert("character_variant_22", HB_TAG('c', 'v', '2', '2')); + feature_sets.insert("character_variant_23", HB_TAG('c', 'v', '2', '3')); + feature_sets.insert("character_variant_24", HB_TAG('c', 'v', '2', '4')); + feature_sets.insert("character_variant_25", HB_TAG('c', 'v', '2', '5')); + feature_sets.insert("character_variant_26", HB_TAG('c', 'v', '2', '6')); + feature_sets.insert("character_variant_27", HB_TAG('c', 'v', '2', '7')); + feature_sets.insert("character_variant_28", HB_TAG('c', 'v', '2', '8')); + feature_sets.insert("character_variant_29", HB_TAG('c', 'v', '2', '9')); + feature_sets.insert("character_variant_30", HB_TAG('c', 'v', '3', '0')); + feature_sets.insert("character_variant_31", HB_TAG('c', 'v', '3', '1')); + feature_sets.insert("character_variant_32", HB_TAG('c', 'v', '3', '2')); + feature_sets.insert("character_variant_33", HB_TAG('c', 'v', '3', '3')); + feature_sets.insert("character_variant_34", HB_TAG('c', 'v', '3', '4')); + feature_sets.insert("character_variant_35", HB_TAG('c', 'v', '3', '5')); + feature_sets.insert("character_variant_36", HB_TAG('c', 'v', '3', '6')); + feature_sets.insert("character_variant_37", HB_TAG('c', 'v', '3', '7')); + feature_sets.insert("character_variant_38", HB_TAG('c', 'v', '3', '8')); + feature_sets.insert("character_variant_39", HB_TAG('c', 'v', '3', '9')); + feature_sets.insert("character_variant_40", HB_TAG('c', 'v', '4', '0')); + feature_sets.insert("character_variant_41", HB_TAG('c', 'v', '4', '1')); + feature_sets.insert("character_variant_42", HB_TAG('c', 'v', '4', '2')); + feature_sets.insert("character_variant_43", HB_TAG('c', 'v', '4', '3')); + feature_sets.insert("character_variant_44", HB_TAG('c', 'v', '4', '4')); + feature_sets.insert("character_variant_45", HB_TAG('c', 'v', '4', '5')); + feature_sets.insert("character_variant_46", HB_TAG('c', 'v', '4', '6')); + feature_sets.insert("character_variant_47", HB_TAG('c', 'v', '4', '7')); + feature_sets.insert("character_variant_48", HB_TAG('c', 'v', '4', '8')); + feature_sets.insert("character_variant_49", HB_TAG('c', 'v', '4', '9')); + feature_sets.insert("character_variant_50", HB_TAG('c', 'v', '5', '0')); + feature_sets.insert("character_variant_51", HB_TAG('c', 'v', '5', '1')); + feature_sets.insert("character_variant_52", HB_TAG('c', 'v', '5', '2')); + feature_sets.insert("character_variant_53", HB_TAG('c', 'v', '5', '3')); + feature_sets.insert("character_variant_54", HB_TAG('c', 'v', '5', '4')); + feature_sets.insert("character_variant_55", HB_TAG('c', 'v', '5', '5')); + feature_sets.insert("character_variant_56", HB_TAG('c', 'v', '5', '6')); + feature_sets.insert("character_variant_57", HB_TAG('c', 'v', '5', '7')); + feature_sets.insert("character_variant_58", HB_TAG('c', 'v', '5', '8')); + feature_sets.insert("character_variant_59", HB_TAG('c', 'v', '5', '9')); + feature_sets.insert("character_variant_60", HB_TAG('c', 'v', '6', '0')); + feature_sets.insert("character_variant_61", HB_TAG('c', 'v', '6', '1')); + feature_sets.insert("character_variant_62", HB_TAG('c', 'v', '6', '2')); + feature_sets.insert("character_variant_63", HB_TAG('c', 'v', '6', '3')); + feature_sets.insert("character_variant_64", HB_TAG('c', 'v', '6', '4')); + feature_sets.insert("character_variant_65", HB_TAG('c', 'v', '6', '5')); + feature_sets.insert("character_variant_66", HB_TAG('c', 'v', '6', '6')); + feature_sets.insert("character_variant_67", HB_TAG('c', 'v', '6', '7')); + feature_sets.insert("character_variant_68", HB_TAG('c', 'v', '6', '8')); + feature_sets.insert("character_variant_69", HB_TAG('c', 'v', '6', '9')); + feature_sets.insert("character_variant_70", HB_TAG('c', 'v', '7', '0')); + feature_sets.insert("character_variant_71", HB_TAG('c', 'v', '7', '1')); + feature_sets.insert("character_variant_72", HB_TAG('c', 'v', '7', '2')); + feature_sets.insert("character_variant_73", HB_TAG('c', 'v', '7', '3')); + feature_sets.insert("character_variant_74", HB_TAG('c', 'v', '7', '4')); + feature_sets.insert("character_variant_75", HB_TAG('c', 'v', '7', '5')); + feature_sets.insert("character_variant_76", HB_TAG('c', 'v', '7', '6')); + feature_sets.insert("character_variant_77", HB_TAG('c', 'v', '7', '7')); + feature_sets.insert("character_variant_78", HB_TAG('c', 'v', '7', '8')); + feature_sets.insert("character_variant_79", HB_TAG('c', 'v', '7', '9')); + feature_sets.insert("character_variant_80", HB_TAG('c', 'v', '8', '0')); + feature_sets.insert("character_variant_81", HB_TAG('c', 'v', '8', '1')); + feature_sets.insert("character_variant_82", HB_TAG('c', 'v', '8', '2')); + feature_sets.insert("character_variant_83", HB_TAG('c', 'v', '8', '3')); + feature_sets.insert("character_variant_84", HB_TAG('c', 'v', '8', '4')); + feature_sets.insert("character_variant_85", HB_TAG('c', 'v', '8', '5')); + feature_sets.insert("character_variant_86", HB_TAG('c', 'v', '8', '6')); + feature_sets.insert("character_variant_87", HB_TAG('c', 'v', '8', '7')); + feature_sets.insert("character_variant_88", HB_TAG('c', 'v', '8', '8')); + feature_sets.insert("character_variant_89", HB_TAG('c', 'v', '8', '9')); + feature_sets.insert("character_variant_90", HB_TAG('c', 'v', '9', '0')); + feature_sets.insert("character_variant_91", HB_TAG('c', 'v', '9', '1')); + feature_sets.insert("character_variant_92", HB_TAG('c', 'v', '9', '2')); + feature_sets.insert("character_variant_93", HB_TAG('c', 'v', '9', '3')); + feature_sets.insert("character_variant_94", HB_TAG('c', 'v', '9', '4')); + feature_sets.insert("character_variant_95", HB_TAG('c', 'v', '9', '5')); + feature_sets.insert("character_variant_96", HB_TAG('c', 'v', '9', '6')); + feature_sets.insert("character_variant_97", HB_TAG('c', 'v', '9', '7')); + feature_sets.insert("character_variant_98", HB_TAG('c', 'v', '9', '8')); + feature_sets.insert("character_variant_99", HB_TAG('c', 'v', '9', '9')); + feature_sets.insert("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c')); + feature_sets.insert("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c')); + feature_sets.insert("distances", HB_TAG('d', 'i', 's', 't')); + feature_sets.insert("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g')); + feature_sets.insert("denominators", HB_TAG('d', 'n', 'o', 'm')); + feature_sets.insert("dotless_forms", HB_TAG('d', 't', 'l', 's')); + feature_sets.insert("expert_forms", HB_TAG('e', 'x', 'p', 't')); + feature_sets.insert("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't')); + feature_sets.insert("terminal_forms_2", HB_TAG('f', 'i', 'n', '2')); + feature_sets.insert("terminal_forms_3", HB_TAG('f', 'i', 'n', '3')); + feature_sets.insert("terminal_forms", HB_TAG('f', 'i', 'n', 'a')); + feature_sets.insert("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c')); + feature_sets.insert("fractions", HB_TAG('f', 'r', 'a', 'c')); + feature_sets.insert("full_widths", HB_TAG('f', 'w', 'i', 'd')); + feature_sets.insert("half_forms", HB_TAG('h', 'a', 'l', 'f')); + feature_sets.insert("halant_forms", HB_TAG('h', 'a', 'l', 'n')); + feature_sets.insert("alternate_half_widths", HB_TAG('h', 'a', 'l', 't')); + feature_sets.insert("historical_forms", HB_TAG('h', 'i', 's', 't')); + feature_sets.insert("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a')); + feature_sets.insert("historical_ligatures", HB_TAG('h', 'l', 'i', 'g')); + feature_sets.insert("hangul", HB_TAG('h', 'n', 'g', 'l')); + feature_sets.insert("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o')); + feature_sets.insert("half_widths", HB_TAG('h', 'w', 'i', 'd')); + feature_sets.insert("initial_forms", HB_TAG('i', 'n', 'i', 't')); + feature_sets.insert("isolated_forms", HB_TAG('i', 's', 'o', 'l')); + feature_sets.insert("italics", HB_TAG('i', 't', 'a', 'l')); + feature_sets.insert("justification_alternates", HB_TAG('j', 'a', 'l', 't')); + feature_sets.insert("jis78_forms", HB_TAG('j', 'p', '7', '8')); + feature_sets.insert("jis83_forms", HB_TAG('j', 'p', '8', '3')); + feature_sets.insert("jis90_forms", HB_TAG('j', 'p', '9', '0')); + feature_sets.insert("jis2004_forms", HB_TAG('j', 'p', '0', '4')); + feature_sets.insert("kerning", HB_TAG('k', 'e', 'r', 'n')); + feature_sets.insert("left_bounds", HB_TAG('l', 'f', 'b', 'd')); + feature_sets.insert("standard_ligatures", HB_TAG('l', 'i', 'g', 'a')); + feature_sets.insert("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o')); + feature_sets.insert("lining_figures", HB_TAG('l', 'n', 'u', 'm')); + feature_sets.insert("localized_forms", HB_TAG('l', 'o', 'c', 'l')); + feature_sets.insert("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a')); + feature_sets.insert("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm')); + feature_sets.insert("mark_positioning", HB_TAG('m', 'a', 'r', 'k')); + feature_sets.insert("medial_forms_2", HB_TAG('m', 'e', 'd', '2')); + feature_sets.insert("medial_forms", HB_TAG('m', 'e', 'd', 'i')); + feature_sets.insert("mathematical_greek", HB_TAG('m', 'g', 'r', 'k')); + feature_sets.insert("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k')); + feature_sets.insert("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't')); + feature_sets.insert("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't')); + feature_sets.insert("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k')); + feature_sets.insert("nukta_forms", HB_TAG('n', 'u', 'k', 't')); + feature_sets.insert("numerators", HB_TAG('n', 'u', 'm', 'r')); + feature_sets.insert("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm')); + feature_sets.insert("optical_bounds", HB_TAG('o', 'p', 'b', 'd')); + feature_sets.insert("ordinals", HB_TAG('o', 'r', 'd', 'n')); + feature_sets.insert("ornaments", HB_TAG('o', 'r', 'n', 'm')); + feature_sets.insert("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't')); + feature_sets.insert("petite_capitals", HB_TAG('p', 'c', 'a', 'p')); + feature_sets.insert("proportional_kana", HB_TAG('p', 'k', 'n', 'a')); + feature_sets.insert("proportional_figures", HB_TAG('p', 'n', 'u', 'm')); + feature_sets.insert("pre_base_forms", HB_TAG('p', 'r', 'e', 'f')); + feature_sets.insert("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's')); + feature_sets.insert("post_base_forms", HB_TAG('p', 's', 't', 'f')); + feature_sets.insert("post_base_substitutions", HB_TAG('p', 's', 't', 's')); + feature_sets.insert("proportional_widths", HB_TAG('p', 'w', 'i', 'd')); + feature_sets.insert("quarter_widths", HB_TAG('q', 'w', 'i', 'd')); + feature_sets.insert("randomize", HB_TAG('r', 'a', 'n', 'd')); + feature_sets.insert("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't')); + feature_sets.insert("rakar_forms", HB_TAG('r', 'k', 'r', 'f')); + feature_sets.insert("required_ligatures", HB_TAG('r', 'l', 'i', 'g')); + feature_sets.insert("reph_forms", HB_TAG('r', 'p', 'h', 'f')); + feature_sets.insert("right_bounds", HB_TAG('r', 't', 'b', 'd')); + feature_sets.insert("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a')); + feature_sets.insert("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm')); + feature_sets.insert("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y')); + feature_sets.insert("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n')); + feature_sets.insert("stylistic_alternates", HB_TAG('s', 'a', 'l', 't')); + feature_sets.insert("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f')); + feature_sets.insert("optical_size", HB_TAG('s', 'i', 'z', 'e')); + feature_sets.insert("small_capitals", HB_TAG('s', 'm', 'c', 'p')); + feature_sets.insert("simplified_forms", HB_TAG('s', 'm', 'p', 'l')); + feature_sets.insert("stylistic_set_01", HB_TAG('s', 's', '0', '1')); + feature_sets.insert("stylistic_set_02", HB_TAG('s', 's', '0', '2')); + feature_sets.insert("stylistic_set_03", HB_TAG('s', 's', '0', '3')); + feature_sets.insert("stylistic_set_04", HB_TAG('s', 's', '0', '4')); + feature_sets.insert("stylistic_set_05", HB_TAG('s', 's', '0', '5')); + feature_sets.insert("stylistic_set_06", HB_TAG('s', 's', '0', '6')); + feature_sets.insert("stylistic_set_07", HB_TAG('s', 's', '0', '7')); + feature_sets.insert("stylistic_set_08", HB_TAG('s', 's', '0', '8')); + feature_sets.insert("stylistic_set_09", HB_TAG('s', 's', '0', '9')); + feature_sets.insert("stylistic_set_10", HB_TAG('s', 's', '1', '0')); + feature_sets.insert("stylistic_set_11", HB_TAG('s', 's', '1', '1')); + feature_sets.insert("stylistic_set_12", HB_TAG('s', 's', '1', '2')); + feature_sets.insert("stylistic_set_13", HB_TAG('s', 's', '1', '3')); + feature_sets.insert("stylistic_set_14", HB_TAG('s', 's', '1', '4')); + feature_sets.insert("stylistic_set_15", HB_TAG('s', 's', '1', '5')); + feature_sets.insert("stylistic_set_16", HB_TAG('s', 's', '1', '6')); + feature_sets.insert("stylistic_set_17", HB_TAG('s', 's', '1', '7')); + feature_sets.insert("stylistic_set_18", HB_TAG('s', 's', '1', '8')); + feature_sets.insert("stylistic_set_19", HB_TAG('s', 's', '1', '9')); + feature_sets.insert("stylistic_set_20", HB_TAG('s', 's', '2', '0')); + feature_sets.insert("math_script_style_alternates", HB_TAG('s', 's', 't', 'y')); + feature_sets.insert("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h')); + feature_sets.insert("subscript", HB_TAG('s', 'u', 'b', 's')); + feature_sets.insert("superscript", HB_TAG('s', 'u', 'p', 's')); + feature_sets.insert("swash", HB_TAG('s', 'w', 's', 'h')); + feature_sets.insert("titling", HB_TAG('t', 'i', 't', 'l')); + feature_sets.insert("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o')); + feature_sets.insert("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm')); + feature_sets.insert("tabular_figures", HB_TAG('t', 'n', 'u', 'm')); + feature_sets.insert("traditional_forms", HB_TAG('t', 'r', 'a', 'd')); + feature_sets.insert("third_widths", HB_TAG('t', 'w', 'i', 'd')); + feature_sets.insert("unicase", HB_TAG('u', 'n', 'i', 'c')); + feature_sets.insert("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't')); + feature_sets.insert("vattu_variants", HB_TAG('v', 'a', 't', 'u')); + feature_sets.insert("vertical_writing", HB_TAG('v', 'e', 'r', 't')); + feature_sets.insert("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l')); + feature_sets.insert("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o')); + feature_sets.insert("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a')); + feature_sets.insert("vertical_kerning", HB_TAG('v', 'k', 'r', 'n')); + feature_sets.insert("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l')); + feature_sets.insert("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2')); + feature_sets.insert("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r')); + feature_sets.insert("slashed_zero", HB_TAG('z', 'e', 'r', 'o')); + // Registered OpenType variation tag. + feature_sets.insert("italic", HB_TAG('i', 't', 'a', 'l')); + feature_sets.insert("optical_size", HB_TAG('o', 'p', 's', 'z')); + feature_sets.insert("slant", HB_TAG('s', 'l', 'n', 't')); + feature_sets.insert("width", HB_TAG('w', 'd', 't', 'h')); + feature_sets.insert("weight", HB_TAG('w', 'g', 'h', 't')); +} int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { - for (int i = 0; feature_set[i].tag != 0; i++) { - if (feature_set[i].name == p_name) { - return feature_set[i].tag; - } + if (feature_sets.has(p_name)) { + return feature_sets[p_name]; } // No readable name, use tag string. @@ -707,9 +701,9 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { } String TextServerAdvanced::tag_to_name(int32_t p_tag) const { - for (int i = 0; feature_set[i].tag != 0; i++) { - if (feature_set[i].tag == p_tag) { - return feature_set[i].name; + for (const KeyValue<StringName, int32_t> &E : feature_sets) { + if (E.value == p_tag) { + return E.key; } } @@ -4633,30 +4627,76 @@ real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) con } struct num_system_data { - String lang; + Set<String> lang; String digits; String percent_sign; String exp; }; static num_system_data num_systems[]{ - { "ar,ar_AR,ar_BH,ar_DJ,ar_EG,ar_ER,ar_IL,ar_IQ,ar_JO,ar_KM,ar_KW,ar_LB,ar_MR,ar_OM,ar_PS,ar_QA,ar_SA,ar_SD,ar_SO,ar_SS,ar_SY,ar_TD,ar_YE", U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" }, - { "fa,ks,pa_Arab,ps,ug,ur_IN,ur,uz_Arab", U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" }, - { "as,bn,mni", U"০১২৩৪৫৬৭৮৯.", U"%", U"e" }, - { "mr,ne", U"०१२३४५६७८९.", U"%", U"e" }, - { "dz", U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" }, - { "sat", U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" }, - { "my", U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" }, - { String(), String(), String(), String() }, + { Set<String>(), U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" }, + { Set<String>(), U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" }, + { Set<String>(), U"০১২৩৪৫৬৭৮৯.", U"%", U"e" }, + { Set<String>(), U"०१२३४५६७८९.", U"%", U"e" }, + { Set<String>(), U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" }, + { Set<String>(), U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" }, + { Set<String>(), U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" }, + { Set<String>(), String(), String(), String() }, }; +static void _insert_num_systems_lang() { + num_systems[0].lang.insert(StringName("ar")); + num_systems[0].lang.insert(StringName("ar_AR")); + num_systems[0].lang.insert(StringName("ar_BH")); + num_systems[0].lang.insert(StringName("ar_DJ")); + num_systems[0].lang.insert(StringName("ar_EG")); + num_systems[0].lang.insert(StringName("ar_ER")); + num_systems[0].lang.insert(StringName("ar_IL")); + num_systems[0].lang.insert(StringName("ar_IQ")); + num_systems[0].lang.insert(StringName("ar_JO")); + num_systems[0].lang.insert(StringName("ar_KM")); + num_systems[0].lang.insert(StringName("ar_KW")); + num_systems[0].lang.insert(StringName("ar_LB")); + num_systems[0].lang.insert(StringName("ar_MR")); + num_systems[0].lang.insert(StringName("ar_OM")); + num_systems[0].lang.insert(StringName("ar_PS")); + num_systems[0].lang.insert(StringName("ar_QA")); + num_systems[0].lang.insert(StringName("ar_SA")); + num_systems[0].lang.insert(StringName("ar_SD")); + num_systems[0].lang.insert(StringName("ar_SO")); + num_systems[0].lang.insert(StringName("ar_SS")); + num_systems[0].lang.insert(StringName("ar_SY")); + num_systems[0].lang.insert(StringName("ar_TD")); + num_systems[0].lang.insert(StringName("ar_YE")); + + num_systems[1].lang.insert(StringName("fa")); + num_systems[1].lang.insert(StringName("ks")); + num_systems[1].lang.insert(StringName("pa_Arab")); + num_systems[1].lang.insert(StringName("ug")); + num_systems[1].lang.insert(StringName("ur_IN")); + num_systems[1].lang.insert(StringName("ur")); + num_systems[1].lang.insert(StringName("uz_Arab")); + + num_systems[2].lang.insert(StringName("as")); + num_systems[2].lang.insert(StringName("bn")); + num_systems[2].lang.insert(StringName("mni")); + + num_systems[3].lang.insert(StringName("mr")); + num_systems[3].lang.insert(StringName("ne")); + + num_systems[4].lang.insert(StringName("dz")); + + num_systems[5].lang.insert(StringName("sat")); + + num_systems[6].lang.insert(StringName("my")); +} + String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; } @@ -4677,12 +4717,11 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p } String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; } @@ -4706,11 +4745,10 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ } String TextServerAdvanced::percent_sign(const String &p_language) const { - String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; + const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; - for (int i = 0; num_systems[i].lang != String(); i++) { - Vector<String> langs = num_systems[i].lang.split(","); - if (langs.has(lang)) { + for (int i = 0; num_systems[i].lang.size() != 0; i++) { + if (num_systems[i].lang.has(lang)) { if (num_systems[i].percent_sign == String()) { return "%"; } @@ -4730,6 +4768,8 @@ void TextServerAdvanced::register_server() { } TextServerAdvanced::TextServerAdvanced() { + _insert_num_systems_lang(); + _insert_feature_sets(); hb_bmp_create_font_funcs(); } diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index 3f137ab7b3..43a8e20ef7 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -4,7 +4,7 @@ A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. </brief_description> <description> - This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer]. + This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer]. You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer]. </description> diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 1549a907b4..b5202469f1 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -5,7 +5,7 @@ </brief_description> <description> This class implements a WebSocket client compatible with any RFC 6455-compliant WebSocket server. - This client can be optionally used as a network peer for the [MultiplayerAPI]. + This client can be optionally used as a multiplayer peer for the [MultiplayerAPI]. After starting the client ([method connect_to_url]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). You will receive appropriate signals when connecting, disconnecting, or when new data is available. </description> @@ -20,7 +20,7 @@ <argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" /> <description> Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. - If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. + If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a multiplayer peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]). You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request. [b]Note:[/b] To avoid mixed content warnings or errors in HTML5, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's SSL certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the SSL certificate. diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index ac63379d71..c7a0ca100f 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -4,7 +4,7 @@ Base class for WebSocket server and client. </brief_description> <description> - Base class for WebSocket server and client, allowing them to be used as network peer for the [MultiplayerAPI]. + Base class for WebSocket server and client, allowing them to be used as multiplayer peer for the [MultiplayerAPI]. </description> <tutorials> </tutorials> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 90182de4c2..b66a1054ab 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -55,7 +55,7 @@ <description> Starts listening on the given port. You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested. - If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a network peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted. + If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a multiplayer peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted. If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.), on the [WebSocketPeer] returned via [code]get_peer(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]). </description> </method> diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 9280e35c3f..8401909384 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -31,6 +31,7 @@ #include "audio_stream_player_2d.h" #include "scene/2d/area_2d.h" +#include "scene/2d/listener_2d.h" #include "scene/main/window.h" void AudioStreamPlayer2D::_notification(int p_what) { @@ -150,13 +151,22 @@ void AudioStreamPlayer2D::_update_panning() { continue; } //compute matrix to convert to screen - Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform(); Vector2 screen_size = vp->get_visible_rect().size; + Vector2 listener_in_global; + Vector2 relative_to_listener; //screen in global is used for attenuation - Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5); + Listener2D *listener = vp->get_listener_2d(); + if (listener) { + listener_in_global = listener->get_global_position(); + relative_to_listener = global_pos - listener_in_global; + } else { + Transform2D to_listener = vp->get_global_canvas_transform() * vp->get_canvas_transform(); + listener_in_global = to_listener.affine_inverse().xform(screen_size * 0.5); + relative_to_listener = to_listener.xform(global_pos) - screen_size * 0.5; + } - float dist = global_pos.distance_to(screen_in_global); //distance to screen center + float dist = global_pos.distance_to(listener_in_global); // Distance to listener, or screen if none. if (dist > max_distance) { continue; //can't hear this sound in this viewport @@ -165,10 +175,7 @@ void AudioStreamPlayer2D::_update_panning() { float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); multiplier *= Math::db2linear(volume_db); //also apply player volume! - //point in screen is used for panning - Vector2 point_in_screen = to_screen.xform(global_pos); - - float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0); + float pan = CLAMP((relative_to_listener.x + screen_size.x * 0.5) / screen_size.x, 0.0, 1.0); float l = 1.0 - pan; float r = pan; diff --git a/scene/2d/listener_2d.cpp b/scene/2d/listener_2d.cpp new file mode 100644 index 0000000000..444f05f2b1 --- /dev/null +++ b/scene/2d/listener_2d.cpp @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* listener_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "listener_2d.h" + +bool Listener2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "current") { + if (p_value.operator bool()) { + make_current(); + } else { + clear_current(); + } + } else { + return false; + } + return true; +} + +bool Listener2D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "current") { + if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { + r_ret = current; + } else { + r_ret = is_current(); + } + } else { + return false; + } + return true; +} + +void Listener2D::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::BOOL, "current")); +} + +void Listener2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (!get_tree()->is_node_being_edited(this) && current) { + make_current(); + } + } break; + case NOTIFICATION_EXIT_TREE: { + if (!get_tree()->is_node_being_edited(this)) { + if (is_current()) { + clear_current(); + current = true; // Keep it true. + } else { + current = false; + } + } + } break; + } +} + +void Listener2D::make_current() { + current = true; + if (!is_inside_tree()) { + return; + } + get_viewport()->_listener_2d_set(this); +} + +void Listener2D::clear_current() { + current = false; + if (!is_inside_tree()) { + return; + } + get_viewport()->_listener_2d_remove(this); +} + +bool Listener2D::is_current() const { + if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { + return get_viewport()->get_listener_2d() == this; + } else { + return current; + } + return false; +} + +void Listener2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("make_current"), &Listener2D::make_current); + ClassDB::bind_method(D_METHOD("clear_current"), &Listener2D::clear_current); + ClassDB::bind_method(D_METHOD("is_current"), &Listener2D::is_current); +} diff --git a/scene/2d/listener_2d.h b/scene/2d/listener_2d.h new file mode 100644 index 0000000000..0289a8087d --- /dev/null +++ b/scene/2d/listener_2d.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* listener_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 LISTENER_2D_H +#define LISTENER_2D_H + +#include "scene/2d/node_2d.h" +#include "scene/main/window.h" + +class Listener2D : public Node2D { + GDCLASS(Listener2D, Node2D); + +private: + bool current = false; + + friend class Viewport; + +protected: + void _update_listener(); + + 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: + void make_current(); + void clear_current(); + bool is_current() const; +}; + +#endif diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 94fb49ae81..0f5de621ea 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -896,19 +896,25 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { int len = bones.size(); // calculate global rests and invert them - Vector<int> bones_to_process = get_parentless_bones(); + LocalVector<int> bones_to_process; + bones_to_process = get_parentless_bones(); while (bones_to_process.size() > 0) { int current_bone_idx = bones_to_process[0]; - bones_to_process.erase(current_bone_idx); const Bone &b = bonesptr[current_bone_idx]; - - // Note: the code below may not work by default. May need to track an integer for the bone pose index order - // in the while loop, instead of using current_bone_idx. - if (b.parent >= 0) { - skin->set_bind_pose(current_bone_idx, skin->get_bind_pose(b.parent) * b.rest); - } else { + bones_to_process.erase(current_bone_idx); + LocalVector<int> child_bones_vector; + child_bones_vector = get_bone_children(current_bone_idx); + int child_bones_size = child_bones_vector.size(); + if (b.parent < 0) { skin->set_bind_pose(current_bone_idx, b.rest); } + for (int i = 0; i < child_bones_size; i++) { + int child_bone_idx = child_bones_vector[i]; + const Bone &cb = bonesptr[child_bone_idx]; + skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); + // Add the bone's children to the list of bones to be processed. + bones_to_process.push_back(child_bones_vector[i]); + } } for (int i = 0; i < len; i++) { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index d846e395ad..953337ecce 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -56,6 +56,10 @@ protected: static void _bind_methods(); public: + // ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes, + // this value should be updated to reflect the new size. + static const int ITEM_PROPERTY_SIZE = 5; + void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1); void add_item(const String &p_label, int p_id = -1); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 5c427e43bc..428076c6da 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -144,6 +144,10 @@ protected: static void _bind_methods(); public: + // ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes, + // this value should be updated to reflect the new size. + static const int ITEM_PROPERTY_SIZE = 10; + void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index b9e9a4d450..f64c07df76 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -37,6 +37,7 @@ #include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "core/string/string_builder.h" #include "core/string/translation.h" #include "scene/main/window.h" @@ -78,7 +79,11 @@ void TextEdit::Text::set_font_size(int p_font_size) { } void TextEdit::Text::set_tab_size(int p_tab_size) { + if (tab_size == p_tab_size) { + return; + } tab_size = p_tab_size; + tab_size_dirty = true; } int TextEdit::Text::get_tab_size() const { @@ -118,10 +123,8 @@ int TextEdit::Text::get_line_width(int p_line, int p_wrap_index) const { return text[p_line].data_buf->get_size().x; } -int TextEdit::Text::get_line_height(int p_line, int p_wrap_index) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); - - return text[p_line].data_buf->get_line_size(p_wrap_index).y; +int TextEdit::Text::get_line_height() const { + return line_height; } void TextEdit::Text::set_width(float p_width) { @@ -153,6 +156,36 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const { return text[p_line].data; } +void TextEdit::Text::_calculate_line_height() { + int height = 0; + for (int i = 0; i < text.size(); i++) { + // Found another line with the same height...nothing to update. + if (text[i].height == line_height) { + height = line_height; + break; + } + height = MAX(height, text[i].height); + } + line_height = height; +} + +void TextEdit::Text::_calculate_max_line_width() { + int width = 0; + for (int i = 0; i < text.size(); i++) { + if (is_hidden(i)) { + continue; + } + + // Found another line with the same width...nothing to update. + if (text[i].width == max_width) { + width = max_width; + break; + } + width = MAX(width, text[i].width); + } + max_width = width; +} + void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); @@ -182,17 +215,54 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); text.write[p_line].data_buf->tab_align(tabs); } + + // Update height. + const int old_height = text.write[p_line].height; + const int wrap_amount = get_line_wrap_amount(p_line); + int height = font->get_height(font_size); + for (int i = 0; i <= wrap_amount; i++) { + height = MAX(height, text[p_line].data_buf->get_line_size(i).y); + } + text.write[p_line].height = height; + + // If this line has shrunk, this may no longer the the tallest line. + if (old_height == line_height && height < line_height) { + _calculate_line_height(); + } else { + line_height = MAX(height, line_height); + } + + // Update width. + const int old_width = text.write[p_line].width; + int width = get_line_width(p_line); + text.write[p_line].width = width; + + // If this line has shrunk, this may no longer the the longest line. + if (old_width == max_width && width < max_width) { + _calculate_max_line_width(); + } else if (!is_hidden(p_line)) { + max_width = MAX(width, max_width); + } } void TextEdit::Text::invalidate_all_lines() { for (int i = 0; i < text.size(); i++) { text.write[i].data_buf->set_width(width); - if (tab_size > 0) { - Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); - text.write[i].data_buf->tab_align(tabs); + if (tab_size_dirty) { + if (tab_size > 0) { + Vector<float> tabs; + tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); + text.write[i].data_buf->tab_align(tabs); + } + // Tabs have changes, force width update. + text.write[i].width = get_line_width(i); } } + + if (tab_size_dirty) { + _calculate_max_line_width(); + tab_size_dirty = false; + } } void TextEdit::Text::invalidate_all() { @@ -211,16 +281,8 @@ void TextEdit::Text::clear() { insert(0, "", Vector<Vector2i>()); } -int TextEdit::Text::get_max_width(bool p_exclude_hidden) const { - // Quite some work, but should be fast enough. - - int max = 0; - for (int i = 0; i < text.size(); i++) { - if (!p_exclude_hidden || !is_hidden(i)) { - max = MAX(max, get_line_width(i)); - } - } - return max; +int TextEdit::Text::get_max_width() const { + return max_width; } void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) { @@ -243,7 +305,20 @@ void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2 } void TextEdit::Text::remove(int p_at) { + int height = text[p_at].height; + int width = text[p_at].width; + text.remove(p_at); + + // If this is the tallest line, we need to get the next tallest. + if (height == line_height) { + _calculate_line_height(); + } + + // If this is the longest line, we need to get the next longest. + if (width == max_width) { + _calculate_max_line_width(); + } } void TextEdit::Text::add_gutter(int p_at) { @@ -293,7 +368,7 @@ void TextEdit::_notification(int p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible()) { call_deferred(SNAME("_update_scrollbars")); - call_deferred(SNAME("_update_wrap_at")); + call_deferred(SNAME("_update_wrap_at_column")); } } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: @@ -644,8 +719,9 @@ void TextEdit::_notification(int p_what) { int characters = 0; int tabs = 0; for (int j = 0; j < str.length(); j++) { - if (color_map.has(last_wrap_column + j)) { - current_color = color_map[last_wrap_column + j].get("color"); + const Variant *color_data = color_map.getptr(last_wrap_column + j); + if (color_data != nullptr) { + current_color = (color_data->operator Dictionary()).get("color", font_color); if (!editable) { current_color.a = font_readonly_color.a; } @@ -1023,8 +1099,9 @@ void TextEdit::_notification(int p_what) { char_ofs = 0; } for (int j = 0; j < gl_size; j++) { - if (color_map.has(glyphs[j].start)) { - current_color = color_map[glyphs[j].start].get("color"); + const Variant *color_data = color_map.getptr(glyphs[j].start); + if (color_data != nullptr) { + current_color = (color_data->operator Dictionary()).get("color", font_color); if (!editable && current_color.a > font_readonly_color.a) { current_color.a = font_readonly_color.a; } @@ -2548,15 +2625,15 @@ void TextEdit::set_text(const String &p_text) { } String TextEdit::get_text() const { - String longthing; - int len = text.size(); - for (int i = 0; i < len; i++) { - longthing += text[i]; - if (i != len - 1) { - longthing += "\n"; + StringBuilder ret_text; + const int text_size = text.size(); + for (int i = 0; i < text_size; i++) { + ret_text += text[i]; + if (i != text_size - 1) { + ret_text += "\n"; } } - return longthing; + return ret_text.as_string(); } int TextEdit::get_line_count() const { @@ -2592,13 +2669,7 @@ int TextEdit::get_line_width(int p_line, int p_wrap_index) const { } int TextEdit::get_line_height() const { - int height = font->get_height(font_size); - for (int i = 0; i < text.size(); i++) { - for (int j = 0; j <= text.get_line_wrap_amount(i); j++) { - height = MAX(height, text.get_line_height(i, j)); - } - } - return height + line_spacing; + return text.get_line_height() + line_spacing; } int TextEdit::get_indent_level(int p_line) const { @@ -4151,6 +4222,9 @@ TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const { void TextEdit::set_gutter_width(int p_gutter, int p_width) { ERR_FAIL_INDEX(p_gutter, gutters.size()); + if (gutters[p_gutter].width == p_width) { + return; + } gutters.write[p_gutter].width = p_width; _update_gutter_width(); } @@ -4166,6 +4240,9 @@ int TextEdit::get_total_gutter_width() const { void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) { ERR_FAIL_INDEX(p_gutter, gutters.size()); + if (gutters[p_gutter].draw == p_draw) { + return; + } gutters.write[p_gutter].draw = p_draw; _update_gutter_width(); } @@ -5458,7 +5535,7 @@ void TextEdit::_update_scrollbars() { } int visible_width = size.width - style_normal->get_minimum_size().width; - int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding; + int total_width = text.get_max_width() + vmin.x + gutters_width + gutter_padding; if (draw_minimap) { total_width += minimap_width; @@ -5910,8 +5987,6 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i text.set_hidden(p_line, false); } - text.invalidate_cache(p_line); - r_end_line = p_line + substrings.size() - 1; r_end_column = text[r_end_line].length() - postinsert_text.length(); @@ -5968,8 +6043,6 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li } text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text)); - text.invalidate_cache(p_from_line); - if (!text_changed_dirty && !setting_text) { if (is_inside_tree()) { MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); @@ -5986,7 +6059,6 @@ TextEdit::TextEdit() { set_default_cursor_shape(CURSOR_IBEAM); text.set_tab_size(text.get_tab_size()); - text.clear(); h_scroll = memnew(HScrollBar); v_scroll = memnew(VScrollBar); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 07999c2442..e996bba983 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -144,6 +144,8 @@ private: Color background_color = Color(0, 0, 0, 0); bool hidden = false; + int height = 0; + int width = 0; Line() { data_buf.instantiate(); @@ -152,6 +154,7 @@ private: private: bool is_dirty = false; + bool tab_size_dirty = false; mutable Vector<Line> text; Ref<Font> font; @@ -162,11 +165,16 @@ private: TextServer::Direction direction = TextServer::DIRECTION_AUTO; bool draw_control_chars = false; + int line_height = -1; + int max_width = -1; int width = -1; int tab_size = 4; int gutter_count = 0; + void _calculate_line_height(); + void _calculate_max_line_width(); + public: void set_tab_size(int p_tab_size); int get_tab_size() const; @@ -176,9 +184,9 @@ private: void set_direction_and_language(TextServer::Direction p_direction, const String &p_language); void set_draw_control_chars(bool p_draw_control_chars); - int get_line_height(int p_line, int p_wrap_index) const; + int get_line_height() const; int get_line_width(int p_line, int p_wrap_index = -1) const; - int get_max_width(bool p_exclude_hidden = false) const; + int get_max_width() const; void set_width(float p_width); int get_line_wrap_amount(int p_line) const; @@ -187,7 +195,14 @@ private: const Ref<TextParagraph> get_line_data(int p_line) const; void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override); - void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; } + void set_hidden(int p_line, bool p_hidden) { + text.write[p_line].hidden = p_hidden; + if (!p_hidden && text[p_line].width > max_width) { + max_width = text[p_line].width; + } else if (p_hidden && text[p_line].width == max_width) { + _calculate_max_line_width(); + } + } bool is_hidden(int p_line) const { return text[p_line].hidden; } void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override); void remove(int p_at); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6ffc8475f5..05409b7bfe 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -516,24 +516,24 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int } } -void Node::set_network_authority(int p_peer_id, bool p_recursive) { - data.network_authority = p_peer_id; +void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) { + data.multiplayer_authority = p_peer_id; if (p_recursive) { for (int i = 0; i < data.children.size(); i++) { - data.children[i]->set_network_authority(p_peer_id, true); + data.children[i]->set_multiplayer_authority(p_peer_id, true); } } } -int Node::get_network_authority() const { - return data.network_authority; +int Node::get_multiplayer_authority() const { + return data.multiplayer_authority; } -bool Node::is_network_authority() const { +bool Node::is_multiplayer_authority() const { ERR_FAIL_COND_V(!is_inside_tree(), false); - return get_multiplayer()->get_network_unique_id() == data.network_authority; + return get_multiplayer()->get_unique_id() == data.multiplayer_authority; } /***** RPC CONFIG ********/ @@ -2737,10 +2737,10 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready); - ClassDB::bind_method(D_METHOD("set_network_authority", "id", "recursive"), &Node::set_network_authority, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_network_authority"), &Node::get_network_authority); + ClassDB::bind_method(D_METHOD("set_multiplayer_authority", "id", "recursive"), &Node::set_multiplayer_authority, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_multiplayer_authority"), &Node::get_multiplayer_authority); - ClassDB::bind_method(D_METHOD("is_network_authority"), &Node::is_network_authority); + ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority); ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); diff --git a/scene/main/node.h b/scene/main/node.h index 27d6958140..198501eeac 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -127,7 +127,7 @@ private: ProcessMode process_mode = PROCESS_MODE_INHERIT; Node *process_owner = nullptr; - int network_authority = 1; // Server by default. + int multiplayer_authority = 1; // Server by default. Vector<Multiplayer::RPCConfig> rpc_methods; // Variables used to properly sort the node when processing, ignored otherwise. @@ -462,9 +462,9 @@ public: bool is_displayed_folded() const; /* NETWORK */ - void set_network_authority(int p_peer_id, bool p_recursive = true); - int get_network_authority() const; - bool is_network_authority() const; + void set_multiplayer_authority(int p_peer_id, bool p_recursive = true); + int get_multiplayer_authority() const; + bool is_multiplayer_authority() const; uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 8260d0dff5..bef7ecb462 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -858,15 +858,6 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio } } -/* -void SceneMainLoop::_update_listener_2d() { - if (listener_2d.is_valid()) { - SpatialSound2DServer::get_singleton()->listener_set_space( listener_2d, world_2d->get_sound_space() ); - } -} - -*/ - void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport) { Map<StringName, Group>::Element *E = group_map.find(p_group); if (!E) { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8ec8c193fb..c5b0f47140 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -37,6 +37,7 @@ #include "core/templates/pair.h" #include "scene/2d/camera_2d.h" #include "scene/2d/collision_object_2d.h" +#include "scene/2d/listener_2d.cpp" #ifndef _3D_DISABLED #include "scene/3d/camera_3d.h" #include "scene/3d/collision_object_3d.h" @@ -831,7 +832,6 @@ void Viewport::set_as_audio_listener_2d(bool p_enable) { } audio_listener_2d = p_enable; - _update_listener_2d(); } @@ -839,6 +839,10 @@ bool Viewport::is_audio_listener_2d() const { return audio_listener_2d; } +Listener2D *Viewport::get_listener_2d() const { + return listener_2d; +} + void Viewport::enable_canvas_transform_override(bool p_enable) { if (override_canvas_transform == p_enable) { return; @@ -903,6 +907,21 @@ void Viewport::_camera_2d_set(Camera2D *p_camera_2d) { camera_2d = p_camera_2d; } +void Viewport::_listener_2d_set(Listener2D *p_listener) { + if (listener_2d == p_listener) { + return; + } else if (listener_2d) { + listener_2d->clear_current(); + } + listener_2d = p_listener; +} + +void Viewport::_listener_2d_remove(Listener2D *p_listener) { + if (listener_2d == p_listener) { + listener_2d = nullptr; + } +} + void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) { canvas_layers.insert(p_canvas_layer); } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index d9b21ce6a8..06efd27073 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -41,6 +41,7 @@ class Listener3D; class World3D; #endif // _3D_DISABLED +class Listener2D; class Camera2D; class CanvasItem; class CanvasLayer; @@ -201,6 +202,7 @@ private: Viewport *parent = nullptr; + Listener2D *listener_2d = nullptr; Camera2D *camera_2d = nullptr; Set<CanvasLayer *> canvas_layers; @@ -416,6 +418,10 @@ private: bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check); + friend class Listener2D; + void _listener_2d_set(Listener2D *p_listener); + void _listener_2d_remove(Listener2D *p_listener); + friend class Camera2D; void _camera_2d_set(Camera2D *p_camera_2d); @@ -457,6 +463,7 @@ protected: public: uint64_t get_processed_events_count() const { return event_count; } + Listener2D *get_listener_2d() const; Camera2D *get_camera_2d() const; void set_as_audio_listener_2d(bool p_enable); bool is_audio_listener_2d() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 25c8e3e0b7..015a4d5dba 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -49,6 +49,7 @@ #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/line_2d.h" +#include "scene/2d/listener_2d.h" #include "scene/2d/mesh_instance_2d.h" #include "scene/2d/multimesh_instance_2d.h" #include "scene/2d/navigation_agent_2d.h" @@ -671,6 +672,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init GDREGISTER_CLASS(Camera2D); + GDREGISTER_CLASS(Listener2D); GDREGISTER_VIRTUAL_CLASS(Joint2D); GDREGISTER_CLASS(PinJoint2D); GDREGISTER_CLASS(GrooveJoint2D); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 1d2a2ef26c..77a68151c4 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -963,7 +963,9 @@ void BaseMaterial3D::_update_shader() { } else { code += " float depth = 1.0 - texture(texture_heightmap, base_uv).r;\n"; } - code += " vec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n"; + // Use offset limiting to improve the appearance of non-deep parallax. + // This reduces the impression of depth, but avoids visible warping in the distance. + code += " vec2 ofs = base_uv - view_dir.xy * depth * heightmap_scale;\n"; } code += " base_uv=ofs;\n"; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index b863a309c0..341ce22185 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1849,10 +1849,16 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } if (groups.size()) { + // Write all groups on the same line as they're part of a section header. + // This improves readability while not impacting VCS friendliness too much, + // since it's rare to have more than 5 groups assigned to a single node. groups.sort_custom<StringName::AlphCompare>(); - String sgroups = " groups=[\n"; + String sgroups = " groups=["; for (int j = 0; j < groups.size(); j++) { - sgroups += "\"" + String(groups[j]).c_escape() + "\",\n"; + sgroups += "\"" + String(groups[j]).c_escape() + "\""; + if (j < groups.size() - 1) { + sgroups += ", "; + } } sgroups += "]"; header += sgroups; diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp index 098e2a5c87..36943c5e5c 100644 --- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp @@ -1469,7 +1469,7 @@ void RendererSceneGIRD::SDFGI::pre_process_gi(const Transform3D &p_transform, Re lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; lights[idx].type = RS::LIGHT_DIRECTIONAL; - lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); lights[idx].has_shadow = storage->light_has_shadow(li->light); idx++; @@ -1514,7 +1514,7 @@ void RendererSceneGIRD::SDFGI::pre_process_gi(const Transform3D &p_transform, Re lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; lights[idx].type = storage->light_get_type(li->light); - lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); lights[idx].has_shadow = storage->light_has_shadow(li->light); lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); @@ -1953,7 +1953,7 @@ void RendererSceneGIRD::SDFGI::render_static_lights(RID p_render_buffers, uint32 lights[idx].color[0] = color.r; lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; - lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); lights[idx].has_shadow = storage->light_has_shadow(li->light); lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl index 4411587116..1ce3e04421 100644 --- a/servers/rendering/renderer_rd/shaders/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl @@ -169,16 +169,33 @@ vec3 tonemap_filmic(vec3 color, float white) { return color_tonemapped / white_tonemapped; } +// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl +// (MIT License). vec3 tonemap_aces(vec3 color, float white) { - const float exposure_bias = 0.85f; - const float A = 2.51f * exposure_bias * exposure_bias; - const float B = 0.03f * exposure_bias; - const float C = 2.43f * exposure_bias * exposure_bias; - const float D = 0.59f * exposure_bias; - const float E = 0.14f; - - vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E); - float white_tonemapped = (white * (A * white + B)) / (white * (C * white + D) + E); + const float exposure_bias = 1.8f; + const float A = 0.0245786f; + const float B = 0.000090537f; + const float C = 0.983729f; + const float D = 0.432951f; + const float E = 0.238081f; + + // Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias` + const mat3 rgb_to_rrt = mat3( + vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias), + vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias), + vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias)); + + const mat3 odt_to_rgb = mat3( + vec3(1.60475f, -0.53108f, -0.07367f), + vec3(-0.10208f, 1.10813f, -0.00605f), + vec3(-0.00327f, -0.07276f, 1.07602f)); + + color *= rgb_to_rrt; + vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E); + color_tonemapped *= odt_to_rgb; + + white *= exposure_bias; + float white_tonemapped = (white * (white + A) - B) / (white * (C * white + D) + E); return color_tonemapped / white_tonemapped; } @@ -200,15 +217,16 @@ vec3 linear_to_srgb(vec3 color) { #define TONEMAPPER_ACES 3 vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color - + // Ensure color values passed to tonemappers are positive. + // They can be negative in the case of negative lights, which leads to undesired behavior. if (params.tonemapper == TONEMAPPER_LINEAR) { return color; } else if (params.tonemapper == TONEMAPPER_REINHARD) { - return tonemap_reinhard(color, white); + return tonemap_reinhard(max(vec3(0.0f), color), white); } else if (params.tonemapper == TONEMAPPER_FILMIC) { - return tonemap_filmic(color, white); + return tonemap_filmic(max(vec3(0.0f), color), white); } else { // TONEMAPPER_ACES - return tonemap_aces(color, white); + return tonemap_aces(max(vec3(0.0f), color), white); } } @@ -401,9 +419,7 @@ void main() { color += screen_space_dither(gl_FragCoord.xy); } - // Ensure color values passed to tonemappers are positive. - // They can be negative in the case of negative lights, which leads to undesired behavior. - color = apply_tonemapping(max(vec3(0.0), color), params.white); + color = apply_tonemapping(color, params.white); color = linear_to_srgb(color); // regular linear -> SRGB conversion diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index b302c6b793..70f676e5ac 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -478,12 +478,28 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("get_memory_usage"), &RenderingDevice::get_memory_usage); + ClassDB::bind_method(D_METHOD("get_driver_resource", "resource", "rid", "index"), &RenderingDevice::get_driver_resource); + BIND_CONSTANT(BARRIER_MASK_RASTER); BIND_CONSTANT(BARRIER_MASK_COMPUTE); BIND_CONSTANT(BARRIER_MASK_TRANSFER); BIND_CONSTANT(BARRIER_MASK_ALL); BIND_CONSTANT(BARRIER_MASK_NO_BARRIER); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_DEVICE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_INSTANCE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_QUEUE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_IMAGE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_IMAGE_VIEW); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_SAMPLER); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_BUFFER); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE); + BIND_ENUM_CONSTANT(DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE); + BIND_ENUM_CONSTANT(DATA_FORMAT_R4G4_UNORM_PACK8); BIND_ENUM_CONSTANT(DATA_FORMAT_R4G4B4A4_UNORM_PACK16); BIND_ENUM_CONSTANT(DATA_FORMAT_B4G4R4A4_UNORM_PACK16); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 2cf1f165dd..5eb8f1cead 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -60,6 +60,23 @@ public: DEVICE_DIRECTX }; + enum DriverResource { + DRIVER_RESOURCE_VULKAN_DEVICE = 0, + DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE, + DRIVER_RESOURCE_VULKAN_INSTANCE, + DRIVER_RESOURCE_VULKAN_QUEUE, + DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX, + DRIVER_RESOURCE_VULKAN_IMAGE, + DRIVER_RESOURCE_VULKAN_IMAGE_VIEW, + DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT, + DRIVER_RESOURCE_VULKAN_SAMPLER, + DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET, + DRIVER_RESOURCE_VULKAN_BUFFER, + DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE, + DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE, + //next driver continue enum from 1000 to keep order + }; + enum ShaderStage { SHADER_STAGE_VERTEX, SHADER_STAGE_FRAGMENT, @@ -1183,6 +1200,8 @@ public: virtual String get_device_name() const = 0; virtual String get_device_pipeline_cache_uuid() const = 0; + virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0) = 0; + static RenderingDevice *get_singleton(); RenderingDevice(); @@ -1217,6 +1236,7 @@ protected: Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); }; +VARIANT_ENUM_CAST(RenderingDevice::DriverResource) VARIANT_ENUM_CAST(RenderingDevice::ShaderStage) VARIANT_ENUM_CAST(RenderingDevice::ShaderLanguage) VARIANT_ENUM_CAST(RenderingDevice::CompareOperator) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 78604dfe8c..db0011aa60 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1467,6 +1467,11 @@ ShaderLanguage::DataType RenderingServer::global_variable_type_get_shader_dataty } } +RenderingDevice *RenderingServer::get_rendering_device() const { + // return the rendering device we're using globally + return RenderingDevice::get_singleton(); +} + RenderingDevice *RenderingServer::create_local_rendering_device() const { return RenderingDevice::get_singleton()->create_local_device(); } @@ -2714,6 +2719,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("force_sync"), &RenderingServer::sync); ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &RenderingServer::draw, DEFVAL(true), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device); ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device); } diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 8fbf231d9b..b79aaefab4 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1493,6 +1493,7 @@ public: virtual void set_print_gpu_profile(bool p_enable) = 0; + RenderingDevice *get_rendering_device() const; RenderingDevice *create_local_rendering_device() const; bool is_render_loop_enabled() const; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index fc1d82a964..bf54158905 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "xr_interface.h" -#include "servers/rendering/renderer_compositor.h" +// #include "servers/rendering/renderer_compositor.h" void XRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name); diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index 5c90139375..6340485bde 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "xr_interface_extension.h" -#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_storage.h" +#include "servers/rendering/rendering_server_globals.h" void XRInterfaceExtension::_bind_methods() { GDVIRTUAL_BIND(_get_name); @@ -41,8 +42,6 @@ void XRInterfaceExtension::_bind_methods() { GDVIRTUAL_BIND(_get_tracking_status); - ClassDB::bind_method(D_METHOD("add_blit", "render_target", "src_rect", "dst_rect", "use_layer", "layer", "apply_lens_distortion", "eye_center", "k1", "k2", "upscale", "aspect_ratio"), &XRInterfaceExtension::add_blit); - GDVIRTUAL_BIND(_get_render_target_size); GDVIRTUAL_BIND(_get_view_count); GDVIRTUAL_BIND(_get_camera_transform); @@ -60,6 +59,11 @@ void XRInterfaceExtension::_bind_methods() { GDVIRTUAL_BIND(_get_anchor_detection_is_enabled); GDVIRTUAL_BIND(_set_anchor_detection_is_enabled, "enabled"); GDVIRTUAL_BIND(_get_camera_feed_id); + + // helper methods + ClassDB::bind_method(D_METHOD("add_blit", "render_target", "src_rect", "dst_rect", "use_layer", "layer", "apply_lens_distortion", "eye_center", "k1", "k2", "upscale", "aspect_ratio"), &XRInterfaceExtension::add_blit); + ClassDB::bind_method(D_METHOD("get_render_target_texture", "render_target"), &XRInterfaceExtension::get_render_target_texture); + // ClassDB::bind_method(D_METHOD("get_render_target_depth", "render_target"), &XRInterfaceExtension::get_render_target_depth); } StringName XRInterfaceExtension::get_name() const { @@ -240,3 +244,19 @@ void XRInterfaceExtension::process() { void XRInterfaceExtension::notification(int p_what) { GDVIRTUAL_CALL(_notification, p_what); } + +RID XRInterfaceExtension::get_render_target_texture(RID p_render_target) { + RendererStorage *storage = RSG::storage; + ERR_FAIL_NULL_V_MSG(storage, RID(), "Renderer storage not setup"); + + return storage->render_target_get_texture(p_render_target); +} + +/* +RID XRInterfaceExtension::get_render_target_depth(RID p_render_target) { + RendererStorage *storage = RSG::storage; + ERR_FAIL_NULL_V_MSG(storage, RID(), "Renderer storage not setup"); + + return storage->render_target_get_depth(p_render_target); +} +*/ diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h index df6f5f8e35..94914a7b3f 100644 --- a/servers/xr/xr_interface_extension.h +++ b/servers/xr/xr_interface_extension.h @@ -100,6 +100,10 @@ public: GDVIRTUAL0(_process); GDVIRTUAL1(_notification, int); + + /* access to some internals we need */ + RID get_render_target_texture(RID p_render_target); + // RID get_render_target_depth(RID p_render_target); }; #endif // !XR_INTERFACE_EXTENSION_H |