feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -35,6 +35,7 @@
#include "editor/gui/editor_run_bar.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
void EditorNetworkProfiler::_bind_methods() {
ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
@ -297,30 +298,37 @@ bool EditorNetworkProfiler::is_profiling() {
}
EditorNetworkProfiler::EditorNetworkProfiler() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override("separation", 8 * EDSCALE);
add_child(hb);
FlowContainer *container = memnew(FlowContainer);
container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
add_child(container);
activate = memnew(Button);
activate->set_toggle_mode(true);
activate->set_text(TTR("Start"));
activate->set_disabled(true);
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_activate_pressed));
hb->add_child(activate);
container->add_child(activate);
clear_button = memnew(Button);
clear_button->set_text(TTR("Clear"));
clear_button->set_disabled(true);
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_clear_pressed));
hb->add_child(clear_button);
container->add_child(clear_button);
CheckBox *autostart_checkbox = memnew(CheckBox);
autostart_checkbox->set_text(TTR("Autostart"));
autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false));
autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorNetworkProfiler::_autostart_toggled));
hb->add_child(autostart_checkbox);
container->add_child(autostart_checkbox);
hb->add_spacer();
Control *c = memnew(Control);
c->set_h_size_flags(SIZE_EXPAND_FILL);
container->add_child(c);
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
container->add_child(hb);
Label *lb = memnew(Label);
// TRANSLATORS: This is the label for the network profiler's incoming bandwidth.
@ -331,6 +339,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
incoming_bandwidth_text->set_editable(false);
incoming_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
incoming_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
incoming_bandwidth_text->set_accessibility_name(TTRC("Incoming Bandwidth"));
hb->add_child(incoming_bandwidth_text);
Control *down_up_spacer = memnew(Control);
@ -346,6 +355,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
outgoing_bandwidth_text->set_editable(false);
outgoing_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
outgoing_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
outgoing_bandwidth_text->set_accessibility_name(TTRC("Outgoing Bandwidth"));
hb->add_child(outgoing_bandwidth_text);
// Set initial texts in the incoming/outgoing bandwidth labels
@ -359,7 +369,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
// RPC
counters_display = memnew(Tree);
counters_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
counters_display->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
counters_display->set_v_size_flags(SIZE_EXPAND_FILL);
counters_display->set_h_size_flags(SIZE_EXPAND_FILL);
counters_display->set_hide_folding(true);
@ -382,7 +392,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
// Replication
replication_display = memnew(Tree);
replication_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
replication_display->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
replication_display->set_v_size_flags(SIZE_EXPAND_FILL);
replication_display->set_h_size_flags(SIZE_EXPAND_FILL);
replication_display->set_hide_folding(true);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_NETWORK_PROFILER_H
#define EDITOR_NETWORK_PROFILER_H
#pragma once
#include "../multiplayer_debugger.h"
@ -120,5 +119,3 @@ public:
EditorNetworkProfiler();
};
#endif // EDITOR_NETWORK_PROFILER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MULTIPLAYER_EDITOR_PLUGIN_H
#define MULTIPLAYER_EDITOR_PLUGIN_H
#pragma once
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/editor_plugin.h"
@ -51,8 +50,6 @@ public:
virtual bool has_capture(const String &p_capture) const override;
virtual bool capture(const String &p_message, const Array &p_data, int p_index) override;
virtual void setup_session(int p_session_id) override;
MultiplayerEditorDebugger() {}
};
class ReplicationEditor;
@ -80,5 +77,3 @@ public:
MultiplayerEditorPlugin();
};
#endif // MULTIPLAYER_EDITOR_PLUGIN_H

View file

@ -234,6 +234,7 @@ ReplicationEditor::ReplicationEditor() {
np_line_edit = memnew(LineEdit);
np_line_edit->set_placeholder(":property");
np_line_edit->set_accessibility_name(TTRC("Path"));
np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
np_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &ReplicationEditor::_np_text_submitted));
hb->add_child(np_line_edit);
@ -251,6 +252,7 @@ ReplicationEditor::ReplicationEditor() {
pin->set_theme_type_variation(SceneStringName(FlatButton));
pin->set_toggle_mode(true);
pin->set_tooltip_text(TTR("Pin replication editor"));
pin->set_accessibility_name(TTRC("Pin"));
hb->add_child(pin);
tree = memnew(Tree);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef REPLICATION_EDITOR_H
#define REPLICATION_EDITOR_H
#pragma once
#include "../scene_replication_config.h"
@ -102,7 +101,4 @@ public:
Button *get_pin() { return pin; }
ReplicationEditor();
~ReplicationEditor() {}
};
#endif // REPLICATION_EDITOR_H

