summaryrefslogtreecommitdiff
path: root/addons/gdUnit4/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'addons/gdUnit4/src/network')
-rw-r--r--addons/gdUnit4/src/network/GdUnitServer.gd41
-rw-r--r--addons/gdUnit4/src/network/GdUnitServer.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/GdUnitServer.tscn10
-rw-r--r--addons/gdUnit4/src/network/GdUnitServerConstants.gd6
-rw-r--r--addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/GdUnitTask.gd25
-rw-r--r--addons/gdUnit4/src/network/GdUnitTask.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpClient.gd126
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpNode.gd73
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpServer.gd140
-rw-r--r--addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/rpc/RPC.gd37
-rw-r--r--addons/gdUnit4/src/network/rpc/RPC.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCClientConnect.gd13
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd13
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd14
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid1
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCMessage.gd18
-rw-r--r--addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid1
23 files changed, 527 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd b/addons/gdUnit4/src/network/GdUnitServer.gd
new file mode 100644
index 0000000..4ce2f48
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitServer.gd
@@ -0,0 +1,41 @@
+@tool
+extends Node
+
+var _client_id: int
+
+@onready var _server: GdUnitTcpServer = $TcpServer
+
+
+@warning_ignore("return_value_discarded")
+func _ready() -> void:
+ var result := _server.start()
+ if result.is_error():
+ push_error(result.error_message())
+ return
+ var server_port :int = result.value()
+ Engine.set_meta("gdunit_server_port", server_port)
+ _server.client_connected.connect(_on_client_connected)
+ _server.client_disconnected.connect(_on_client_disconnected)
+ _server.rpc_data.connect(_receive_rpc_data)
+
+
+func _on_client_connected(client_id: int) -> void:
+ _client_id = client_id
+ GdUnitSignals.instance().gdunit_client_connected.emit(client_id)
+
+
+func _on_client_disconnected(client_id: int) -> void:
+ GdUnitSignals.instance().gdunit_client_disconnected.emit(client_id)
+
+
+func _receive_rpc_data(p_rpc: RPC) -> void:
+ if p_rpc is RPCMessage:
+ var rpc_message: RPCMessage = p_rpc
+ GdUnitSignals.instance().gdunit_message.emit(rpc_message.message())
+ return
+ if p_rpc is RPCGdUnitEvent:
+ var rpc_event: RPCGdUnitEvent = p_rpc
+ var event := rpc_event.event()
+ GdUnitSignals.instance().gdunit_event.emit(event)
+ if event.type() == GdUnitEvent.SESSION_CLOSE and _server != null:
+ _server.disconnect_client(_client_id)
diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd.uid b/addons/gdUnit4/src/network/GdUnitServer.gd.uid
new file mode 100644
index 0000000..b44864e
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitServer.gd.uid
@@ -0,0 +1 @@
+uid://cu75icxofcpxv
diff --git a/addons/gdUnit4/src/network/GdUnitServer.tscn b/addons/gdUnit4/src/network/GdUnitServer.tscn
new file mode 100644
index 0000000..4dbe8c4
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitServer.tscn
@@ -0,0 +1,10 @@
+[gd_scene load_steps=3 format=3 uid="uid://cn5mp3tmi2gb1"]
+
+[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitServer.gd" id="1"]
+[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitTcpServer.gd" id="2"]
+
+[node name="Control" type="Node"]
+script = ExtResource("1")
+
+[node name="TcpServer" type="Node" parent="."]
+script = ExtResource("2")
diff --git a/addons/gdUnit4/src/network/GdUnitServerConstants.gd b/addons/gdUnit4/src/network/GdUnitServerConstants.gd
new file mode 100644
index 0000000..d31eee7
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitServerConstants.gd
@@ -0,0 +1,6 @@
+class_name GdUnitServerConstants
+extends RefCounted
+
+const DEFAULT_SERVER_START_RETRY_TIMES :int = 5
+const GD_TEST_SERVER_PORT :int = 31002
+const JSON_RESPONSE_DELIMITER :String = "<<JRD>>"
diff --git a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid
new file mode 100644
index 0000000..b847741
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid
@@ -0,0 +1 @@
+uid://cg7ssosnfdrcn
diff --git a/addons/gdUnit4/src/network/GdUnitTask.gd b/addons/gdUnit4/src/network/GdUnitTask.gd
new file mode 100644
index 0000000..e0188a0
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTask.gd
@@ -0,0 +1,25 @@
+class_name GdUnitTask
+extends RefCounted
+
+const TASK_NAME = "task_name"
+const TASK_ARGS = "task_args"
+
+var _task_name :String
+var _fref :Callable
+
+
+func _init(task_name :String,instance :Object,func_name :String) -> void:
+ _task_name = task_name
+ if not instance.has_method(func_name):
+ push_error("Can't create GdUnitTask, Invalid func name '%s' for instance '%s'" % [instance, func_name])
+ _fref = Callable(instance, func_name)
+
+
+func name() -> String:
+ return _task_name
+
+
+func execute(args :Array) -> GdUnitResult:
+ if args.is_empty():
+ return _fref.call()
+ return _fref.callv(args)
diff --git a/addons/gdUnit4/src/network/GdUnitTask.gd.uid b/addons/gdUnit4/src/network/GdUnitTask.gd.uid
new file mode 100644
index 0000000..99a1242
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTask.gd.uid
@@ -0,0 +1 @@
+uid://dapl4oygj1lal
diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd b/addons/gdUnit4/src/network/GdUnitTcpClient.gd
new file mode 100644
index 0000000..d6c8a42
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd
@@ -0,0 +1,126 @@
+@tool
+class_name GdUnitTcpClient
+extends GdUnitTcpNode
+
+signal connection_succeeded(message: String)
+signal connection_failed(message: String)
+
+
+var _client_name: String
+var _debug := false
+var _host: String
+var _port: int
+var _client_id: int
+var _connected: bool
+var _stream: StreamPeerTCP
+
+
+func _init(client_name := "GdUnit4 TCP Client", debug := false) -> void:
+ _client_name = client_name
+ _debug = debug
+
+
+func _ready() -> void:
+ _connected = false
+ _stream = StreamPeerTCP.new()
+ #_stream.set_big_endian(true)
+
+
+func stop() -> void:
+ console("Disconnecting from server")
+ if _stream != null:
+ rpc_send(_stream, RPCClientDisconnect.new().with_id(_client_id))
+ if _stream != null:
+ _stream.disconnect_from_host()
+ _connected = false
+
+
+func start(host: String, port: int) -> GdUnitResult:
+ _host = host
+ _port = port
+ if _connected:
+ return GdUnitResult.warn("Client already connected ... %s:%d" % [_host, _port])
+
+ # Connect client to server
+ if _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:
+ var err := _stream.connect_to_host(host, port)
+ #prints("connect_to_host", host, port, err)
+ if err != OK:
+ return GdUnitResult.error("GdUnit4: Can't establish client, error code: %s" % err)
+ return GdUnitResult.success("GdUnit4: Client connected checked port %d" % port)
+
+
+func _process(_delta: float) -> void:
+ match _stream.get_status():
+ StreamPeerTCP.STATUS_NONE:
+ return
+
+ StreamPeerTCP.STATUS_CONNECTING:
+ set_process(false)
+ # wait until client is connected to server
+ for retry in 10:
+ @warning_ignore("return_value_discarded")
+ _stream.poll()
+ console("Waiting to connect ..")
+ if _stream.get_status() == StreamPeerTCP.STATUS_CONNECTING:
+ await get_tree().create_timer(0.500).timeout
+ if _stream.get_status() == StreamPeerTCP.STATUS_CONNECTED:
+ set_process(true)
+ return
+ set_process(true)
+ _stream.disconnect_from_host()
+ console("Connection failed")
+ connection_failed.emit("Connect to TCP Server %s:%d faild!" % [_host, _port])
+
+ StreamPeerTCP.STATUS_CONNECTED:
+ if not _connected:
+ var rpc_data :RPC = null
+ set_process(false)
+ while rpc_data == null:
+ await get_tree().create_timer(0.500).timeout
+ rpc_data = rpc_receive()
+ set_process(true)
+ _client_id = (rpc_data as RPCClientConnect).client_id()
+ console("Connected to Server: %d" % _client_id)
+ connection_succeeded.emit("Connect to TCP Server %s:%d success." % [_host, _port])
+ _connected = true
+ process_rpc()
+
+ StreamPeerTCP.STATUS_ERROR:
+ console("Connection failed")
+ _stream.disconnect_from_host()
+ connection_failed.emit("Connect to TCP Server %s:%d faild!" % [_host, _port])
+ return
+
+
+func is_client_connected() -> bool:
+ return _connected
+
+
+func process_rpc() -> void:
+ if _stream.get_available_bytes() > 0:
+ var rpc_data := rpc_receive()
+ if rpc_data is RPCClientDisconnect:
+ console("RPCClientDisconnect")
+ GdUnitSignals.instance().gdunit_test_session_terminate.emit.call_deferred()
+
+
+func send(data: RPC) -> void:
+ rpc_send(_stream, data)
+
+
+func rpc_receive() -> RPC:
+ return receive_packages(_stream).front()
+
+
+func console(value: Variant) -> void:
+ if _debug:
+ print(_client_name, ": ", value)
+
+
+func _on_connection_failed(message: String) -> void:
+ console("Connection faild by: " + message)
+
+
+func _on_connection_succeeded(message: String) -> void:
+ console("Connected: " + message)
diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid
new file mode 100644
index 0000000..7166e61
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid
@@ -0,0 +1 @@
+uid://bitvc4op4ulwh
diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd b/addons/gdUnit4/src/network/GdUnitTcpNode.gd
new file mode 100644
index 0000000..156c764
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd
@@ -0,0 +1,73 @@
+class_name GdUnitTcpNode
+extends Node
+
+
+func rpc_send(stream: StreamPeerTCP, data: RPC) -> void:
+ var package_buffer := StreamPeerBuffer.new()
+ var buffer := data.serialize().to_utf16_buffer()
+ package_buffer.put_u32(0xDEADBEEF)
+ package_buffer.put_u32(buffer.size())
+ var status_code := package_buffer.put_data(buffer)
+ if status_code != OK:
+ push_error("'rpc_send:' Can't put_data(), error: %s" % error_string(status_code))
+ return
+ stream.put_data(package_buffer.data_array)
+
+
+func receive_packages(stream: StreamPeerTCP, rpc_cb: Callable = noop) -> Array[RPC]:
+ var received_packages: Array[RPC] = []
+ var package_buffer := StreamPeerBuffer.new()
+ if stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:
+ return received_packages
+
+ while stream.get_status() == StreamPeerTCP.STATUS_CONNECTED and stream.get_available_bytes() > 0:
+ var buffer := stream.get_data(8)
+ var status_code: int = buffer[0]
+ if status_code != OK:
+ push_error("'receive_packages:' Can't get_data(%d) for available_bytes, error: %s"
+ % [stream.get_available_bytes(), error_string(status_code)])
+ return received_packages
+
+ var data_package: PackedByteArray
+ package_buffer.data_array = buffer[1]
+ package_buffer.seek(0)
+
+ if package_buffer.get_u32() == 0xDEADBEEF:
+ var size := package_buffer.get_u32()
+ if stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:
+ return received_packages
+ if stream.get_available_bytes() < size:
+ prints("size check:",
+ package_buffer.get_size(), ":",
+ package_buffer.get_position(),
+ "to read:",
+ size,
+ "available size:",
+ stream.get_available_bytes())
+ push_error("'receive_packages:' Can't receive data get_data(%d) for package, error: %s" % [size, error_string(status_code)])
+ return received_packages
+
+ buffer = stream.get_data(size)
+ package_buffer.data_array = buffer[1]
+
+ var rpc_data := package_buffer.get_data(size)
+ status_code = rpc_data[0]
+ if status_code != OK:
+ push_error("'receive_packages:' Can't get_data(%d) for package, error: %s" % [size, error_string(status_code)])
+ continue
+ data_package = rpc_data[1]
+ else:
+ data_package = buffer[1]
+
+ var json := data_package.get_string_from_utf16()
+ if json.is_empty():
+ push_warning("json is empty, can't process data")
+ continue
+ var data := RPC.deserialize(json)
+ received_packages.append(data)
+ rpc_cb.call(data)
+ return received_packages
+
+
+static func noop(_rpc_data: RPC) -> void:
+ pass
diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid
new file mode 100644
index 0000000..4483fe2
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid
@@ -0,0 +1 @@
+uid://c8bwg30yoifsu
diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd b/addons/gdUnit4/src/network/GdUnitTcpServer.gd
new file mode 100644
index 0000000..997d6e3
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd
@@ -0,0 +1,140 @@
+@tool
+class_name GdUnitTcpServer
+extends Node
+
+signal client_connected(client_id: int)
+signal client_disconnected(client_id: int)
+@warning_ignore("unused_signal")
+signal rpc_data(rpc_data: RPC)
+
+var _server: TCPServer
+var _server_name: String
+
+class TcpConnection extends GdUnitTcpNode:
+ var _id: int
+ var _stream: StreamPeerTCP
+
+
+ func _init(tcp_server: TCPServer) -> void:
+ _stream = tcp_server.take_connection()
+ #_stream.set_big_endian(true)
+ _id = _stream.get_instance_id()
+ rpc_send(_stream, RPCClientConnect.new().with_id(_id))
+
+
+ func _ready() -> void:
+ server().client_connected.emit(_id)
+
+
+ func close() -> void:
+ if _stream != null and _stream.get_status() == StreamPeerTCP.STATUS_CONNECTED:
+ _stream.disconnect_from_host()
+ queue_free()
+
+
+ func id() -> int:
+ return _id
+
+
+ func server() -> GdUnitTcpServer:
+ return get_parent()
+
+
+ func _process(_delta: float) -> void:
+ if _stream == null or _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:
+ return
+ receive_packages(_stream, func(rpc_data: RPC) -> void:
+ server().rpc_data.emit(rpc_data)
+ # is client disconnecting we close the server after a timeout of 1 second
+ if rpc_data is RPCClientDisconnect:
+ close()
+ )
+
+
+ func disconnect_from_server() -> void:
+ if _stream == null or _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED:
+ return
+ rpc_send(_stream, RPCClientDisconnect.new().with_id(_id))
+
+
+ func console(_value: Variant) -> void:
+ #print_debug("TCP Server: ", value)
+ pass
+
+
+func _init(server_name := "GdUnit4 TCP Server") -> void:
+ _server_name = server_name
+ GdUnitSignals.instance().gdunit_test_session_terminate.connect(func() -> void:
+ for connection in get_children():
+ if connection is TcpConnection:
+ (connection as TcpConnection).disconnect_from_server()
+ )
+
+
+func _ready() -> void:
+ _server = TCPServer.new()
+ client_connected.connect(_on_client_connected)
+ client_disconnected.connect(_on_client_disconnected)
+
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_PREDELETE:
+ stop()
+
+
+func start(server_port := GdUnitServerConstants.GD_TEST_SERVER_PORT) -> GdUnitResult:
+ var err := OK
+ for retry in GdUnitServerConstants.DEFAULT_SERVER_START_RETRY_TIMES:
+ err = _server.listen(server_port, "127.0.0.1")
+ if err != OK:
+ prints("GdUnit4: Can't establish server checked port: %d, Error: %s" % [server_port, error_string(err)])
+ server_port += 1
+ prints("GdUnit4: Retry (%d) ..." % retry)
+ else:
+ break
+ if err != OK:
+ if err == ERR_ALREADY_IN_USE:
+ return GdUnitResult.error("GdUnit4: Can't establish server, the server is already in use. Error: %s, " % error_string(err))
+ return GdUnitResult.error("GdUnit4: Can't establish server. Error: %s." % error_string(err))
+ console("Successfully started checked port: %d" % server_port)
+ return GdUnitResult.success(server_port)
+
+
+func stop() -> void:
+ if _server:
+ _server.stop()
+ for connection in get_children():
+ if connection is TcpConnection:
+ @warning_ignore("unsafe_method_access")
+ connection.close()
+ remove_child(connection)
+ _server = null
+
+
+func disconnect_client(client_id: int) -> void:
+ client_disconnected.emit(client_id)
+
+
+func _process(_delta: float) -> void:
+ if _server != null and not _server.is_listening():
+ return
+ # check if connection is ready to be used
+ if _server != null and _server.is_connection_available():
+ add_child(TcpConnection.new(_server))
+
+
+func _on_client_connected(client_id: int) -> void:
+ console("Client connected %d" % client_id)
+
+
+func _on_client_disconnected(client_id: int) -> void:
+ for connection in get_children():
+ @warning_ignore("unsafe_method_access")
+ if connection is TcpConnection and connection.id() == client_id:
+ @warning_ignore("unsafe_method_access")
+ connection.close()
+ remove_child(connection)
+
+
+func console(value: Variant) -> void:
+ print(_server_name, ": ", value)
diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid
new file mode 100644
index 0000000..76f0fc0
--- /dev/null
+++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid
@@ -0,0 +1 @@
+uid://csm58erfvsxld
diff --git a/addons/gdUnit4/src/network/rpc/RPC.gd b/addons/gdUnit4/src/network/rpc/RPC.gd
new file mode 100644
index 0000000..6569cb1
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPC.gd
@@ -0,0 +1,37 @@
+class_name RPC
+extends RefCounted
+
+
+var _data: Dictionary = {}
+
+
+func _init(obj: Object = null) -> void:
+ if obj != null:
+ if obj.has_method("serialize"):
+ _data = obj.call("serialize")
+ else:
+ _data = inst_to_dict(obj)
+
+
+func get_data() -> Object:
+ return dict_to_inst(_data)
+
+
+func serialize() -> String:
+ return JSON.stringify(inst_to_dict(self))
+
+
+# using untyped version see comments below
+static func deserialize(json_value: String) -> Object:
+ var json := JSON.new()
+ var err := json.parse(json_value)
+ if err != OK:
+ push_error("Can't deserialize JSON, error at line %d:\n error: %s \n json: '%s'"
+ % [json.get_error_line(), json.get_error_message(), json_value])
+ return null
+ var result: Dictionary = json.get_data()
+ if not typeof(result) == TYPE_DICTIONARY:
+ push_error("Can't deserialize JSON. Expecting dictionary, error at line %d:\n error: %s \n json: '%s'"
+ % [result.error_line, result.error_string, json_value])
+ return null
+ return dict_to_inst(result)
diff --git a/addons/gdUnit4/src/network/rpc/RPC.gd.uid b/addons/gdUnit4/src/network/rpc/RPC.gd.uid
new file mode 100644
index 0000000..40c58dc
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPC.gd.uid
@@ -0,0 +1 @@
+uid://na86vw3ufqjo
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd
new file mode 100644
index 0000000..6b494cf
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd
@@ -0,0 +1,13 @@
+class_name RPCClientConnect
+extends RPC
+
+var _client_id: int
+
+
+func with_id(id: int) -> RPCClientConnect:
+ _client_id = id
+ return self
+
+
+func client_id() -> int:
+ return _client_id
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid
new file mode 100644
index 0000000..016194f
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid
@@ -0,0 +1 @@
+uid://e0qdfuxqc6y0
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd
new file mode 100644
index 0000000..7445b9d
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd
@@ -0,0 +1,13 @@
+class_name RPCClientDisconnect
+extends RPC
+
+var _client_id: int
+
+
+func with_id(id: int) -> RPCClientDisconnect:
+ _client_id = id
+ return self
+
+
+func client_id() -> int:
+ return _client_id
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid
new file mode 100644
index 0000000..3114387
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid
@@ -0,0 +1 @@
+uid://brg4jjgbo7okh
diff --git a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd
new file mode 100644
index 0000000..dbf55c6
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd
@@ -0,0 +1,14 @@
+class_name RPCGdUnitEvent
+extends RPC
+
+
+static func of(p_event: GdUnitEvent) -> RPCGdUnitEvent:
+ return RPCGdUnitEvent.new(p_event)
+
+
+func event() -> GdUnitEvent:
+ return GdUnitEvent.new().deserialize(_data)
+
+
+func _to_string() -> String:
+ return "RPCGdUnitEvent: " + str(_data)
diff --git a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid
new file mode 100644
index 0000000..2543b41
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid
@@ -0,0 +1 @@
+uid://co8lhd0kirsur
diff --git a/addons/gdUnit4/src/network/rpc/RPCMessage.gd b/addons/gdUnit4/src/network/rpc/RPCMessage.gd
new file mode 100644
index 0000000..1db0470
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCMessage.gd
@@ -0,0 +1,18 @@
+class_name RPCMessage
+extends RPC
+
+var _message: String
+
+
+static func of(msg :String) -> RPCMessage:
+ var rpc := RPCMessage.new()
+ rpc._message = msg
+ return rpc
+
+
+func message() -> String:
+ return _message
+
+
+func _to_string() -> String:
+ return "RPCMessage: " + _message
diff --git a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid
new file mode 100644
index 0000000..d470686
--- /dev/null
+++ b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid
@@ -0,0 +1 @@
+uid://muf7ucwnbkuk