summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp41
-rw-r--r--modules/enet/enet_multiplayer_peer.h12
-rw-r--r--modules/gdnative/include/net/godot_net.h2
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp11
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h2
-rw-r--r--modules/gdscript/gdscript_editor.cpp1
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp28
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp125
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h3
-rw-r--r--modules/gdscript/language_server/lsp.hpp60
-rw-r--r--modules/mono/csharp_script.cpp7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs259
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs4
-rw-r--r--modules/mono/glue/collections_glue.cpp15
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml2
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp97
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h24
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp8
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h2
25 files changed, 624 insertions, 127 deletions
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index 28b6bb035a..38ca38385c 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -33,6 +33,14 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
+void ENetMultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int ENetMultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode;
}
@@ -234,8 +242,8 @@ bool ENetMultiplayerPeer::_poll_client() {
}
switch (ret) {
case ENetConnection::EVENT_CONNECT: {
- emit_signal(SNAME("peer_connected"), 1);
connection_status = CONNECTION_CONNECTED;
+ emit_signal(SNAME("peer_connected"), 1);
emit_signal(SNAME("connection_succeeded"));
return false;
}
@@ -441,20 +449,23 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
int packet_flags = 0;
int channel = SYSCH_RELIABLE;
-
- switch (transfer_mode) {
- case TRANSFER_MODE_UNRELIABLE: {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED: {
- packet_flags = 0;
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_RELIABLE: {
- packet_flags = ENET_PACKET_FLAG_RELIABLE;
- channel = SYSCH_RELIABLE;
- } break;
+ if (transfer_channel > 0) {
+ channel = SYSCH_MAX + transfer_channel - 1;
+ } else {
+ switch (transfer_mode) {
+ case TRANSFER_MODE_UNRELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ packet_flags = 0;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case TRANSFER_MODE_RELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_RELIABLE;
+ channel = SYSCH_RELIABLE;
+ } break;
+ }
}
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index 703396d0cb..78e280db7c 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -47,10 +47,10 @@ private:
};
enum {
- SYSCH_CONFIG,
- SYSCH_RELIABLE,
- SYSCH_UNRELIABLE,
- SYSCH_MAX
+ SYSCH_CONFIG = 0,
+ SYSCH_RELIABLE = 1,
+ SYSCH_UNRELIABLE = 2,
+ SYSCH_MAX = 3
};
enum Mode {
@@ -65,6 +65,7 @@ private:
uint32_t unique_id = 0;
int target_peer = 0;
+ int transfer_channel = 0;
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
bool refuse_connections = false;
@@ -100,6 +101,9 @@ protected:
static void _bind_methods();
public:
+ virtual void set_transfer_channel(int p_channel) override;
+ virtual int get_transfer_channel() const override;
+
virtual void set_transfer_mode(TransferMode p_mode) override;
virtual TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer) override;
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
index 94e7739ef9..3fb7b9e1cc 100644
--- a/modules/gdnative/include/net/godot_net.h
+++ b/modules/gdnative/include/net/godot_net.h
@@ -91,6 +91,8 @@ typedef struct {
godot_int (*get_max_packet_size)(const void *);
/* This is MultiplayerPeer */
+ void (*set_transfer_channel)(void *, godot_int);
+ godot_int (*get_transfer_channel)(void *);
void (*set_transfer_mode)(void *, godot_int);
godot_int (*get_transfer_mode)(const void *);
// 0 = broadcast, 1 = server, <0 = all but abs(value)
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
index 8ceba0f339..9908ed4533 100644
--- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
@@ -62,6 +62,16 @@ int MultiplayerPeerGDNative::get_available_packet_count() const {
}
/* MultiplayerPeer */
+void MultiplayerPeerGDNative::set_transfer_channel(int p_channel) {
+ ERR_FAIL_COND(interface == nullptr);
+ return interface->set_transfer_channel(interface->data, p_channel);
+}
+
+int MultiplayerPeerGDNative::get_transfer_channel() const {
+ ERR_FAIL_COND_V(interface == nullptr, 0);
+ return interface->get_transfer_channel(interface->data);
+}
+
void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
ERR_FAIL_COND(interface == nullptr);
interface->set_transfer_mode(interface->data, (godot_int)p_mode);
@@ -113,6 +123,7 @@ MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status
}
void MultiplayerPeerGDNative::_bind_methods() {
+ ADD_PROPERTY_DEFAULT("transfer_channel", 0);
ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE);
ADD_PROPERTY_DEFAULT("refuse_new_connections", true);
}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
index 7c10ab77f7..ab084faae6 100644
--- a/modules/gdnative/net/multiplayer_peer_gdnative.h
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.h
@@ -56,6 +56,8 @@ public:
virtual int get_available_packet_count() const override;
/* Specific to MultiplayerPeer */
+ virtual void set_transfer_channel(int p_channel) override;
+ virtual int get_transfer_channel() const override;
virtual void set_transfer_mode(TransferMode p_mode) override;
virtual TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer_id) override;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2a93bb620b..2e570d5a5b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2795,6 +2795,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
if (base_type.class_type->has_member(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
r_result.location = base_type.class_type->get_member(p_symbol).get_line();
+ r_result.class_path = base_type.script_path;
return OK;
}
base_type = base_type.class_type->base_type;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 466ddb4b10..a500dfd51a 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -168,7 +168,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
// Networking.
- register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTER>, 4, true);
+ register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>, 4, true);
// TODO: Warning annotations.
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 0d1f98778e..b6c48468f5 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -128,13 +128,13 @@ Error GDScriptLanguageProtocol::on_client_connected() {
peer->connection = tcp_peer;
clients.set(next_client_id, peer);
next_client_id++;
- EditorNode::get_log()->add_message("Connection Taken", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
}
void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
clients.erase(p_client_id);
- EditorNode::get_log()->add_message("Disconnected", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Disconnected", EditorLog::MSG_TYPE_EDITOR);
}
String GDScriptLanguageProtocol::process_message(const String &p_text) {
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 69ddbe5d1e..c0013ac23a 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -42,10 +42,12 @@ void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+ ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve);
+ ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename);
ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange);
ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens);
ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink);
@@ -79,6 +81,20 @@ void GDScriptTextDocument::didChange(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::didSave(const Variant &p_param) {
+ lsp::TextDocumentItem doc = load_document_item(p_param);
+ Dictionary dict = p_param;
+ String text = dict["text"];
+
+ sync_script_content(doc.uri, text);
+
+ /*String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
+
+ Ref<GDScript> script = ResourceLoader::load(path);
+ script->load_source_code(path);
+ script->reload(true);*/
+}
+
lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
lsp::TextDocumentItem doc;
Dictionary params = p_param;
@@ -215,6 +231,14 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
return arr;
}
+Dictionary GDScriptTextDocument::rename(const Dictionary &p_params) {
+ lsp::TextDocumentPositionParams params;
+ params.load(p_params);
+ String new_name = p_params["newName"];
+
+ return GDScriptLanguageProtocol::get_singleton()->get_workspace()->rename(params, new_name);
+}
+
Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
lsp::CompletionItem item;
item.load(p_params);
@@ -405,7 +429,11 @@ GDScriptTextDocument::~GDScriptTextDocument() {
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
+
EditorFileSystem::get_singleton()->update_file(path);
+ Ref<GDScript> script = ResourceLoader::load(path);
+ script->load_source_code(path);
+ script->reload(true);
}
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index e2987f779c..9021c84a3f 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -45,6 +45,7 @@ protected:
void didOpen(const Variant &p_param);
void didClose(const Variant &p_param);
void didChange(const Variant &p_param);
+ void didSave(const Variant &p_param);
void sync_script_content(const String &p_path, const String &p_content);
void show_native_symbol_in_editor(const String &p_symbol_id);
@@ -61,6 +62,7 @@ public:
Array documentSymbol(const Dictionary &p_params);
Array completion(const Dictionary &p_params);
Dictionary resolve(const Dictionary &p_params);
+ Dictionary rename(const Dictionary &p_params);
Array foldingRange(const Dictionary &p_params);
Array codeLens(const Dictionary &p_params);
Array documentLink(const Dictionary &p_params);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index e6c819b22f..4fc229a0f8 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -116,6 +116,36 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_
return nullptr;
}
+const lsp::DocumentSymbol *GDScriptWorkspace::get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier) {
+ for (int i = 0; i < p_parent->children.size(); ++i) {
+ const lsp::DocumentSymbol *parameter_symbol = &p_parent->children[i];
+ if (!parameter_symbol->detail.is_empty() && parameter_symbol->name == symbol_identifier) {
+ return parameter_symbol;
+ }
+ }
+
+ return nullptr;
+}
+
+const lsp::DocumentSymbol *GDScriptWorkspace::get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier) {
+ const lsp::DocumentSymbol *class_symbol = &p_parser->get_symbols();
+
+ for (int i = 0; i < class_symbol->children.size(); ++i) {
+ if (class_symbol->children[i].kind == lsp::SymbolKind::Function || class_symbol->children[i].kind == lsp::SymbolKind::Class) {
+ const lsp::DocumentSymbol *function_symbol = &class_symbol->children[i];
+
+ for (int l = 0; l < function_symbol->children.size(); ++l) {
+ const lsp::DocumentSymbol *local = &function_symbol->children[l];
+ if (!local->detail.is_empty() && local->name == p_symbol_identifier) {
+ return local;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
void GDScriptWorkspace::reload_all_workspace_scripts() {
List<String> paths;
list_script_files("res://", paths);
@@ -231,18 +261,13 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
- Vector<DocData::PropertyDoc> properties;
- properties.append_array(class_data.properties);
- const int theme_prop_start_idx = properties.size();
- properties.append_array(class_data.theme_properties);
-
for (int i = 0; i < class_data.properties.size(); i++) {
const DocData::PropertyDoc &data = class_data.properties[i];
lsp::DocumentSymbol symbol;
symbol.name = data.name;
symbol.native_class = class_name;
symbol.kind = lsp::SymbolKind::Property;
- symbol.detail = String(i >= theme_prop_start_idx ? "<Theme> var" : "var") + " " + class_name + "." + data.name;
+ symbol.detail = "var " + class_name + "." + data.name;
if (data.enumeration.length()) {
symbol.detail += ": " + data.enumeration;
} else {
@@ -252,6 +277,17 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
+ for (int i = 0; i < class_data.theme_properties.size(); i++) {
+ const DocData::ThemeItemDoc &data = class_data.theme_properties[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = data.name;
+ symbol.native_class = class_name;
+ symbol.kind = lsp::SymbolKind::Property;
+ symbol.detail = "<Theme> var " + class_name + "." + data.name + ": " + data.type;
+ symbol.documentation = data.description;
+ class_symbol.children.push_back(symbol);
+ }
+
Vector<DocData::MethodDoc> methods_signals;
methods_signals.append_array(class_data.methods);
const int signal_start_idx = methods_signals.size();
@@ -350,6 +386,50 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
return err;
}
+Dictionary GDScriptWorkspace::rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name) {
+ Error err;
+ String path = get_file_path(p_doc_pos.textDocument.uri);
+
+ lsp::WorkspaceEdit edit;
+
+ List<String> paths;
+ list_script_files("res://", paths);
+
+ const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos);
+ if (reference_symbol) {
+ String identifier = reference_symbol->name;
+
+ for (List<String>::Element *PE = paths.front(); PE; PE = PE->next()) {
+ PackedStringArray content = FileAccess::get_file_as_string(PE->get(), &err).split("\n");
+ for (int i = 0; i < content.size(); ++i) {
+ String line = content[i];
+
+ int character = line.find(identifier);
+ while (character > -1) {
+ lsp::TextDocumentPositionParams params;
+
+ lsp::TextDocumentIdentifier text_doc;
+ text_doc.uri = get_file_uri(PE->get());
+
+ params.textDocument = text_doc;
+ params.position.line = i;
+ params.position.character = character;
+
+ const lsp::DocumentSymbol *other_symbol = resolve_symbol(params);
+
+ if (other_symbol == reference_symbol) {
+ edit.add_change(text_doc.uri, i, character, character + identifier.length(), new_name);
+ }
+
+ character = line.find(identifier, character + 1);
+ }
+ }
+ }
+ }
+
+ return edit.to_json();
+}
+
Error GDScriptWorkspace::parse_local_script(const String &p_path) {
Error err;
String content = FileAccess::get_file_as_string(p_path, &err);
@@ -440,8 +520,29 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
Node *owner_scene_node = _get_owner_scene_node(path);
+
+ Array stack;
+ Node *current = nullptr;
+ stack.push_back(owner_scene_node);
+
+ while (!stack.is_empty()) {
+ current = stack.pop_back();
+ Ref<GDScript> script = current->get_script();
+ if (script.is_valid() && script->get_path() == path) {
+ break;
+ }
+ for (int i = 0; i < current->get_child_count(); ++i) {
+ stack.push_back(current->get_child(i));
+ }
+ }
+
+ Ref<GDScript> script = current->get_script();
+ if (!script.is_valid() || script->get_path() != path) {
+ current = owner_scene_node;
+ }
+
String code = parser->get_text_for_completion(p_params.position);
- GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint);
+ GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint);
if (owner_scene_node) {
memdelete(owner_scene_node);
}
@@ -478,10 +579,16 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
String target_script_path = path;
if (!ret.script.is_null()) {
target_script_path = ret.script->get_path();
+ } else if (!ret.class_path.is_empty()) {
+ target_script_path = ret.class_path;
}
if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {
symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location));
+
+ if (symbol && symbol->kind == lsp::SymbolKind::Function && symbol->name != symbol_identifier) {
+ symbol = get_parameter_symbol(symbol, symbol_identifier);
+ }
}
} else {
@@ -493,6 +600,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
} else {
symbol = parser->get_member_symbol(symbol_identifier);
+
+ if (!symbol) {
+ symbol = get_local_symbol(parser, symbol_identifier);
+ }
}
}
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 8b166a873c..9496677449 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -52,6 +52,8 @@ protected:
const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const;
const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const;
+ const lsp::DocumentSymbol *get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier);
+ const lsp::DocumentSymbol *get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier);
void reload_all_workspace_scripts();
@@ -90,6 +92,7 @@ public:
Dictionary generate_script_api(const String &p_path);
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
void did_delete_files(const Dictionary &p_params);
+ Dictionary rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 0138f132ad..9ac6c6bd4e 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -255,6 +255,62 @@ struct TextEdit {
};
/**
+ * The edits to be applied.
+ */
+struct WorkspaceEdit {
+ /**
+ * Holds changes to existing resources.
+ */
+ Map<String, Vector<TextEdit>> changes;
+
+ _FORCE_INLINE_ Dictionary to_json() const {
+ Dictionary dict;
+
+ Dictionary out_changes;
+ for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) {
+ Array edits;
+ for (int i = 0; i < E->get().size(); ++i) {
+ Dictionary text_edit;
+ text_edit["range"] = E->get()[i].range.to_json();
+ text_edit["newText"] = E->get()[i].newText;
+ edits.push_back(text_edit);
+ }
+ out_changes[E->key()] = edits;
+ }
+ dict["changes"] = out_changes;
+
+ return dict;
+ }
+
+ _FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) {
+ if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
+ Vector<TextEdit> edit_list = E->value();
+ for (int i = 0; i < edit_list.size(); ++i) {
+ TextEdit edit = edit_list[i];
+ if (edit.range.start.character == start_character) {
+ return;
+ }
+ }
+ }
+
+ TextEdit new_edit;
+ new_edit.newText = new_text;
+ new_edit.range.start.line = line;
+ new_edit.range.start.character = start_character;
+ new_edit.range.end.line = line;
+ new_edit.range.end.character = end_character;
+
+ if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
+ E->value().push_back(new_edit);
+ } else {
+ Vector<TextEdit> edit_list;
+ edit_list.push_back(new_edit);
+ changes.insert(uri, edit_list);
+ }
+ }
+};
+
+/**
* Represents a reference to a command.
* Provides a title which will be used to represent a command in the UI.
* Commands are identified by a string identifier.
@@ -486,7 +542,7 @@ struct TextDocumentSyncOptions {
* If present save notifications are sent to the server. If omitted the notification should not be
* sent.
*/
- bool save = false;
+ SaveOptions save;
Dictionary to_json() {
Dictionary dict;
@@ -494,7 +550,7 @@ struct TextDocumentSyncOptions {
dict["willSave"] = willSave;
dict["openClose"] = openClose;
dict["change"] = change;
- dict["save"] = save;
+ dict["save"] = save.to_json();
return dict;
}
};
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 15a5807370..520262c0eb 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -875,6 +875,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// As scripts are going to be reloaded, must proceed without locking here
for (Ref<CSharpScript> &script : scripts) {
+ // If someone removes a script from a node, deletes the script, builds, adds a script to the
+ // same node, then builds again, the script might have no path and also no script_class. In
+ // that case, we can't (and don't need to) reload it.
+ if (script->get_path().is_empty() && !script->script_class) {
+ continue;
+ }
+
to_reload.push_back(script);
if (script->get_path().is_empty()) {
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index ed69c2b833..897f1b2822 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -11,6 +11,7 @@ namespace GodotTools.Build
{
public BuildOutputView BuildOutputView { get; private set; }
+ private MenuButton buildMenuBtn;
private Button errorsBtn;
private Button warningsBtn;
private Button viewLogBtn;
@@ -131,7 +132,7 @@ namespace GodotTools.Build
var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
AddChild(toolBarHBox);
- var buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")};
+ buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")};
toolBarHBox.AddChild(buildMenuBtn);
var buildMenu = buildMenuBtn.GetPopup();
@@ -177,5 +178,16 @@ namespace GodotTools.Build
BuildOutputView = new BuildOutputView();
AddChild(BuildOutputView);
}
+
+ public override void _Notification(int what)
+ {
+ base._Notification(what);
+
+ if (what == NotificationThemeChanged) {
+ buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons");
+ errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons");
+ warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons");
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 8271b43b48..968f853c2d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -207,7 +207,7 @@ namespace Godot
}
}
- public Quaternion RotationQuaternion()
+ public Quaternion GetRotationQuaternion()
{
Basis orthonormalizedBasis = Orthonormalized();
real_t det = orthonormalizedBasis.Determinant();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 213fc181c1..61a34bfc87 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Diagnostics.CodeAnalysis;
namespace Godot.Collections
{
@@ -25,6 +26,11 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Wrapper around Godot's Dictionary class, a dictionary of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine.
+ /// </summary>
public class Dictionary :
IDictionary,
IDisposable
@@ -32,11 +38,19 @@ namespace Godot.Collections
DictionarySafeHandle safeHandle;
bool disposed = false;
+ /// <summary>
+ /// Constructs a new empty <see cref="Dictionary"/>.
+ /// </summary>
public Dictionary()
{
safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
}
+ /// <summary>
+ /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary dictionary) : this()
{
if (dictionary == null)
@@ -64,6 +78,9 @@ namespace Godot.Collections
return safeHandle.DangerousGetHandle();
}
+ /// <summary>
+ /// Disposes of this <see cref="Dictionary"/>.
+ /// </summary>
public void Dispose()
{
if (disposed)
@@ -78,6 +95,11 @@ namespace Godot.Collections
disposed = true;
}
+ /// <summary>
+ /// Duplicates this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
@@ -85,6 +107,9 @@ namespace Godot.Collections
// IDictionary
+ /// <summary>
+ /// Gets the collection of keys in this <see cref="Dictionary"/>.
+ /// </summary>
public ICollection Keys
{
get
@@ -94,6 +119,9 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Gets the collection of elements in this <see cref="Dictionary"/>.
+ /// </summary>
public ICollection Values
{
get
@@ -103,47 +131,88 @@ namespace Godot.Collections
}
}
- public bool IsFixedSize => false;
+ private (Array keys, Array values, int count) GetKeyValuePairs()
+ {
+ int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
+ Array keys = new Array(new ArraySafeHandle(keysHandle));
+ Array values = new Array(new ArraySafeHandle(valuesHandle));
+ return (keys, values, count);
+ }
+
+ bool IDictionary.IsFixedSize => false;
- public bool IsReadOnly => false;
+ bool IDictionary.IsReadOnly => false;
+ /// <summary>
+ /// Returns the object at the given <paramref name="key"/>.
+ /// </summary>
+ /// <value>The object at the given <paramref name="key"/>.</value>
public object this[object key]
{
get => godot_icall_Dictionary_GetValue(GetPtr(), key);
set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
}
+ /// <summary>
+ /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// to this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <param name="key">The key at which to add the object.</param>
+ /// <param name="value">The object to add.</param>
public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
+ /// <summary>
+ /// Erases all items from this <see cref="Dictionary"/>.
+ /// </summary>
public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
+ /// <summary>
+ /// Checks if this <see cref="Dictionary"/> contains the given key.
+ /// </summary>
+ /// <param name="key">The key to look for.</param>
+ /// <returns>Whether or not this dictionary contains the given key.</returns>
public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+ /// <summary>
+ /// Removes an element from this <see cref="Dictionary"/> by key.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
// ICollection
- public object SyncRoot => this;
+ object ICollection.SyncRoot => this;
- public bool IsSynchronized => false;
+ bool ICollection.IsSynchronized => false;
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Dictionary"/>.
+ /// This is also known as the size or length of the dictionary.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count => godot_icall_Dictionary_Count(GetPtr());
+ /// <summary>
+ /// Copies the elements of this <see cref="Dictionary"/> to the given
+ /// untyped C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="index">The index to start at.</param>
public void CopyTo(System.Array array, int index)
{
- // TODO Can be done with single internal call
-
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
- Array keys = (Array)Keys;
- Array values = (Array)Values;
- int count = Count;
+ var (keys, values, count) = GetKeyValuePairs();
if (array.Length < (index + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
@@ -161,24 +230,39 @@ namespace Godot.Collections
private class DictionaryEnumerator : IDictionaryEnumerator
{
- Array keys;
- Array values;
- int count;
- int index = -1;
+ private readonly Dictionary dictionary;
+ private readonly int count;
+ private int index = -1;
+ private bool dirty = true;
+
+ private DictionaryEntry entry;
public DictionaryEnumerator(Dictionary dictionary)
{
- // TODO 3 internal calls, can reduce to 1
- keys = (Array)dictionary.Keys;
- values = (Array)dictionary.Values;
+ this.dictionary = dictionary;
count = dictionary.Count;
}
public object Current => Entry;
- public DictionaryEntry Entry =>
- // TODO 2 internal calls, can reduce to 1
- new DictionaryEntry(keys[index], values[index]);
+ public DictionaryEntry Entry
+ {
+ get
+ {
+ if (dirty)
+ {
+ UpdateEntry();
+ }
+ return entry;
+ }
+ }
+
+ private void UpdateEntry()
+ {
+ dirty = false;
+ godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), index, out object key, out object value);
+ entry = new DictionaryEntry(key, value);
+ }
public object Key => Entry.Key;
@@ -187,15 +271,21 @@ namespace Godot.Collections
public bool MoveNext()
{
index++;
+ dirty = true;
return index < count;
}
public void Reset()
{
index = -1;
+ dirty = true;
}
}
+ /// <summary>
+ /// Converts this <see cref="Dictionary"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this dictionary.</returns>
public override string ToString()
{
return godot_icall_Dictionary_ToString(GetPtr());
@@ -226,6 +316,12 @@ namespace Godot.Collections
internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -259,10 +355,18 @@ namespace Godot.Collections
internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr);
}
+ /// <summary>
+ /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine. Otherwise prefer .NET collections
+ /// such as <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
+ /// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
public class Dictionary<TKey, TValue> :
IDictionary<TKey, TValue>
{
- Dictionary objectDict;
+ private readonly Dictionary objectDict;
internal static int valTypeEncoding;
internal static IntPtr valTypeClass;
@@ -272,11 +376,19 @@ namespace Godot.Collections
Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
}
+ /// <summary>
+ /// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public Dictionary()
{
objectDict = new Dictionary();
}
+ /// <summary>
+ /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
objectDict = new Dictionary();
@@ -294,6 +406,11 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
objectDict = dictionary;
@@ -309,6 +426,10 @@ namespace Godot.Collections
objectDict = new Dictionary(handle);
}
+ /// <summary>
+ /// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
+ /// </summary>
+ /// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
return from.objectDict;
@@ -319,6 +440,11 @@ namespace Godot.Collections
return objectDict.GetPtr();
}
+ /// <summary>
+ /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep));
@@ -326,12 +452,19 @@ namespace Godot.Collections
// IDictionary<TKey, TValue>
+ /// <summary>
+ /// Returns the value at the given <paramref name="key"/>.
+ /// </summary>
+ /// <value>The value at the given <paramref name="key"/>.</value>
public TValue this[TKey key]
{
get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
set { objectDict[key] = value; }
}
+ /// <summary>
+ /// Gets the collection of keys in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public ICollection<TKey> Keys
{
get
@@ -341,6 +474,9 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Gets the collection of elements in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public ICollection<TValue> Values
{
get
@@ -350,56 +486,93 @@ namespace Godot.Collections
}
}
+ private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
+ {
+ Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value);
+ return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
+ }
+
+ /// <summary>
+ /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// to this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key at which to add the object.</param>
+ /// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
objectDict.Add(key, value);
}
+ /// <summary>
+ /// Checks if this <see cref="Dictionary{TKey, TValue}"/> contains the given key.
+ /// </summary>
+ /// <param name="key">The key to look for.</param>
+ /// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(TKey key)
{
return objectDict.Contains(key);
}
+ /// <summary>
+ /// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
public bool Remove(TKey key)
{
return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
}
- public bool TryGetValue(TKey key, out TValue value)
+ /// <summary>
+ /// Gets the object at the given <paramref name="key"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to get.</param>
+ /// <param name="value">The value at the given <paramref name="key"/>.</param>
+ /// <returns>If an object was found for the given <paramref name="key"/>.</returns>
+ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
- object retValue;
- bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default(TValue);
+ bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
+ value = found ? (TValue)retValue : default;
return found;
}
// ICollection<KeyValuePair<TKey, TValue>>
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// This is also known as the size or length of the dictionary.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count
{
get { return objectDict.Count; }
}
- public bool IsReadOnly
- {
- get { return objectDict.IsReadOnly; }
- }
+ bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
- public void Add(KeyValuePair<TKey, TValue> item)
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
objectDict.Add(item.Key, item.Value);
}
+ /// <summary>
+ /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public void Clear()
{
objectDict.Clear();
}
- public bool Contains(KeyValuePair<TKey, TValue> item)
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
}
+ /// <summary>
+ /// Copies the elements of this <see cref="Dictionary{TKey, TValue}"/> to the given
+ /// untyped C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
@@ -408,9 +581,6 @@ namespace Godot.Collections
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
- // TODO 3 internal calls, can reduce to 1
- Array<TKey> keys = (Array<TKey>)Keys;
- Array<TValue> values = (Array<TValue>)Values;
int count = Count;
if (array.Length < (arrayIndex + count))
@@ -418,13 +588,12 @@ namespace Godot.Collections
for (int i = 0; i < count; i++)
{
- // TODO 2 internal calls, can reduce to 1
- array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ array[arrayIndex] = GetKeyValuePair(i);
arrayIndex++;
}
}
- public bool Remove(KeyValuePair<TKey, TValue> item)
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
;
@@ -432,17 +601,15 @@ namespace Godot.Collections
// IEnumerable<KeyValuePair<TKey, TValue>>
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
- // TODO 3 internal calls, can reduce to 1
- Array<TKey> keys = (Array<TKey>)Keys;
- Array<TValue> values = (Array<TValue>)Values;
- int count = Count;
-
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < Count; i++)
{
- // TODO 2 internal calls, can reduce to 1
- yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ yield return GetKeyValuePair(i);
}
}
@@ -451,6 +618,10 @@ namespace Godot.Collections
return GetEnumerator();
}
+ /// <summary>
+ /// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this dictionary.</returns>
public override string ToString() => objectDict.ToString();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index f8f5e27397..71d0593916 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -28,6 +28,16 @@ namespace Godot
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
+ private static object[] GetPrintParams(object[] parameters)
+ {
+ if (parameters == null)
+ {
+ return new[] { "null" };
+ }
+
+ return Array.ConvertAll(parameters, x => x?.ToString() ?? "null");
+ }
+
public static int Hash(object var)
{
return godot_icall_GD_hash(var);
@@ -65,7 +75,7 @@ namespace Godot
public static void Print(params object[] what)
{
- godot_icall_GD_print(Array.ConvertAll(what ?? new object[] { "null" }, x => x != null ? x.ToString() : "null"));
+ godot_icall_GD_print(GetPrintParams(what));
}
public static void PrintStack()
@@ -75,22 +85,22 @@ namespace Godot
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(Array.ConvertAll(what ?? new object[] { "null" }, x => x != null ? x.ToString() : "null"));
+ godot_icall_GD_printerr(GetPrintParams(what));
}
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(Array.ConvertAll(what ?? new object[] { "null" }, x => x != null ? x.ToString() : "null"));
+ godot_icall_GD_printraw(GetPrintParams(what));
}
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(Array.ConvertAll(what ?? new object[] { "null" }, x => x != null ? x.ToString() : "null"));
+ godot_icall_GD_prints(GetPrintParams(what));
}
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(Array.ConvertAll(what ?? new object[] { "null" }, x => x != null ? x.ToString() : "null"));
+ godot_icall_GD_printt(GetPrintParams(what));
}
public static float Randf()
@@ -118,9 +128,9 @@ namespace Godot
return godot_icall_GD_randi_range(from, to);
}
- public static uint RandSeed(ulong seed, out ulong newSeed)
+ public static uint RandFromSeed(ref ulong seed)
{
- return godot_icall_GD_rand_seed(seed, out newSeed);
+ return godot_icall_GD_rand_seed(seed, out seed);
}
public static IEnumerable<int> Range(int end)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 1b717fb4ae..afc6a65a45 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -124,11 +124,11 @@ namespace Godot
/* not sure if very "efficient" but good enough? */
Vector3 sourceScale = basis.Scale;
- Quaternion sourceRotation = basis.RotationQuaternion();
+ Quaternion sourceRotation = basis.GetRotationQuaternion();
Vector3 sourceLocation = origin;
Vector3 destinationScale = transform.basis.Scale;
- Quaternion destinationRotation = transform.basis.RotationQuaternion();
+ Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
Vector3 destinationLocation = transform.origin;
var interpolated = new Transform3D();
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 191f863350..86976de244 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -230,6 +230,19 @@ int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size();
}
+int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
+ *keys = godot_icall_Dictionary_Keys(ptr);
+ *values = godot_icall_Dictionary_Values(ptr);
+ return godot_icall_Dictionary_Count(ptr);
+}
+
+void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
+ Array *keys = godot_icall_Dictionary_Keys(ptr);
+ Array *values = godot_icall_Dictionary_Values(ptr);
+ *key = GDMonoMarshal::variant_to_mono_object(keys->get(index));
+ *value = GDMonoMarshal::variant_to_mono_object(values->get(index));
+}
+
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
@@ -338,6 +351,8 @@ void godot_register_collections_icalls() {
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index 0c4a0d4ea0..c53af22ae1 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -51,10 +51,12 @@
<return type="int" enum="Error" />
<argument index="0" name="peer_id" type="int" />
<argument index="1" name="server_compatibility" type="bool" default="false" />
+ <argument index="2" name="channels_config" type="Array" default="[]" />
<description>
Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647).
If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted.
If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED].
+ You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="remove_peer">
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index 9b08a26aed..95c8c13449 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -34,7 +34,7 @@
#include "core/os/os.h"
void WebRTCMultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1));
ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
@@ -43,6 +43,14 @@ void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
}
+void WebRTCMultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int WebRTCMultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
transfer_mode = p_mode;
}
@@ -192,8 +200,34 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status()
return connection_status;
}
-Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat) {
- ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
+ ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+ channels_config.clear();
+ for (int i = 0; i < p_channels_config.size(); i++) {
+ ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
+ int mode = p_channels_config[i].operator int();
+ // Initialize data channel configurations.
+ Dictionary cfg;
+ cfg["id"] = CH_RESERVED_MAX + i + 1;
+ cfg["negotiated"] = true;
+ cfg["ordered"] = true;
+
+ switch (mode) {
+ case TRANSFER_MODE_UNRELIABLE_ORDERED:
+ cfg["maxPacketLifetime"] = 1;
+ break;
+ case TRANSFER_MODE_UNRELIABLE:
+ cfg["maxPacketLifetime"] = 1;
+ cfg["ordered"] = false;
+ break;
+ case TRANSFER_MODE_RELIABLE:
+ break;
+ default:
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
+ }
+ channels_config.push_back(cfg);
+ }
+
unique_id = p_self_id;
server_compat = p_server_compat;
@@ -260,17 +294,23 @@ Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_pe
cfg["id"] = 1;
peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_RELIABLE].is_null(), FAILED);
cfg["id"] = 2;
cfg["maxPacketLifetime"] = p_unreliable_lifetime;
peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_ORDERED].is_null(), FAILED);
cfg["id"] = 3;
cfg["ordered"] = false;
peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_UNRELIABLE].is_null(), FAILED);
+
+ for (const Dictionary &dict : channels_config) {
+ Ref<WebRTCDataChannel> ch = p_peer->create_data_channel(String::num_int64(dict["id"]), dict);
+ ERR_FAIL_COND_V(ch.is_null(), FAILED);
+ peer->channels.push_back(ch);
+ }
peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map
@@ -312,17 +352,21 @@ Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_
Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED);
- int ch = CH_RELIABLE;
- switch (transfer_mode) {
- case TRANSFER_MODE_RELIABLE:
- ch = CH_RELIABLE;
- break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED:
- ch = CH_ORDERED;
- break;
- case TRANSFER_MODE_UNRELIABLE:
- ch = CH_UNRELIABLE;
- break;
+ int ch = transfer_channel;
+ if (ch == 0) {
+ switch (transfer_mode) {
+ case TRANSFER_MODE_RELIABLE:
+ ch = CH_RELIABLE;
+ break;
+ case TRANSFER_MODE_UNRELIABLE_ORDERED:
+ ch = CH_ORDERED;
+ break;
+ case TRANSFER_MODE_UNRELIABLE:
+ ch = CH_UNRELIABLE;
+ break;
+ }
+ } else {
+ ch += CH_RESERVED_MAX - 1;
}
Map<int, Ref<ConnectedPeer>>::Element *E = nullptr;
@@ -331,8 +375,8 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
E = peer_map.find(target_peer);
ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + ".");
- ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG);
- ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG);
+ ERR_FAIL_COND_V_MSG(E->value()->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
+ ERR_FAIL_COND_V(E->value()->channels[ch].is_null(), ERR_BUG);
return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
} else {
@@ -344,7 +388,8 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
continue;
}
- ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid());
+ ERR_CONTINUE_MSG(F->value()->channels.size() <= ch, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
+ ERR_CONTINUE(F->value()->channels[ch].is_null());
F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
}
}
@@ -370,23 +415,13 @@ int WebRTCMultiplayerPeer::get_max_packet_size() const {
void WebRTCMultiplayerPeer::close() {
peer_map.clear();
+ channels_config.clear();
unique_id = 0;
next_packet_peer = 0;
target_peer = 0;
connection_status = CONNECTION_DISCONNECTED;
}
-WebRTCMultiplayerPeer::WebRTCMultiplayerPeer() {
- unique_id = 0;
- next_packet_peer = 0;
- target_peer = 0;
- client_count = 0;
- transfer_mode = TRANSFER_MODE_RELIABLE;
- refuse_connections = false;
- connection_status = CONNECTION_DISCONNECTED;
- server_compat = false;
-}
-
WebRTCMultiplayerPeer::~WebRTCMultiplayerPeer() {
close();
}
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index 1d9387b6dc..ef4fe1678c 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -62,25 +62,27 @@ private:
}
};
- uint32_t unique_id;
- int target_peer;
- int client_count;
- bool refuse_connections;
- ConnectionStatus connection_status;
- TransferMode transfer_mode;
- int next_packet_peer;
- bool server_compat;
+ uint32_t unique_id = 0;
+ int target_peer = 0;
+ int client_count = 0;
+ bool refuse_connections = false;
+ ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
+ int transfer_channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
+ int next_packet_peer = 0;
+ bool server_compat = false;
Map<int, Ref<ConnectedPeer>> peer_map;
+ List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer();
public:
- WebRTCMultiplayerPeer();
+ WebRTCMultiplayerPeer() {}
~WebRTCMultiplayerPeer();
- Error initialize(int p_self_id, bool p_server_compat = false);
+ Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id);
@@ -95,6 +97,8 @@ public:
int get_max_packet_size() const override;
// MultiplayerPeer
+ void set_transfer_channel(int p_channel) override;
+ int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override;
void set_target_peer(int p_peer_id) override;
diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index 52d9a602a1..163cc7706b 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -105,6 +105,14 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer
//
// MultiplayerPeer
//
+void WebSocketMultiplayerPeer::set_transfer_channel(int p_channel) {
+ // Websocket does not have channels.
+}
+
+int WebSocketMultiplayerPeer::get_transfer_channel() const {
+ return 0;
+}
+
void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
// Websocket uses TCP, reliable
}
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 4e80f876d6..0fee196f41 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -78,6 +78,8 @@ protected:
public:
/* MultiplayerPeer */
+ void set_transfer_channel(int p_channel) override;
+ int get_transfer_channel() const override;
void set_transfer_mode(TransferMode p_mode) override;
TransferMode get_transfer_mode() const override;
void set_target_peer(int p_target_peer) override;