View file

@ -150,9 +150,7 @@ void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_
int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
Array arr;
arr.push_back(incoming_bandwidth);
arr.push_back(outgoing_bandwidth);
Array arr = { incoming_bandwidth, outgoing_bandwidth };
EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);
}
}
@ -160,8 +158,7 @@ void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_
// RPCProfiler
Array MultiplayerDebugger::RPCFrame::serialize() {
Array arr;
arr.push_back(infos.size() * 6);
Array arr = { infos.size() * 6 };
for (int i = 0; i < infos.size(); ++i) {
arr.push_back(uint64_t(infos[i].node));
arr.push_back(infos[i].node_path);
@ -198,7 +195,7 @@ void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
}
rpc_node_data.insert(p_node, RPCNodeInfo());
rpc_node_data[p_node].node = p_node;
rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
rpc_node_data[p_node].node_path = ObjectDB::get_instance<Node>(p_node)->get_path();
}
void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
@ -270,8 +267,7 @@ bool MultiplayerDebugger::SyncInfo::read_from_array(const Array &p_arr, int p_of
}
Array MultiplayerDebugger::ReplicationFrame::serialize() {
Array arr;
arr.push_back(infos.size() * 7);
Array arr = { infos.size() * 7 };
for (const KeyValue<ObjectID, SyncInfo> &E : infos) {
E.value.write_to_array(arr);
}
@ -304,7 +300,7 @@ void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {
const String what = p_data[0];
const ObjectID id = p_data[1];
const uint64_t size = p_data[2];
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(id));
MultiplayerSynchronizer *sync = ObjectDB::get_instance<MultiplayerSynchronizer>(id);
ERR_FAIL_NULL(sync);
if (!sync_data.has(id)) {
sync_data[id] = SyncInfo(sync);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MULTIPLAYER_DEBUGGER_H
#define MULTIPLAYER_DEBUGGER_H
#pragma once
#include "core/debugger/engine_profiler.h"
@ -128,5 +127,3 @@ public:
static void initialize();
static void deinitialize();
};
#endif // MULTIPLAYER_DEBUGGER_H

View file

@ -185,7 +185,7 @@ void MultiplayerSpawner::_update_spawn_node() {
}
#endif
if (spawn_node.is_valid()) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node));
Node *node = ObjectDB::get_instance<Node>(spawn_node);
if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
}
@ -211,7 +211,7 @@ void MultiplayerSpawner::_notification(int p_what) {
_update_spawn_node();
for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
Node *node = ObjectDB::get_instance<Node>(E.key);
ERR_CONTINUE(!node);
node->disconnect(SceneStringName(tree_exiting), callable_mp(this, &MultiplayerSpawner::_node_exit));
get_multiplayer()->object_configuration_remove(node, this);
@ -265,7 +265,7 @@ void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
}
void MultiplayerSpawner::_node_exit(ObjectID p_id) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
Node *node = ObjectDB::get_instance<Node>(p_id);
ERR_FAIL_NULL(node);
if (tracked_nodes.has(p_id)) {
tracked_nodes.erase(p_id);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MULTIPLAYER_SPAWNER_H
#define MULTIPLAYER_SPAWNER_H
#pragma once
#include "core/templates/local_vector.h"
#include "scene/main/node.h"
@ -91,7 +90,7 @@ public:
PackedStringArray get_configuration_warnings() const override;
Node *get_spawn_node() const {
return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr;
return spawn_node.is_valid() ? ObjectDB::get_instance<Node>(spawn_node) : nullptr;
}
void add_spawnable_scene(const String &p_path);
@ -115,5 +114,3 @@ public:
MultiplayerSpawner() {}
};
#endif // MULTIPLAYER_SPAWNER_H

View file

