86#include <netinet/tcp.h>
94#define DEBUG_NETWORK 1
95#define DEBUG_THREADS 1
131static atomic_bool g_connection_active =
false;
141static atomic_bool g_connection_lost =
false;
151static atomic_bool g_should_reconnect =
false;
172static char g_server_ip[256] = {0};
183static mutex_t g_send_mutex = {0};
208static bool g_encryption_enabled =
false;
215#define MAX_RECONNECT_DELAY (5 * 1000 * 1000)
229static unsigned int get_reconnect_delay(
unsigned int reconnect_attempt) {
233 unsigned int delay_us = 100000 + (reconnect_attempt - 1) * 200000;
256 log_debug(
"Closing socket %d", socketfd);
290 log_error(
"Failed to initialize send mutex");
296 g_client_transport = NULL;
297 atomic_store(&g_connection_active,
false);
298 atomic_store(&g_connection_lost,
false);
299 atomic_store(&g_should_reconnect,
false);
323 bool has_ever_connected) {
324 (void)first_connection;
325 if (!address || port <= 0) {
326 log_error(
"Invalid address or port parameters");
337 if (reconnect_attempt > 0) {
338 unsigned int delay_us = get_reconnect_delay(reconnect_attempt);
344 log_debug(
"Exit requested during reconnection delay");
355 (strcmp(address,
"localhost") == 0 || strcmp(address,
"127.0.0.1") == 0 || strcmp(address,
"::1") == 0);
357 struct addrinfo hints, *res = NULL, *addr_iter;
358 memset(&hints, 0,
sizeof(hints));
359 hints.ai_family = AF_UNSPEC;
360 hints.ai_socktype = SOCK_STREAM;
362 hints.ai_flags = AI_NUMERICSERV;
370 log_debug(
"Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
371 hints.ai_family = AF_INET6;
372 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
374 int ipv6_result = getaddrinfo(
"::1", port_str, &hints, &res);
375 if (ipv6_result == 0 && res != NULL) {
377 g_sockfd =
socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
379 log_info(
"Trying IPv6 loopback connection to [::1]:%s...", port_str);
381 log_debug(
"Connection successful using IPv6 loopback");
385 goto connection_success;
401 log_debug(
"Exit requested during connection attempt");
406 log_debug(
"IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
407 hints.ai_family = AF_INET;
409 int ipv4_result = getaddrinfo(
"127.0.0.1", port_str, &hints, &res);
410 if (ipv4_result == 0 && res != NULL) {
411 g_sockfd =
socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
413 log_info(
"Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
415 log_debug(
"Connection successful using IPv4 loopback");
416 SAFE_STRNCPY(g_server_ip,
"127.0.0.1",
sizeof(g_server_ip));
419 goto connection_success;
434 log_warn(
"Could not connect to localhost using either IPv6 or IPv4 loopback");
439 log_debug(
"Resolving server address '%s' port %s...", address, port_str);
440 hints.ai_family = AF_UNSPEC;
442 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
443 if (getaddr_result != 0) {
444 log_error(
"Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
450 for (
int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
451 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
453 if (addr_iter->ai_family != address_family) {
458 g_sockfd =
socket_create(addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
465 if (addr_iter->ai_family == AF_INET) {
467 }
else if (addr_iter->ai_family == AF_INET6) {
474 log_debug(
"Connection successful using %s", addr_iter->ai_family == AF_INET ?
"IPv4"
475 : addr_iter->ai_family == AF_INET6 ?
"IPv6"
476 :
"unknown protocol");
479 if (
format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, g_server_ip,
sizeof(g_server_ip)) ==
481 log_debug(
"Resolved server IP: %s", g_server_ip);
483 log_warn(
"Failed to format server IP address");
486 goto connection_success;
508 log_warn(
"Could not connect to server %s:%d (tried all addresses)", address, port);
513 struct sockaddr_storage local_addr = {0};
514 socklen_t addr_len =
sizeof(local_addr);
515 if (getsockname(g_sockfd, (
struct sockaddr *)&local_addr, &addr_len) == -1) {
524 if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET) {
525 local_port =
NET_TO_HOST_U16(((
struct sockaddr_in *)&local_addr)->sin_port);
526 }
else if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
527 local_port =
NET_TO_HOST_U16(((
struct sockaddr_in6 *)&local_addr)->sin6_port);
529 g_my_client_id = (
uint32_t)local_port;
532 atomic_store(&g_connection_active,
true);
533 atomic_store(&g_connection_lost,
false);
534 atomic_store(&g_should_reconnect,
false);
538 log_debug(
"CLIENT_CONNECT: Calling client_crypto_init()");
540 log_error(
"Failed to initialize crypto (password required or incorrect)");
541 log_debug(
"CLIENT_CONNECT: client_crypto_init() failed");
548 log_debug(
"CLIENT_CONNECT: Calling client_crypto_handshake()");
550 if (handshake_result != 0) {
552 log_debug(
"CLIENT_CONNECT: client_crypto_handshake() failed with code %d", handshake_result);
556 "Crypto handshake failed with server - this usually indicates a protocol mismatch or network issue");
558 log_debug(
"CLIENT_CONNECT: client_crypto_handshake() succeeded");
564 if (!g_client_transport) {
565 log_error(
"Failed to create ACIP transport");
570 log_debug(
"CLIENT_CONNECT: Created ACIP transport with crypto context");
575 log_debug(
"Connected to server - terminal logging will be disabled after initial setup");
577 log_debug(
"Connected to server - terminal logging kept enabled for snapshot mode");
601 if (!
GET_OPTION(snapshot_mode) && has_ever_connected) {
603 log_debug(
"Reconnected to server - terminal logging disabled to prevent interference with ASCII display");
608 log_info(
"GET_OPTION(audio_enabled) = %d (sending CLIENT_JOIN)",
GET_OPTION(audio_enabled));
610 log_info(
"Adding CLIENT_CAP_AUDIO to capabilities");
625 SAFE_SNPRINTF(my_display_name,
sizeof(my_display_name),
"%s-%d", display_name, pid);
669 return g_client_transport;
683 log_debug(
"server_connection_set_transport() called with transport=%p", (
void *)transport);
686 if (g_client_transport) {
687 log_warn(
"Replacing existing transport with new fallback transport");
691 log_debug(
"Setting g_client_transport to %p", (
void *)transport);
692 g_client_transport = transport;
696 log_debug(
"Transport is non-NULL, extracting socket...");
698 g_sockfd = acip_transport_get_socket(transport);
699 log_debug(
"Socket extracted: %d", (
int)g_sockfd);
701 atomic_store(&g_connection_active,
true);
702 log_debug(
"Server connection transport set and marked active (sockfd=%d)", (
int)g_sockfd);
705 atomic_store(&g_connection_active,
false);
706 log_debug(
"Server connection transport cleared and marked inactive");
709 log_debug(
"server_connection_set_transport() completed");
720 return g_my_client_id;
750 log_debug(
"Server IP set to: %s", g_server_ip);
752 g_server_ip[0] =
'\0';
766 atomic_store(&g_connection_active,
false);
769 if (g_client_transport) {
771 g_client_transport = NULL;
782 if (g_encryption_enabled) {
784 g_encryption_enabled =
false;
806 atomic_store(&g_connection_active,
false);
807 atomic_store(&g_connection_lost,
true);
831 atomic_store(&g_connection_lost,
true);
832 atomic_store(&g_connection_active,
false);
847 return atomic_load(&g_connection_lost);
889 if (!atomic_load(&g_connection_active) || !g_client_transport) {
961 int frame_duration) {
965 if (!atomic_load(&g_connection_active) || !g_client_transport) {
971 size_t header_size = 16;
972 size_t total_size = header_size + opus_size;
984 memcpy(buf + 4, &fd, 4);
985 memset(buf + 8, 0, 8);
988 memcpy(buf + header_size, opus_data, opus_size);
1022 const uint16_t *frame_sizes,
int frame_count) {
1026 if (!atomic_load(&g_connection_active) || !g_client_transport) {
1120 log_warn(
"Terminal capability detection not reliable, using fallback");
1121 SAFE_MEMSET(&caps,
sizeof(caps), 0,
sizeof(caps));
1143 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom :
"";
1195 SAFE_MEMSET(&join_packet,
sizeof(join_packet), 0,
sizeof(join_packet));
1202 if (send_result == 0) {
1204 bool active = atomic_load(&g_connection_active);
1205 socket_t socket_snapshot = g_sockfd;
1210 "CLIENT_JOIN sent (display=\"%s\", capabilities=0x%x)", join_packet.
display_name, capabilities);
π Cross-platform abstraction layer umbrella header for ascii-chat
ποΈ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
bool should_exit()
Check if client should exit.
void crypto_handshake_cleanup(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
Common declarations and data structures for cryptographic handshake.
ascii-chat Client Display Management Interface
π Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
#define HOST_TO_NET_U32(val)
#define NET_TO_HOST_U16(val)
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
int threaded_send_pong_packet(void)
Thread-safe pong packet transmission.
void server_connection_close()
Close the server connection gracefully.
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_shutdown()
Emergency connection shutdown for signal handlers.
int threaded_send_terminal_size_with_auto_detect(unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
int threaded_send_client_join_packet(const char *display_name, uint32_t capabilities)
Thread-safe client join packet transmission.
acip_transport_t * server_connection_get_transport(void)
Get ACIP transport instance.
int threaded_send_stream_start_packet(uint32_t stream_type)
Thread-safe stream start packet transmission.
uint32_t server_connection_get_client_id()
Get client ID assigned by server.
socket_t server_connection_get_socket()
Get current socket file descriptor.
asciichat_error_t threaded_send_audio_opus(const uint8_t *opus_data, size_t opus_size, int sample_rate, int frame_duration)
Thread-safe Opus audio frame transmission.
void server_connection_set_transport(acip_transport_t *transport)
Set ACIP transport instance from connection fallback.
void server_connection_set_ip(const char *ip)
Set the server IP address.
int threaded_send_ping_packet(void)
Thread-safe ping packet transmission.
bool server_connection_is_lost()
Check if connection loss has been detected.
int server_connection_init()
Initialize the server connection management subsystem.
asciichat_error_t threaded_send_audio_opus_batch(const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int frame_count)
Thread-safe Opus audio batch packet transmission.
asciichat_error_t threaded_send_packet(packet_type_t type, const void *data, size_t len)
Thread-safe packet transmission.
int threaded_send_audio_batch_packet(const float *samples, int num_samples, int batch_count)
Thread-safe batched audio packet transmission.
void server_connection_cleanup()
Cleanup connection management subsystem.
void server_connection_lost()
Signal that connection has been lost.
const char * server_connection_get_ip()
Get resolved server IP address.
int server_connection_establish(const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
Establish connection to ascii-chat server.
crypto_handshake_context_t g_crypto_ctx
Per-connection crypto handshake context.
const crypto_context_t * crypto_client_get_context(void)
Get crypto context for encryption/decryption.
int client_crypto_init(void)
Initialize client crypto handshake.
int client_crypto_handshake(socket_t socket)
Perform crypto handshake with server.
bool crypto_client_is_ready(void)
Check if crypto handshake is ready.
void display_full_reset()
Perform full display reset.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MEMSET(dest, dest_size, ch, count)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
int g_max_fps
Runtime configurable maximum frame rate (can be overridden via environment or command line)
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
uint32_t color_level
Color level enum value (terminal_color_mode_t)
uint32_t color_count
Actual color count (16, 256, or 16777216)
char display_name[MAX_DISPLAY_NAME_LEN]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
uint16_t height
Terminal height in characters.
#define CLIENT_CAP_STRETCH
Client can stretch frames to fill terminal.
asciichat_error_t send_audio_batch_packet(socket_t sockfd, const float *samples, int num_samples, int batch_count, crypto_context_t *crypto_ctx)
Send a batched audio packet with encryption support.
uint8_t reserved[2]
Reserved bytes for alignment (must be zero)
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
#define CLIENT_CAP_COLOR
Client supports color rendering.
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
uint16_t width
Terminal width in characters.
const char * network_error_string()
Get human-readable error string for network errors.
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
uint32_t client_id
Unique client identifier (1-9, 0 = server)
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
char colorterm[32]
$COLORTERM environment variable value (for debugging)
uint32_t palette_type
Palette type enum value (palette_type_t)
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
#define CLIENT_CAP_AUDIO
Client can send/receive audio.
#define CLIENT_CAP_VIDEO
Client can send/receive video.
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
char term_type[32]
$TERM environment variable value (for debugging)
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
const options_t * options_get(void)
Get current options (lock-free read)
#define COLOR_MODE_NONE
Monochrome mode.
packet_type_t
Network protocol packet type enumeration.
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
@ PACKET_TYPE_PONG
Keepalive pong response.
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
@ PACKET_TYPE_PING
Keepalive ping packet.
@ PALETTE_CUSTOM
User-defined via βpalette-chars.
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
π IP Address Parsing and Formatting Utilities
π Core network I/O operations with timeout support
βοΈ Command-line options parsing and configuration management for ascii-chat
Packet protocol implementation with encryption and compression support.
ASCII Palette Management for Video-to-ASCII Conversion.
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len)
Send packet via transport with proper header (exported for generic wrappers)
asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
Send batched Opus-encoded audio frames.
ACIP shared/bidirectional packet sending functions.
ascii-chat Server Mode Entry Point Header
#define MAX_RECONNECT_DELAY
ascii-chat Client Server Connection Management Interface
@ CONNECTION_ERROR_AUTH_FAILED
Authentication failure (no retry)
Server cryptographic operations and per-client handshake management.
Transport instance structure.
Client information packet structure.
Cryptographic context structure.
Cryptographic handshake context structure.
Consolidated options structure.
Terminal capabilities packet structure (Packet Type 5)
Complete terminal capabilities structure.
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
bool utf8_support
True if terminal supports UTF-8 encoding.
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
uint32_t color_count
Maximum number of colors (16, 256, or 16777216)
char term_type[64]
$TERM environment variable value (for debugging)
char colorterm[64]
$COLORTERM environment variable value (for debugging)
bool detection_reliable
True if detection is confident (reliable detection)
Cross-platform system functions interface for ascii-chat.
π₯οΈ Cross-platform terminal interface for ascii-chat
β±οΈ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.
acip_transport_t * acip_tcp_transport_create(socket_t sockfd, crypto_context_t *crypto_ctx)
Create TCP transport from existing socket.
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.
Common SIMD utilities and structures.