/*--------------------------------------------------------------------------*/ /* network interface support */ /*--------------------------------------------------------------------------*/ #include #include #include #ifdef NETWORKING #include #include #include #include #include #include #include #include #include #include #include #endif #include "network.h" #include "actors.h" #include "canvas.h" #include "board.h" #include "main.h" /*--------------------------------------------------------------------------*/ /* define */ /*--------------------------------------------------------------------------*/ #define FONT_NAME "-misc-fixed-medium-*-normal-*-20-0-*-*-*-*-iso8859-1" #define TIMEOUT 3 #define RETRIES 4 #define ACK (htonl(12345678)) /*--------------------------------------------------------------------------*/ /* functions */ /*--------------------------------------------------------------------------*/ static void network_print(char *format, ...); #ifdef NETWORKING static int network_create_socket(int port); static void *network_thread_server(NETWORK_CONFIG *config); static void *network_thread_client(NETWORK_CONFIG *config); static void network_end_game(void); static int network_write(long info); static long network_read(void); #endif /*--------------------------------------------------------------------------*/ /* variables */ /*--------------------------------------------------------------------------*/ #ifdef NETWORKING static NETWORK_CONFIG *configs[2]; static int network_side; static int network_socket = 0; static struct sockaddr_in network_remote_name; static int network_ready; static int network_count; static pthread_t network_thread; static pthread_mutex_t network_mutex; #endif static void *font = NULL; static int font_color; static int font_y; /*--------------------------------------------------------------------------*/ /* network_print */ /*--------------------------------------------------------------------------*/ void network_print(char *format, ...) { char str[128]; va_list argptr; int w, h; #ifdef NETWORKING typedef void (*r_t)(void *); pthread_cleanup_push((r_t)pthread_mutex_unlock, (void *)&network_mutex); pthread_mutex_lock(&network_mutex); #endif if (font == NULL) { font = canvas_font_load(FONT_NAME); font_color = canvas_alloc_color(255, 0, 0); } va_start(argptr, format); vsprintf(str, format, argptr); va_end(argptr); canvas_font_size(str, font, &w, &h); canvas_font_print(str, (CANVAS_WIDTH - w) / 2, font_y, font, font_color); font_y += h * 2; canvas_refresh(); #ifdef DEBUG printf("%s\n", str); #endif #ifdef NETWORKING pthread_mutex_unlock(&network_mutex); pthread_cleanup_pop(1); #endif } /*--------------------------------------------------------------------------*/ /* network_start */ /*--------------------------------------------------------------------------*/ int network_start(int side, NETWORK_CONFIG *config) { int e; #ifdef NETWORKING void *func; struct timespec tv; configs[side] = config; configs[!side] = NULL; network_side = side; if (network_socket != 0) { close(network_socket); network_socket = 0; } network_ready = 0; #endif font_y = CANVAS_HEIGHT / 8; /* somewhere at the top */ canvas_clear(); canvas_install_colormap(1); #ifdef NETWORKING pthread_mutex_init(&network_mutex, NULL); func = network_thread_server; /* default to server-mode */ if (side == 0) /* but if light side is network */ func = network_thread_client; /* then we're in client-mode */ e = pthread_create(&network_thread, NULL, func, configs[side]); if (e != 0) network_print("Network: pthread_create() failed: %s\n", strerror(e)); else { e = pthread_detach(network_thread); if (e != 0) network_print("Network: pthread_detach() failed: %s\n", strerror(e)); } while (!network_ready) { pthread_mutex_lock(&network_mutex); e = !main_handle_events(0) || canvas_key_down(0xFF1B); pthread_mutex_unlock(&network_mutex); if (e) { pthread_cancel(network_thread); if (network_socket != 0) { close(network_socket); network_socket = 0; } canvas_key_event(NULL); /* reset Escape (and all other) */ canvas_install_colormap(0); return 0; } tv.tv_sec = 0; tv.tv_nsec = 200000000; nanosleep(&tv, NULL); } pthread_cancel(network_thread); fcntl(network_socket, F_SETFL, O_NONBLOCK); network_count = 0; return 1; #else /* ! NETWORKING */ network_print("Networking support has not been compiled"); while (1) { e = !main_handle_events(0) || canvas_key_down(0xFF1B); if (e) { canvas_key_event(NULL); /* reset Escape (and all other) */ canvas_install_colormap(0); return 0; } } #endif } #ifdef NETWORKING /*--------------------------------------------------------------------------*/ /* network_create_socket */ /*--------------------------------------------------------------------------*/ int network_create_socket(int port) { int sock; int yesno; struct linger linger; int err = 0; struct sockaddr_in name; sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { network_print("Network: socket() failed: %s", strerror(errno)); pthread_exit(NULL); } if (err == 0) { yesno = 1; err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yesno, sizeof(yesno)); } if (err == 0) { yesno = 0; err = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yesno, sizeof(yesno)); } if (err == 0) { linger.l_onoff = 0; err = setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); } if (err < 0) { network_print("Network: setsockopt() failed: %s", strerror(errno)); pthread_exit(NULL); } name.sin_family = AF_INET; name.sin_addr.s_addr = htonl(INADDR_ANY); name.sin_port = htons(port); if (bind(sock, (struct sockaddr *)&name, sizeof(struct sockaddr_in)) < 0) { network_print("Network: bind() failed: %s", strerror(errno)); pthread_exit(NULL); } return sock; } /*--------------------------------------------------------------------------*/ /* network_thread_server */ /*--------------------------------------------------------------------------*/ void *network_thread_server(NETWORK_CONFIG *config) { struct sockaddr_in name; int name_len; char dummy[8]; int n; network_socket = network_create_socket(config->port); network_print("Network: waiting for dark side to connect on port %d", config->port); while (1) { name_len = sizeof(struct sockaddr_in); n = recvfrom(network_socket, dummy, 1, 0, (struct sockaddr *)&name, &name_len); if (n < 0) { network_print("Network: recvfrom() failed: %s", strerror(errno)); pthread_exit(NULL); } if (n == 1 && dummy[0] == '?') break; } network_print("Network: connection from %s, port %d", inet_ntoa(name.sin_addr), ntohs(name.sin_port)); dummy[0] = '!'; *((long *)&dummy + 1) = htonl(random_index); if (sendto(network_socket, dummy, 8, 0, (struct sockaddr *)&name, sizeof(struct sockaddr_in)) < 0) { network_print("Network: sendto() failed: %s", strerror(errno)); pthread_exit(NULL); } sleep(1); memcpy(&network_remote_name, &name, sizeof(struct sockaddr_in)); network_ready = 1; return NULL; } /*--------------------------------------------------------------------------*/ /* network_thread_client */ /*--------------------------------------------------------------------------*/ void *network_thread_client(NETWORK_CONFIG *config) { struct sockaddr_in name; int name_len; struct hostent *host; char dummy[8] = "?"; int n; network_socket = network_create_socket(0); network_print("Network: resolving %s", config->address); host = gethostbyname(config->address); if (host == NULL) { network_print("Network: gethostbyname() failed: error %d", h_errno); pthread_exit(NULL); } name.sin_family = AF_INET; name.sin_addr = *(struct in_addr *)host->h_addr; name.sin_port = htons(config->port); network_print("Network: connecting to %s on port %d", inet_ntoa(name.sin_addr), config->port); if (sendto(network_socket, dummy, 1, 0, (struct sockaddr *)&name, sizeof(struct sockaddr_in)) < 0) { network_print("Network: sendto() failed: %s", strerror(errno)); pthread_exit(NULL); } memcpy(&network_remote_name, &name, sizeof(struct sockaddr_in)); network_print("Network: waiting for light side to acknowledge"); while (1) { name_len = sizeof(struct sockaddr_in); n = recvfrom(network_socket, dummy, 8, 0, (struct sockaddr *)&name, &name_len); if (n < 0) { network_print("Network: recvfrom() failed: %s", strerror(errno)); pthread_exit(NULL); } if (n == 8 && dummy[0] == '!') { random_index = ntohl(*((long *)&dummy + 1)); break; } } network_ready = 1; return NULL; } #endif /* NETWORKING */ /*--------------------------------------------------------------------------*/ /* network_turn */ /*--------------------------------------------------------------------------*/ void network_turn(int side, int mode, COMMAND *cmd) { #ifdef NETWORKING network_side = side; #endif } /*--------------------------------------------------------------------------*/ /* network_end_game */ /*--------------------------------------------------------------------------*/ void network_end_game(void) { font_y = CANVAS_HEIGHT / 8; /* somewhere at the top */ network_print("Network: game ended due to broken connection"); canvas_refresh(); board_end_game(); } #ifdef NETWORKING /*--------------------------------------------------------------------------*/ /* network_write */ /*--------------------------------------------------------------------------*/ int network_write(long info) { int retry; fd_set fds; struct timeval tv1, tv2; long data[2]; struct sockaddr_in name; int name_len; /* send a packet to the other side, and don't continue until */ /* we get an acknowledgement for this packet. */ for (retry = 1; retry <= RETRIES; retry++) { data[0] = htonl(network_count); data[1] = htonl(info); sendto(network_socket, data, sizeof(data), 0, (struct sockaddr *)&network_remote_name, sizeof(struct sockaddr_in)); #ifdef DEBUG printf("WRITE: Sent info packet with count=%d, info=%ld\n", network_count, info); #endif gettimeofday(&tv2, NULL); tv2.tv_sec += TIMEOUT; while (1) { #ifndef DEBUG gettimeofday(&tv1, NULL); if (tv1.tv_sec >= tv2.tv_sec) break; #endif tv1.tv_sec = 1; tv1.tv_usec = 0; FD_ZERO(&fds); FD_SET(network_socket, &fds); if (select(network_socket + 1, &fds, NULL, NULL, &tv1) <= 0) continue; name_len = sizeof(struct sockaddr_in); if (recvfrom(network_socket, data, sizeof(data), 0, (struct sockaddr *)&name, &name_len) != sizeof(data)) continue; if (name.sin_addr.s_addr != network_remote_name.sin_addr.s_addr || name.sin_port != network_remote_name.sin_port) continue; #ifdef DEBUG printf("WRITE: Received packet, count=%ld, info=%ld\n", ntohl(data[0]), ntohl(data[1])); #endif if (ntohl(data[0]) == network_count && data[1] == ACK) return 0; } #ifdef DEBUG printf("Try %d for count %d FAILED. Retrying\n", retry, network_count); #endif } printf("Network has died.\n"); network_end_game(); return -1; } /*--------------------------------------------------------------------------*/ /* network_read */ /*--------------------------------------------------------------------------*/ long network_read(void) { fd_set fds; struct timeval tv1, tv2; long data[2]; struct sockaddr_in name; int name_len; long info; /* receive a packet from the other side and acknowledge it. */ /* old packets are discarded. */ gettimeofday(&tv2, NULL); tv2.tv_sec += TIMEOUT * RETRIES; while (1) { #ifndef DEBUG gettimeofday(&tv1, NULL); if (tv1.tv_sec >= tv2.tv_sec) { network_end_game(); return -1; } #endif tv1.tv_sec = 1; tv1.tv_usec = 0; FD_ZERO(&fds); FD_SET(network_socket, &fds); if (select(network_socket + 1, &fds, NULL, NULL, &tv1) <= 0) continue; name_len = sizeof(struct sockaddr_in); if (recvfrom(network_socket, data, sizeof(data), 0, (struct sockaddr *)&name, &name_len) != sizeof(data)) continue; if (name.sin_addr.s_addr != network_remote_name.sin_addr.s_addr || name.sin_port != network_remote_name.sin_port) continue; #ifdef DEBUG printf("READ : Received packet, count=%d, info=%d\n", ntohl(data[0]), ntohl(data[1])); #endif if (data[1] == ACK) continue; info = ntohl(data[1]); data[1] = ACK; sendto(network_socket, data, sizeof(data), 0, (struct sockaddr *)&network_remote_name, sizeof(struct sockaddr_in)); #ifdef DEBUG printf("READ : Sent ack packet with same count\n"); #endif if (ntohl(data[0]) == network_count) { #ifdef DEBUG printf("READ : Returning with info=%d\n", info); #endif return info; } } } #endif /* NETWORKING */ /*--------------------------------------------------------------------------*/ /* network_frame */ /*--------------------------------------------------------------------------*/ void network_frame(int *keys_down) { #ifdef NETWORKING long info; int i; network_count++; #ifdef DEBUG printf("*FRM*: Network Count = %d for Side = %d\n", network_count, network_side); #endif if (configs[network_side] != NULL) { /* network side */ memset(keys_down, 0, STATE_MOVE_COUNT * sizeof(int)); info = network_read(); if (info != -1) for (i = 0; i < STATE_MOVE_COUNT; i++) keys_down[i] = (info & (1 << i)) == (1 << i); } else { /* real player side */ info = 0; for (i = 0; i < STATE_MOVE_COUNT; i++) info |= (keys_down[i] != 0) << i; network_write(info); } #endif /* NETWORKING */ } /*--------------------------------------------------------------------------*/ /* network_config_read */ /*--------------------------------------------------------------------------*/ void network_config_read(FILE *fp, NETWORK_CONFIG *config) { fscanf(fp, "%64s %d", config->address, &config->port); } /*--------------------------------------------------------------------------*/ /* network_config_write */ /*--------------------------------------------------------------------------*/ void network_config_write(FILE *fp, NETWORK_CONFIG *config) { fprintf(fp, "%-64s %d\n", config->address, config->port); } /****************************************************************************/ /* */ /* GTK+ Stuff */ /* */ /****************************************************************************/ #include #include "gtk-callbacks.h" #include "gtk-interface.h" #include "gtk-support.h" /*--------------------------------------------------------------------------*/ /* variables */ /*--------------------------------------------------------------------------*/ static NETWORK_CONFIG *orig_config; static GtkWidget *window; /*--------------------------------------------------------------------------*/ /* network_config_edit */ /*--------------------------------------------------------------------------*/ void network_config_edit(NETWORK_CONFIG *_config) { GtkWidget *widget; char port[5]; orig_config = _config; window = create_network_window(); widget = lookup_widget(window, "address"); gtk_entry_set_text(GTK_ENTRY(widget), orig_config->address); widget = lookup_widget(window, "port"); sprintf(port, "%d", orig_config->port); gtk_entry_set_text(GTK_ENTRY(widget), port); gtk_widget_show(window); } /*--------------------------------------------------------------------------*/ /* network_ok_clicked */ /*--------------------------------------------------------------------------*/ void network_ok_clicked(GtkButton *button, gpointer data) { GtkWidget *widget; char *str; char port[8]; widget = lookup_widget(window, "address"); str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, sizeof(orig_config->address) - 2); strcpy(orig_config->address, str); widget = lookup_widget(window, "port"); str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, sizeof(port) - 2); orig_config->port = atoi(str); gtk_widget_destroy(window); } /*--------------------------------------------------------------------------*/ /* network_cancel_clicked */ /*--------------------------------------------------------------------------*/ void network_cancel_clicked(GtkObject *object, gpointer data) { gtk_widget_destroy(window); }