@ -100,7 +100,7 @@ void MultiplayerSynchronizer::_update_process() {
}
Node *MultiplayerSynchronizer::get_root_node() {
return root_node_cache.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(root_node_cache)) : nullptr;
return root_node_cache.is_valid() ? ObjectDB::get_instance<Node>(root_node_cache) : nullptr;
}
void MultiplayerSynchronizer::reset() {
@ -376,7 +376,7 @@ Error MultiplayerSynchronizer::_watch_changes(uint64_t p_usec) {
if (props.size() != watchers.size()) {
watchers.resize(props.size());
}
if (props.size() == 0) {
if (props.is_empty()) {
return OK;
}
Node *node = get_root_node();

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MULTIPLAYER_SYNCHRONIZER_H
#define MULTIPLAYER_SYNCHRONIZER_H
#pragma once
#include "scene_replication_config.h"
@ -125,5 +124,3 @@ public:
};
VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
#endif // MULTIPLAYER_SYNCHRONIZER_H

View file

@ -48,8 +48,10 @@ void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(MultiplayerSynchronizer);
GDREGISTER_CLASS(OfflineMultiplayerPeer);
GDREGISTER_CLASS(SceneMultiplayer);
MultiplayerAPI::set_default_interface("SceneMultiplayer");
MultiplayerDebugger::initialize();
if (GD_IS_CLASS_ENABLED(MultiplayerAPI)) {
MultiplayerAPI::set_default_interface("SceneMultiplayer");
MultiplayerDebugger::initialize();
}
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
@ -59,5 +61,7 @@ void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
}
void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) {
MultiplayerDebugger::deinitialize();
if (GD_IS_CLASS_ENABLED(MultiplayerAPI)) {
MultiplayerDebugger::deinitialize();
}
}

View file

@ -28,12 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MULTIPLAYER_REGISTER_TYPES_H
#define MULTIPLAYER_REGISTER_TYPES_H
#pragma once
#include "modules/register_module_types.h"
void initialize_multiplayer_module(ModuleInitializationLevel p_level);
void uninitialize_multiplayer_module(ModuleInitializationLevel p_level);
#endif // MULTIPLAYER_REGISTER_TYPES_H

View file

@ -100,8 +100,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
ERR_FAIL_NULL(root_node);
int ofs = 1;
String methods_md5;
methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
String methods_md5 = String::utf8((const char *)(p_packet + ofs), 32);
ofs += 33;
int id = decode_uint32(&p_packet[ofs]);
@ -109,8 +108,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
ERR_FAIL_COND_MSG(peers_info[p_from].recv_nodes.has(id), vformat("Duplicate remote cache ID %d for peer %d", id, p_from));
String paths;
paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
String paths = String::utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
const NodePath path = paths;
@ -154,7 +152,7 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack
}
if (valid_rpc_checksum == false) {
const Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid));
const Node *node = ObjectDB::get_instance<Node>(*oid);
ERR_FAIL_NULL(node); // Bug.
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + node->get_path());
}
@ -280,7 +278,7 @@ Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id)
RecvNode *recv_node = pinfo->recv_nodes.getptr(p_cache_id);
ERR_FAIL_NULL_V_MSG(recv_node, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from));
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(recv_node->oid));
Node *node = ObjectDB::get_instance<Node>(recv_node->oid);
if (!node) {
// Fallback to path lookup.
Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_CACHE_INTERFACE_H
#define SCENE_CACHE_INTERFACE_H
#pragma once
#include "core/object/ref_counted.h"
@ -89,5 +88,3 @@ public:
SceneCacheInterface(SceneMultiplayer *p_multiplayer) { multiplayer = p_multiplayer; }
};
#endif // SCENE_CACHE_INTERFACE_H

View file

@ -37,15 +37,14 @@
#include "core/os/os.h"
#endif
#include <stdint.h>
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void SceneMultiplayer::_profile_bandwidth(const String &p_what, int p_value) {
if (EngineDebugger::is_profiling("multiplayer:bandwidth")) {
Array values;
values.push_back(p_what);
values.push_back(OS::get_singleton()->get_ticks_msec());
values.push_back(p_value);
Array values = {
p_what,
OS::get_singleton()->get_ticks_msec(),
p_value
};
EngineDebugger::profiler_add_frame_data("multiplayer:bandwidth", values);
}
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_MULTIPLAYER_H
#define SCENE_MULTIPLAYER_H
#pragma once
#include "scene_cache_interface.h"
#include "scene_replication_interface.h"
@ -204,5 +203,3 @@ public:
SceneMultiplayer();
~SceneMultiplayer();
};
#endif // SCENE_MULTIPLAYER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_REPLICATION_CONFIG_H
#define SCENE_REPLICATION_CONFIG_H
#pragma once
#include "core/io/resource.h"
#include "core/variant/typed_array.h"
@ -108,5 +107,3 @@ public:
};
VARIANT_ENUM_CAST(SceneReplicationConfig::ReplicationMode);
#endif // SCENE_REPLICATION_CONFIG_H

