2020-02-27 03:30:20 +01:00
/**************************************************************************/
/* remote_debugger_peer.cpp */
/**************************************************************************/
/* 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. */
/**************************************************************************/
# include "remote_debugger_peer.h"
2020-11-07 19:33:38 -03:00
# include "core/config/project_settings.h"
2020-02-27 03:30:20 +01:00
# include "core/io/marshalls.h"
# include "core/os/os.h"
bool RemoteDebuggerPeerTCP : : is_peer_connected ( ) {
return connected ;
}
bool RemoteDebuggerPeerTCP : : has_message ( ) {
return in_queue . size ( ) > 0 ;
}
Array RemoteDebuggerPeerTCP : : get_message ( ) {
MutexLock lock ( mutex ) ;
2025-06-18 06:06:54 +10:00
List < Array > : : Element * E = in_queue . front ( ) ;
ERR_FAIL_NULL_V_MSG ( E , Array ( ) , " No remote debugger messages in queue. " ) ;
Array out = E - > get ( ) ;
2020-02-27 03:30:20 +01:00
in_queue . pop_front ( ) ;
return out ;
}
Error RemoteDebuggerPeerTCP : : put_message ( const Array & p_arr ) {
MutexLock lock ( mutex ) ;
2020-05-14 16:41:43 +02:00
if ( out_queue . size ( ) > = max_queued_messages ) {
2020-02-27 03:30:20 +01:00
return ERR_OUT_OF_MEMORY ;
2020-05-14 16:41:43 +02:00
}
2020-02-27 03:30:20 +01:00
out_queue . push_back ( p_arr ) ;
return OK ;
}
int RemoteDebuggerPeerTCP : : get_max_message_size ( ) const {
return 8 < < 20 ; // 8 MiB
}
void RemoteDebuggerPeerTCP : : close ( ) {
2021-01-19 13:29:41 +01:00
running = false ;
2023-04-22 15:34:16 +02:00
if ( thread . is_started ( ) ) {
thread . wait_to_finish ( ) ;
}
2020-02-27 03:30:20 +01:00
tcp_client - > disconnect_from_host ( ) ;
2022-02-02 00:04:13 +05:45
out_buf . clear ( ) ;
in_buf . clear ( ) ;
2020-02-27 03:30:20 +01:00
}
2025-06-25 07:01:29 +10:00
RemoteDebuggerPeerTCP : : RemoteDebuggerPeerTCP ( ) {
2020-02-27 03:30:20 +01:00
// This means remote debugger takes 16 MiB just because it exists...
in_buf . resize ( ( 8 < < 20 ) + 4 ) ; // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
out_buf . resize ( 8 < < 20 ) ; // 8 MiB should be way more than enough
2025-06-25 07:01:29 +10:00
}
RemoteDebuggerPeerTCP : : RemoteDebuggerPeerTCP ( Ref < StreamPeerSocket > p_stream ) :
RemoteDebuggerPeerTCP ( ) {
DEV_ASSERT ( p_stream . is_valid ( ) ) ;
tcp_client = p_stream ;
connected = true ;
running = true ;
thread . start ( _thread_func , this ) ;
2020-02-27 03:30:20 +01:00
}
RemoteDebuggerPeerTCP : : ~ RemoteDebuggerPeerTCP ( ) {
close ( ) ;
}
void RemoteDebuggerPeerTCP : : _write_out ( ) {
2022-03-27 15:30:17 +02:00
while ( tcp_client - > get_status ( ) = = StreamPeerTCP : : STATUS_CONNECTED & & tcp_client - > wait ( NetSocket : : POLL_TYPE_OUT ) = = OK ) {
2020-02-27 03:30:20 +01:00
uint8_t * buf = out_buf . ptrw ( ) ;
if ( out_left < = 0 ) {
mutex . lock ( ) ;
2025-06-18 06:06:54 +10:00
List < Array > : : Element * E = out_queue . front ( ) ;
if ( ! E ) {
mutex . unlock ( ) ;
break ;
}
Variant var = E - > get ( ) ;
2020-02-27 03:30:20 +01:00
out_queue . pop_front ( ) ;
mutex . unlock ( ) ;
int size = 0 ;
2020-04-02 01:20:12 +02:00
Error err = encode_variant ( var , nullptr , size ) ;
2020-02-27 03:30:20 +01:00
ERR_CONTINUE ( err ! = OK | | size > out_buf . size ( ) - 4 ) ; // 4 bytes separator.
encode_uint32 ( size , buf ) ;
encode_variant ( var , buf + 4 , size ) ;
out_left = size + 4 ;
out_pos = 0 ;
}
int sent = 0 ;
tcp_client - > put_partial_data ( buf + out_pos , out_left , sent ) ;
out_left - = sent ;
out_pos + = sent ;
}
}
void RemoteDebuggerPeerTCP : : _read_in ( ) {
2022-03-27 15:30:17 +02:00
while ( tcp_client - > get_status ( ) = = StreamPeerTCP : : STATUS_CONNECTED & & tcp_client - > wait ( NetSocket : : POLL_TYPE_IN ) = = OK ) {
2020-02-27 03:30:20 +01:00
uint8_t * buf = in_buf . ptrw ( ) ;
if ( in_left < = 0 ) {
if ( in_queue . size ( ) > max_queued_messages ) {
break ; // Too many messages already in queue.
}
if ( tcp_client - > get_available_bytes ( ) < 4 ) {
break ; // Need 4 more bytes.
}
uint32_t size = 0 ;
int read = 0 ;
Error err = tcp_client - > get_partial_data ( ( uint8_t * ) & size , 4 , read ) ;
ERR_CONTINUE ( read ! = 4 | | err ! = OK | | size > ( uint32_t ) in_buf . size ( ) ) ;
in_left = size ;
in_pos = 0 ;
}
int read = 0 ;
tcp_client - > get_partial_data ( buf + in_pos , in_left , read ) ;
in_left - = read ;
in_pos + = read ;
if ( in_left = = 0 ) {
Variant var ;
Error err = decode_variant ( var , buf , in_pos , & read ) ;
ERR_CONTINUE ( read ! = in_pos | | err ! = OK ) ;
ERR_CONTINUE_MSG ( var . get_type ( ) ! = Variant : : ARRAY , " Malformed packet received, not an Array. " ) ;
2024-08-28 11:27:01 +02:00
MutexLock lock ( mutex ) ;
2020-02-27 03:30:20 +01:00
in_queue . push_back ( var ) ;
}
}
}
2025-06-25 07:01:29 +10:00
Error RemoteDebuggerPeerTCP : : _try_connect ( Ref < StreamPeerSocket > tcp_client ) {
2020-02-27 03:30:20 +01:00
const int tries = 6 ;
2022-04-05 13:40:26 +03:00
const int waits [ tries ] = { 1 , 10 , 100 , 1000 , 1000 , 1000 } ;
2020-02-27 03:30:20 +01:00
for ( int i = 0 ; i < tries ; i + + ) {
2022-03-27 15:30:17 +02:00
tcp_client - > poll ( ) ;
2020-02-27 03:30:20 +01:00
if ( tcp_client - > get_status ( ) = = StreamPeerTCP : : STATUS_CONNECTED ) {
print_verbose ( " Remote Debugger: Connected! " ) ;
break ;
} else {
const int ms = waits [ i ] ;
OS : : get_singleton ( ) - > delay_usec ( ms * 1000 ) ;
2025-01-15 12:14:17 -05:00
print_verbose ( " Remote Debugger: Connection failed with status: ' " + String : : num_int64 ( tcp_client - > get_status ( ) ) + " ', retrying in " + String : : num_int64 ( ms ) + " msec. " ) ;
2020-05-19 15:46:49 +02:00
}
}
2020-02-27 03:30:20 +01:00
if ( tcp_client - > get_status ( ) ! = StreamPeerTCP : : STATUS_CONNECTED ) {
2025-01-15 12:14:17 -05:00
ERR_PRINT ( vformat ( " Remote Debugger: Unable to connect. Status: %s. " , String : : num_int64 ( tcp_client - > get_status ( ) ) ) ) ;
2020-02-27 03:30:20 +01:00
return FAILED ;
2020-05-19 15:46:49 +02:00
}
2020-02-27 03:30:20 +01:00
return OK ;
}
void RemoteDebuggerPeerTCP : : _thread_func ( void * p_ud ) {
2022-02-01 21:53:32 +03:00
// Update in time for 144hz monitors
const uint64_t min_tick = 6900 ;
2022-04-05 13:40:26 +03:00
RemoteDebuggerPeerTCP * peer = static_cast < RemoteDebuggerPeerTCP * > ( p_ud ) ;
2020-02-27 03:30:20 +01:00
while ( peer - > running & & peer - > is_peer_connected ( ) ) {
2021-03-16 21:05:06 +01:00
uint64_t ticks_usec = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2020-02-27 03:30:20 +01:00
peer - > _poll ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! peer - > is_peer_connected ( ) ) {
2020-02-27 03:30:20 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2021-03-16 21:05:06 +01:00
ticks_usec = OS : : get_singleton ( ) - > get_ticks_usec ( ) - ticks_usec ;
if ( ticks_usec < min_tick ) {
OS : : get_singleton ( ) - > delay_usec ( min_tick - ticks_usec ) ;
}
2020-02-27 03:30:20 +01:00
}
}
void RemoteDebuggerPeerTCP : : poll ( ) {
2022-10-03 10:57:36 +02:00
// Nothing to do, polling is done in thread.
2020-02-27 03:30:20 +01:00
}
void RemoteDebuggerPeerTCP : : _poll ( ) {
2022-03-27 15:30:17 +02:00
tcp_client - > poll ( ) ;
2020-02-27 03:30:20 +01:00
if ( connected ) {
_write_out ( ) ;
_read_in ( ) ;
connected = tcp_client - > get_status ( ) = = StreamPeerTCP : : STATUS_CONNECTED ;
}
}
2025-06-25 07:01:29 +10:00
RemoteDebuggerPeer * RemoteDebuggerPeerTCP : : create_tcp ( const String & p_uri ) {
2020-03-11 11:23:21 +01:00
ERR_FAIL_COND_V ( ! p_uri . begins_with ( " tcp:// " ) , nullptr ) ;
2020-02-27 03:30:20 +01:00
String debug_host = p_uri . replace ( " tcp:// " , " " ) ;
uint16_t debug_port = 6007 ;
2024-12-05 17:56:08 +01:00
if ( debug_host . contains_char ( ' : ' ) ) {
2024-11-16 18:52:15 +01:00
int sep_pos = debug_host . rfind_char ( ' : ' ) ;
2020-02-27 03:30:20 +01:00
debug_port = debug_host . substr ( sep_pos + 1 ) . to_int ( ) ;
debug_host = debug_host . substr ( 0 , sep_pos ) ;
}
2020-03-11 11:23:21 +01:00
2025-06-25 07:01:29 +10:00
IPAddress ip ;
if ( debug_host . is_valid_ip_address ( ) ) {
ip = debug_host ;
} else {
ip = IP : : get_singleton ( ) - > resolve_hostname ( debug_host ) ;
2020-03-11 11:23:21 +01:00
}
2025-06-25 07:01:29 +10:00
Ref < StreamPeerTCP > stream ;
stream . instantiate ( ) ;
ERR_FAIL_COND_V_MSG ( stream - > connect_to_host ( ip , debug_port ) ! = OK , nullptr , vformat ( " Remote Debugger: Unable to connect to host '%s:%d'. " , debug_host , debug_port ) ) ;
ERR_FAIL_COND_V ( _try_connect ( stream ) , nullptr ) ;
return memnew ( RemoteDebuggerPeerTCP ( stream ) ) ;
}
RemoteDebuggerPeer * RemoteDebuggerPeerTCP : : create_unix ( const String & p_uri ) {
ERR_FAIL_COND_V ( ! p_uri . begins_with ( " unix:// " ) , nullptr ) ;
String debug_path = p_uri . replace ( " unix:// " , " " ) ;
Ref < StreamPeerUDS > stream ;
stream . instantiate ( ) ;
Error err = stream - > connect_to_host ( debug_path ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK & & err ! = ERR_BUSY , nullptr , vformat ( " Remote Debugger: Unable to connect to socket path '%s'. " , debug_path ) ) ;
ERR_FAIL_COND_V ( _try_connect ( stream ) , nullptr ) ;
return memnew ( RemoteDebuggerPeerTCP ( stream ) ) ;
2020-02-27 03:30:20 +01:00
}
RemoteDebuggerPeer : : RemoteDebuggerPeer ( ) {
max_queued_messages = ( int ) GLOBAL_GET ( " network/limits/debugger/max_queued_messages " ) ;
}