Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/multiplayer/tests/test_multiplayer_spawner.h
10278 views
1
/**************************************************************************/
2
/* test_multiplayer_spawner.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "tests/test_macros.h"
34
#include "tests/test_utils.h"
35
36
#include "../multiplayer_spawner.h"
37
38
namespace TestMultiplayerSpawner {
39
class Wasp : public Node {
40
GDCLASS(Wasp, Node);
41
42
int _size = 0;
43
44
public:
45
int get_size() const {
46
return _size;
47
}
48
void set_size(int p_size) {
49
_size = p_size;
50
}
51
52
Wasp() {
53
set_name("Wasp");
54
set_scene_file_path("wasp.tscn");
55
}
56
};
57
58
class SpawnWasps : public Object {
59
GDCLASS(SpawnWasps, Object);
60
61
protected:
62
static void _bind_methods() {
63
ClassDB::bind_method(D_METHOD("wasp", "size"), &SpawnWasps::create_wasps);
64
{
65
MethodInfo mi;
66
mi.name = "wasp_error";
67
mi.arguments.push_back(PropertyInfo(Variant::INT, "size"));
68
69
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "wasp_error", &SpawnWasps::create_wasps_error, mi, varray(), false);
70
}
71
ClassDB::bind_method(D_METHOD("echo", "size"), &SpawnWasps::echo_size);
72
}
73
74
public:
75
Wasp *create_wasps(int p_size) {
76
Wasp *wasp = memnew(Wasp);
77
wasp->set_size(p_size);
78
return wasp;
79
}
80
81
Wasp *create_wasps_error(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
82
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
83
return nullptr;
84
}
85
86
int echo_size(int p_size) {
87
return p_size;
88
}
89
};
90
91
TEST_CASE("[Multiplayer][MultiplayerSpawner] Defaults") {
92
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
93
94
CHECK_EQ(multiplayer_spawner->get_configuration_warnings().size(), 1);
95
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
96
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
97
CHECK_EQ(multiplayer_spawner->get_spawn_path(), NodePath());
98
CHECK_EQ(multiplayer_spawner->get_spawn_limit(), 0);
99
CHECK_EQ(multiplayer_spawner->get_spawn_function(), Callable());
100
101
memdelete(multiplayer_spawner);
102
}
103
104
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn Path warning") {
105
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
106
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
107
108
// If there is no spawn path, there should be a warning.
109
PackedStringArray warning_messages = multiplayer_spawner->get_configuration_warnings();
110
REQUIRE_EQ(warning_messages.size(), 1);
111
CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
112
113
// If there is a spawn path, but it doesn't exist a node on it, there should be a warning.
114
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
115
warning_messages = multiplayer_spawner->get_configuration_warnings();
116
REQUIRE_EQ(warning_messages.size(), 1);
117
CHECK_MESSAGE(warning_messages[0].contains("\"Spawn Path\""), "Invalid configuration warning");
118
119
// If there is a spawn path and a node on it, shouldn't be a warning.
120
Node *foo = memnew(Node);
121
foo->set_name("Foo");
122
SceneTree::get_singleton()->get_root()->add_child(foo);
123
warning_messages = multiplayer_spawner->get_configuration_warnings();
124
CHECK_EQ(warning_messages.size(), 0);
125
126
memdelete(foo);
127
memdelete(multiplayer_spawner);
128
}
129
130
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn node") {
131
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
132
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
133
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
134
135
Node *foo = memnew(Node);
136
foo->set_name("Foo");
137
SceneTree::get_singleton()->get_root()->add_child(foo);
138
139
SUBCASE("nullptr if spawn path doesn't exists") {
140
multiplayer_spawner->set_spawn_path(NodePath("/root/NotExists"));
141
142
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
143
}
144
145
SUBCASE("Get it after setting spawn path with no signal connections") {
146
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
147
148
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
149
CHECK_FALSE(foo->has_connections("child_entered_tree"));
150
}
151
152
SUBCASE("Get it after setting spawn path with signal connections") {
153
multiplayer_spawner->add_spawnable_scene("scene.tscn");
154
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
155
156
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
157
CHECK(foo->has_connections("child_entered_tree"));
158
}
159
160
SUBCASE("Set a new one should disconnect signals from the old one") {
161
multiplayer_spawner->add_spawnable_scene("scene.tscn");
162
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
163
164
CHECK_EQ(multiplayer_spawner->get_spawn_node(), foo);
165
CHECK(foo->has_connections("child_entered_tree"));
166
167
Node *bar = memnew(Node);
168
bar->set_name("Bar");
169
SceneTree::get_singleton()->get_root()->add_child(bar);
170
multiplayer_spawner->set_spawn_path(NodePath("/root/Bar"));
171
172
CHECK_EQ(multiplayer_spawner->get_spawn_node(), bar);
173
CHECK(bar->has_connections("child_entered_tree"));
174
CHECK_FALSE(foo->has_connections("child_entered_tree"));
175
176
memdelete(bar);
177
}
178
179
memdelete(foo);
180
memdelete(multiplayer_spawner);
181
}
182
183
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawnable scene") {
184
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
185
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
186
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
187
188
SUBCASE("Add one") {
189
multiplayer_spawner->add_spawnable_scene("scene.tscn");
190
191
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 1);
192
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
193
}
194
195
SUBCASE("Add one and if there is a valid spawn path add a connection to it") {
196
Node *foo = memnew(Node);
197
foo->set_name("Foo");
198
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
199
CHECK_FALSE(foo->has_connections("child_entered_tree"));
200
201
// Adding now foo to the tree to avoid set_spawn_path() making the connection.
202
SceneTree::get_singleton()->get_root()->add_child(foo);
203
multiplayer_spawner->notification(Node::NOTIFICATION_POST_ENTER_TREE);
204
CHECK_FALSE(foo->has_connections("child_entered_tree"));
205
multiplayer_spawner->add_spawnable_scene("scene.tscn");
206
CHECK(foo->has_connections("child_entered_tree"));
207
208
memdelete(foo);
209
}
210
211
SUBCASE("Add multiple") {
212
multiplayer_spawner->add_spawnable_scene("scene.tscn");
213
multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
214
multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
215
216
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
217
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(0), "scene.tscn");
218
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(1), "other_scene.tscn");
219
CHECK_EQ(multiplayer_spawner->get_spawnable_scene(2), "yet_another_scene.tscn");
220
}
221
222
SUBCASE("Clear") {
223
Node *foo = memnew(Node);
224
foo->set_name("Foo");
225
SceneTree::get_singleton()->get_root()->add_child(foo);
226
multiplayer_spawner->set_spawn_path(NodePath("/root/Foo"));
227
228
multiplayer_spawner->add_spawnable_scene("scene.tscn");
229
multiplayer_spawner->add_spawnable_scene("other_scene.tscn");
230
multiplayer_spawner->add_spawnable_scene("yet_another_scene.tscn");
231
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 3);
232
CHECK(foo->has_connections("child_entered_tree"));
233
234
multiplayer_spawner->clear_spawnable_scenes();
235
236
CHECK_EQ(multiplayer_spawner->get_spawnable_scene_count(), 0);
237
CHECK_FALSE(foo->has_connections("child_entered_tree"));
238
}
239
240
memdelete(multiplayer_spawner);
241
}
242
243
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Instantiate custom") {
244
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
245
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
246
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
247
248
Node *nest = memnew(Node);
249
nest->set_name("Nest");
250
SceneTree::get_singleton()->get_root()->add_child(nest);
251
multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
252
253
SpawnWasps *spawn_wasps = memnew(SpawnWasps);
254
255
SUBCASE("Instantiates a node properly") {
256
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
257
258
multiplayer_spawner->set_spawn_limit(1);
259
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
260
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
261
CHECK_NE(wasp, nullptr);
262
CHECK_EQ(wasp->get_name(), "Wasp");
263
CHECK_EQ(wasp->get_size(), 42);
264
265
memdelete(wasp);
266
}
267
268
SUBCASE("Instantiates multiple nodes properly if there is no spawn limit") {
269
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
270
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
271
272
for (int i = 0; i < 10; i++) {
273
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(i)));
274
CHECK_NE(wasp, nullptr);
275
CHECK_EQ(wasp->get_name(), "Wasp");
276
CHECK_EQ(wasp->get_size(), i);
277
nest->add_child(wasp, true);
278
}
279
}
280
281
SUBCASE("Fails if spawn limit is reached") {
282
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
283
284
multiplayer_spawner->set_spawn_limit(1);
285
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
286
287
// This one works.
288
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->instantiate_custom(Variant(42)));
289
CHECK_NE(wasp, nullptr);
290
CHECK_EQ(wasp->get_name(), "Wasp");
291
CHECK_EQ(wasp->get_size(), 42);
292
// Adding to the spawner node to get it tracked.
293
nest->add_child(wasp);
294
295
// This one fails because spawn limit is reached.
296
ERR_PRINT_OFF;
297
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(255)), nullptr);
298
ERR_PRINT_ON;
299
300
memdelete(wasp);
301
}
302
303
SUBCASE("Fails if spawn function is not set") {
304
ERR_PRINT_OFF;
305
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
306
ERR_PRINT_ON;
307
}
308
309
SUBCASE("Fails when spawn function fails") {
310
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
311
312
multiplayer_spawner->set_spawn_limit(1);
313
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp_error"));
314
315
ERR_PRINT_OFF;
316
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
317
ERR_PRINT_ON;
318
}
319
320
SUBCASE("Fails when spawn function not returns a node") {
321
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
322
323
multiplayer_spawner->set_spawn_limit(1);
324
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
325
326
ERR_PRINT_OFF;
327
CHECK_EQ(multiplayer_spawner->instantiate_custom(Variant(42)), nullptr);
328
ERR_PRINT_ON;
329
}
330
331
memdelete(spawn_wasps);
332
memdelete(nest);
333
memdelete(multiplayer_spawner);
334
}
335
336
TEST_CASE("[Multiplayer][MultiplayerSpawner][SceneTree] Spawn") {
337
MultiplayerSpawner *multiplayer_spawner = memnew(MultiplayerSpawner);
338
339
SUBCASE("Fails because is not inside tree") {
340
ERR_PRINT_OFF;
341
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
342
ERR_PRINT_ON;
343
}
344
345
SceneTree::get_singleton()->get_root()->add_child(multiplayer_spawner);
346
CHECK_EQ(multiplayer_spawner->get_spawn_node(), nullptr);
347
348
Node *nest = memnew(Node);
349
nest->set_name("Nest");
350
SceneTree::get_singleton()->get_root()->add_child(nest);
351
multiplayer_spawner->set_spawn_path(NodePath("/root/Nest"));
352
353
SpawnWasps *spawn_wasps = memnew(SpawnWasps);
354
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
355
356
SUBCASE("Spawns a node, track it and add it to spawn node") {
357
multiplayer_spawner->set_spawn_limit(1);
358
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
359
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
360
CHECK_NE(wasp, nullptr);
361
CHECK_EQ(wasp->get_name(), "Wasp");
362
CHECK_EQ(wasp->get_size(), 42);
363
CHECK_EQ(wasp->get_parent(), nest);
364
CHECK_EQ(nest->get_child_count(), 1);
365
CHECK_EQ(nest->get_child(0), wasp);
366
}
367
368
SUBCASE("Spawns multiple nodes properly if there is no spawn limit") {
369
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
370
371
for (int i = 0; i < 10; i++) {
372
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(i)));
373
CHECK_NE(wasp, nullptr);
374
CHECK_EQ(wasp->get_name(), "Wasp" + String((i == 0) ? "" : itos(i + 1)));
375
CHECK_EQ(wasp->get_size(), i);
376
CHECK_EQ(wasp->get_parent(), nest);
377
CHECK_EQ(nest->get_child_count(), i + 1);
378
CHECK_EQ(nest->get_child(i), wasp);
379
}
380
}
381
382
SUBCASE("Fails if spawn limit is reached") {
383
multiplayer_spawner->set_spawn_limit(1);
384
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
385
386
// This one works.
387
Wasp *wasp = Object::cast_to<Wasp>(multiplayer_spawner->spawn(Variant(42)));
388
CHECK_NE(wasp, nullptr);
389
CHECK_EQ(wasp->get_name(), "Wasp");
390
CHECK_EQ(wasp->get_size(), 42);
391
CHECK_EQ(wasp->get_parent(), nest);
392
CHECK_EQ(nest->get_child_count(), 1);
393
CHECK_EQ(nest->get_child(0), wasp);
394
395
// This one fails because spawn limit is reached.
396
ERR_PRINT_OFF;
397
CHECK_EQ(multiplayer_spawner->spawn(Variant(255)), nullptr);
398
ERR_PRINT_ON;
399
400
memdelete(wasp);
401
}
402
403
SUBCASE("Fails if spawn function is not set") {
404
ERR_PRINT_OFF;
405
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
406
ERR_PRINT_ON;
407
}
408
409
SUBCASE("Fails if spawn node cannot be found") {
410
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "wasp"));
411
multiplayer_spawner->set_spawn_path(NodePath(""));
412
413
ERR_PRINT_OFF;
414
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
415
ERR_PRINT_ON;
416
}
417
418
SUBCASE("Fails when instantiate_custom not returns a node") {
419
multiplayer_spawner->add_spawnable_scene("wasp.tscn");
420
421
multiplayer_spawner->set_spawn_limit(1);
422
multiplayer_spawner->set_spawn_function(Callable(spawn_wasps, "echo"));
423
424
ERR_PRINT_OFF;
425
CHECK_EQ(multiplayer_spawner->spawn(Variant(42)), nullptr);
426
ERR_PRINT_ON;
427
}
428
429
memdelete(spawn_wasps);
430
memdelete(nest);
431
memdelete(multiplayer_spawner);
432
}
433
434
} // namespace TestMultiplayerSpawner
435
436