View file

@ -43,10 +43,7 @@
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void SceneReplicationInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) {
if (EngineDebugger::is_profiling("multiplayer:replication")) {
Array values;
values.push_back(p_what);
values.push_back(p_id);
values.push_back(p_size);
Array values = { p_what, p_id, p_size };
EngineDebugger::profiler_add_frame_data("multiplayer:replication", values);
}
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_REPLICATION_INTERFACE_H
#define SCENE_REPLICATION_INTERFACE_H
#pragma once
#include "multiplayer_spawner.h"
#include "multiplayer_synchronizer.h"
@ -114,7 +113,7 @@ private:
template <typename T>
static T *get_id_as(const ObjectID &p_id) {
return p_id.is_valid() ? Object::cast_to<T>(ObjectDB::get_instance(p_id)) : nullptr;
return p_id.is_valid() ? ObjectDB::get_instance<T>(p_id) : nullptr;
}
#ifdef DEBUG_ENABLED
@ -151,5 +150,3 @@ public:
multiplayer_cache = p_cache;
}
};
#endif // SCENE_REPLICATION_INTERFACE_H

View file

@ -54,10 +54,7 @@
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) {
if (EngineDebugger::is_profiling("multiplayer:rpc")) {
Array values;
values.push_back(p_what);
values.push_back(p_id);
values.push_back(p_size);
Array values = { p_what, p_id, p_size };
EngineDebugger::profiler_add_frame_data("multiplayer:rpc", values);
}
}
@ -138,8 +135,7 @@ Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet,
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
String paths;
paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
String paths = String::utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
NodePath np = paths;
@ -301,7 +297,7 @@ void SceneRPCInterface::_send_rpc(Node *p_node, int p_to, uint16_t p_rpc_id, con
ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255).");
if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) {
if (p_to != 0 && !multiplayer->get_connected_peers().has(Math::abs(p_to))) {
ERR_FAIL_COND_MSG(p_to == multiplayer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(multiplayer->get_unique_id()) + ".");
ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCENE_RPC_INTERFACE_H
#define SCENE_RPC_INTERFACE_H
#pragma once
#include "core/object/ref_counted.h"
#include "scene/main/multiplayer_api.h"
@ -110,5 +109,3 @@ public:
multiplayer_replicator = p_replicator;
}
};
#endif // SCENE_RPC_INTERFACE_H

View file

@ -0,0 +1,445 @@
/**************************************************************************/
/* test_multiplayer_spawner.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "tests/test_macros.h"
#include "tests/test_utils.h"
#include "../multiplayer_spawner.h"
namespace TestMultiplayerSpawner {
static inline Array build_array() {
return Array();
}
template <typename... Targs>
static inline Array build_array(Variant item, Targs... Fargs) {
Array a = build_array(Fargs...);
a.push_front(item);
return a;
}
class Wasp : public Node {
GDCLASS(Wasp, Node);
int _size = 0;
public:
int get_size() const {
return _size;
}
void set_size(int p_size) {
_size = p_size;
}
Wasp() {
set_name("Wasp");
set_scene_file_path("wasp.tscn");
}
};
class SpawnWasps : public Object {
GDCLASS(SpawnWasps, Object);
protected:
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("wasp", "size"), &SpawnWasps::create_wasps);
{
MethodInfo mi;
mi.name = "wasp_error";
mi.arguments.push_back(PropertyInfo(Variant::INT, "size"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "wasp_error", &SpawnWasps::create_wasps_error, mi, varray(), false);
}
ClassDB::bind_method(D_METHOD("echo", "size"), &SpawnWasps::echo_size);
}
public:
Wasp *create_wasps(int p_size) {
Wasp *wasp = memnew(Wasp);
wasp->set_size(p_size);
return wasp;
}
Wasp *create_wasps_error(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
return nullptr;
}
int echo_size(int p_size) {
return p_size;
}
};
TEST_CASE("[Multiplayer][MultiplayerSpawner] Defaults") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
CHECK_EQ(multiplayer_spawner->get_configuration_warnings().size(), 1);
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
CHECK_EQ(multiplayer_spawner->get_spawn_path(), NodePath());
CHECK_EQ(multiplayer_spawner->get_spawn_limit(), 0);
CHECK_EQ(multiplayer_spawner->get_spawn_function(), Callable());
memdelete(multiplayer_spawner);
}
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn Path warning") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
// If there is no spawn path, there should be a warning.
PackedStringArray warning_messages = multiplayer_spawner->get_configuration_warnings();
REQUIRE_EQ(warning_messages.size(), 1);
CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
// If there is a spawn path, but it doesn't exist a node on it, there should be a warning.
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
warning_messages = multiplayer_spawner->get_configuration_warnings();
REQUIRE_EQ(warning_messages.size(), 1);
CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
// If there is a spawn path and a node on it, shouldn't be a warning.
Node *foo = memnew(Node);
foo->set_name("Foo");
SceneTree::get_singleton()->get_root()->add_child(foo);
warning_messages = multiplayer_spawner->get_configuration_warnings();
CHECK_EQ(warning_messages.size(), 0);
memdelete(foo);
memdelete(multiplayer_spawner);
}
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn node") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
Node *foo = memnew(Node);
foo->set_name("Foo");
SceneTree::get_singleton()->get_root()->add_child(foo);
SUBCASE("nullptr if spawn path doesn't exists") {
multiplayer_spawner->set_spawn_path(NodePath("/root/NotExists"));
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
}
SUBCASE("Get it after setting spawn path with no signal connections") {
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
CHECK_FALSE(foo->has_connections("child_entered_tree"));
}
SUBCASE("Get it after setting spawn path with signal connections") {
multiplayer_spawner->add_spawnable_scene("scene.tscn");
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
CHECK(foo->has_connections("child_entered_tree"));
}
SUBCASE("Set a new one should disconnect signals from the old one") {
multiplayer_spawner->add_spawnable_scene("scene.tscn");
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
CHECK(foo->has_connections("child_entered_tree"));
Node *bar = memnew(Node);
bar->set_name("Bar");
SceneTree::get_singleton()->get_root()->add_child(bar);
multiplayer_spawner->set_spawn_path(NodePath("/root/Bar"));
CHECK_EQ(multiplayer_spawner->get_spawn_node(), bar);
CHECK(bar->has_connections("child_entered_tree"));
CHECK_FALSE(foo->has_connections("child_entered_tree"));
memdelete(bar);
}
memdelete(foo);
memdelete(multiplayer_spawner);
}
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawnable scene") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
SUBCASE("Add one") {
multiplayer_spawner->add_spawnable_scene("scene.tscn");
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 1);
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
}
SUBCASE("Add one and if there is a valid spawn path add a connection to it") {
Node *foo = memnew(Node);
foo->set_name("Foo");
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
CHECK_FALSE(foo->has_connections("child_entered_tree"));
// Adding now foo to the tree to avoid set_spawn_path() making the connection.
SceneTree::get_singleton()->get_root()->add_child(foo);
multiplayer_spawner->notification(Node::NOTIFICATION_POST_ENTER_TREE);
CHECK_FALSE(foo->has_connections("child_entered_tree"));
multiplayer_spawner->add_spawnable_scene("scene.tscn");
CHECK(foo->has_connections("child_entered_tree"));
memdelete(foo);
}
SUBCASE("Add multiple") {
multiplayer_spawner->add_spawnable_scene("scene.tscn");
multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(1), "other_scene.tscn");
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(2), "yet_another_scene.tscn");
}
SUBCASE("Clear") {
Node *foo = memnew(Node);
foo->set_name("Foo");
SceneTree::get_singleton()->get_root()->add_child(foo);
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
multiplayer_spawner->add_spawnable_scene("scene.tscn");
multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
CHECK(foo->has_connections("child_entered_tree"));
multiplayer_spawner->clear_spawnable_scenes();
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
CHECK_FALSE(foo->has_connections("child_entered_tree"));
}
memdelete(multiplayer_spawner);
}
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Instantiate custom") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
Node *nest = memnew(Node);
nest->set_name("Nest");
SceneTree::get_singleton()->get_root()->add_child(nest);
multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
SpawnWasps *spawn_wasps = memnew(SpawnWasps);
SUBCASE("Instantiates a node properly") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp");
CHECK_EQ(wasp->get_size(), 42);
memdelete(wasp);
}
SUBCASE("Instantiates multiple nodes properly if there is no spawn limit") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
for (int i = 0; i < 10; i++) {
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(i)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp");
CHECK_EQ(wasp->get_size(), i);
nest->add_child(wasp, true);
}
}
SUBCASE("Fails if spawn limit is reached") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
// This one works.
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp");
CHECK_EQ(wasp->get_size(), 42);
// Adding to the spawner node to get it tracked.
nest->add_child(wasp);
// This one fails because spawn limit is reached.
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(255)), nullptr);
ERR_PRINT_ON;
memdelete(wasp);
}
SUBCASE("Fails if spawn function is not set") {
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
ERR_PRINT_ON;
}
SUBCASE("Fails when spawn function fails") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp_error"));
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
ERR_PRINT_ON;
}
SUBCASE("Fails when spawn function not returns a node") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
ERR_PRINT_ON;
}
memdelete(spawn_wasps);
memdelete(nest);
memdelete(multiplayer_spawner);
}
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn") {
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
SUBCASE("Fails because is not inside tree") {
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
ERR_PRINT_ON;
}
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
Node *nest = memnew(Node);
nest->set_name("Nest");
SceneTree::get_singleton()->get_root()->add_child(nest);
multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
SpawnWasps *spawn_wasps = memnew(SpawnWasps);
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
SUBCASE("Spawns a node, track it and add it to spawn node") {
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp");
CHECK_EQ(wasp->get_size(), 42);
CHECK_EQ(wasp->get_parent(), nest);
CHECK_EQ(nest->get_child_count(), 1);
CHECK_EQ(nest->get_child(0), wasp);
}
SUBCASE("Spawns multiple nodes properly if there is no spawn limit") {
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
for (int i = 0; i < 10; i++) {
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(i)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp" + String((i == 0) ? "" : itos(i + 1)));
CHECK_EQ(wasp->get_size(), i);
CHECK_EQ(wasp->get_parent(), nest);
CHECK_EQ(nest->get_child_count(), i + 1);
CHECK_EQ(nest->get_child(i), wasp);
}
}
SUBCASE("Fails if spawn limit is reached") {
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
// This one works.
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
CHECK_NE(wasp, nullptr);
CHECK_EQ(wasp->get_name(), "Wasp");
CHECK_EQ(wasp->get_size(), 42);
CHECK_EQ(wasp->get_parent(), nest);
CHECK_EQ(nest->get_child_count(), 1);
CHECK_EQ(nest->get_child(0), wasp);
// This one fails because spawn limit is reached.
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->spawn(Variant(255)), nullptr);
ERR_PRINT_ON;
memdelete(wasp);
}
SUBCASE("Fails if spawn function is not set") {
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
ERR_PRINT_ON;
}
SUBCASE("Fails if spawn node cannot be found") {
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
multiplayer_spawner->set_spawn_path(NodePath(""));
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
ERR_PRINT_ON;
}
SUBCASE("Fails when instantiate_custom not returns a node") {
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
multiplayer_spawner->set_spawn_limit(1);
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
ERR_PRINT_OFF;
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
ERR_PRINT_ON;
}
memdelete(spawn_wasps);
memdelete(nest);
memdelete(multiplayer_spawner);
}
} // namespace TestMultiplayerSpawner

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEST_SCENE_MULTIPLAYER_H
#define TEST_SCENE_MULTIPLAYER_H
#pragma once
#include "tests/test_macros.h"
#include "tests/test_utils.h"
@ -280,5 +279,3 @@ TEST_CASE("[Multiplayer][SceneMultiplayer][SceneTree] Complete Authentication")
}
} // namespace TestSceneMultiplayer
#endif // TEST_SCENE_MULTIPLAYER_H