Uploaded by Tristan Besser

BesserTristan-CS43203-Project Phase III

advertisement
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ncurses.h>
#define BOARD_WIDTH 43
#define BOARD_HEIGHT 21
#define PADDLE_L_X 1
#define PADDLE_R_X BOARD_WIDTH - 2
#define MAX_LINE_LENGTH 2048
#define MAX_PENDING_CONNECTIONS 5
bool is_host = false;
int final_socket;
bool write_log = false;
pthread_mutex_t board_lock = PTHREAD_MUTEX_INITIALIZER;
int ball_x, ball_y;
int dx, dy;
int paddle_l_y, paddle_r_y;
int score_l, score_r;
WINDOW *window;
int receive_int(int);
void *network_interaction(void*);
int connect_as_host(int);
int connect_as_general(char*,char*);
void handle_signal(int);
void send_string(int, char*);
void send_int(int, int);
int receive_with_check(int, char*);
void print_log(char*);
void draw_board(int,int,int,int,int,int);
void reset_board();
void countdown(const char*);
void game_tick(int);
void *listen_input(void *);
void initialize_ncurses();
int main(int argc, char *argv[]) {
int refresh_rate;
int final_socket;
if(argc >= 3) {
char *host_flag = argv[1];
if (strcmp(host_flag, "--host") == 0){
is_host = true;
if (argc < 4){
printf("Usage: ./pong_game --host PORT DIFFICULTY\n");
exit(1);
}
int port = atoi(argv[2]);
char *difficulty = argv[3];
if(strcmp(difficulty, "easy") == 0) refresh_rate = 80000;
else if(strcmp(difficulty, "medium") == 0) refresh_rate = 40000;
else if(strcmp(difficulty, "hard") == 0) refresh_rate = 20000;
else {
printf("ERROR: Difficulty should be one of easy, medium, hard.\n");
exit(1);
}
final_socket = connect_as_host(port);
if (argc == 5) {
char *str = argv[4];
if (strcmp(str, "DEBUG") == 0) write_log = true;
}
}
else {
final_socket = connect_as_general(argv[1], argv[2]);
char difficulty[MAX_LINE_LENGTH];
receive_with_check(final_socket, difficulty);
if (strcmp(difficulty, "easy") == 0) refresh_rate = 80000;
else if (strcmp(difficulty, "medium") == 0) refresh_rate = 40000;
else if (strcmp(difficulty, "hard") == 0) refresh_rate = 20000;
else {
printf("ERROR: user received weird difficulty setting\n");
exit(1);
}
if (argc == 4) {
char *str = argv[3];
if (strcmp(str, "DEBUG") == 0) write_log = true;
}
}
}
else {
printf("Usage: ./pong_game HOSTNAME PORT\n");
printf("Usage: ./pong_game --host PORT DIFFICULTY\n");
exit(0);
}
final_socket = final_socket;
print_log("CONNECTIONS WORKED");
initialize_ncurses();
reset_board();
countdown("Starting Game");
pthread_t input_thread;
if (pthread_create(&input_thread, NULL, listen_input, &final_socket) < 0){
perror("listen_input socket");
exit(1);
}
pthread_t network_thread;
if (pthread_create(&network_thread, NULL, network_interaction, &final_socket) < 0){
perror("network_interaction socket");
exit(1);
}
struct timeval tv;
while(1) {
signal(SIGINT, handle_signal);
gettimeofday(&tv,NULL);
unsigned long before = 1000000 * tv.tv_sec + tv.tv_usec;
pthread_mutex_lock(&board_lock);
game_tick(final_socket);
pthread_mutex_unlock(&board_lock);
gettimeofday(&tv,NULL);
unsigned long after = 1000000 * tv.tv_sec + tv.tv_usec;
unsigned long to_sleep = refresh_rate - (after - before);
if(to_sleep > refresh_rate) to_sleep = refresh_rate;
usleep(to_sleep);
}
pthread_join(input_thread, NULL);
endwin();
return 0;
}
int receive_int(int sock_fd) {
int num_bytes_received;
int num;
int flags = 0;
if ((num_bytes_received = recv(sock_fd, &num, sizeof(num) , flags)) == -1){
perror("receive error");
close(sock_fd);
exit(1);
}
if (num_bytes_received == 0){
printf("receive_int: zero bytes received\n");
close(sock_fd);
exit(1);
}
return ntohl(num);
}
void *network_interaction(void *sock){
int sock_fd = *(int*)sock;
int read_size;
char buf[MAX_LINE_LENGTH];
char msg_from_other_user[MAX_LINE_LENGTH];
while(1){
receive_with_check(sock_fd, msg_from_other_user);
if (strcmp(msg_from_other_user, "ball") == 0){
pthread_mutex_lock(&board_lock);
dx = receive_int(sock_fd);
dy = receive_int(sock_fd);
ball_x = receive_int(sock_fd);
ball_y = receive_int(sock_fd);
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "ballY") == 0){
pthread_mutex_lock(&board_lock);
dy = receive_int(sock_fd);
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "paddleR") == 0){
pthread_mutex_lock(&board_lock);
paddle_r_y = receive_int(sock_fd);
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "paddleL") == 0){
pthread_mutex_lock(&board_lock);
paddle_l_y = receive_int(sock_fd);
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "scoreL") == 0){
pthread_mutex_lock(&board_lock);
score_l = receive_int(sock_fd);
reset_board();
countdown("<-- SCORE");
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "scoreR") == 0) {
pthread_mutex_lock(&board_lock);
score_r = receive_int(sock_fd);
reset_board();
countdown("SCORE -->");
pthread_mutex_unlock(&board_lock);
}
else if (strcmp(msg_from_other_user, "quit") == 0){
print_log("received SIGINT from other player");
close(sock_fd);
endwin();
exit(1);
}
}
}
int connect_as_host(int port){
print_log("HOST: Attempt Connection");
struct sockaddr_in sin;
bzero((char*)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
int listen_sock_fd;
if ((listen_sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("host: socket");
exit(1);
}
int opt = 1;
if ((setsockopt(listen_sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(int))) <
0){
perror("host: setsocket");
close(listen_sock_fd);
exit(1);
}
if ((bind(listen_sock_fd, (struct sockaddr *)&sin, sizeof(sin))) < 0){
perror("host: bind");
close(listen_sock_fd);
exit(1);
}
if ((listen(listen_sock_fd, MAX_PENDING_CONNECTIONS)) < 0){
perror("host: listen");
close(listen_sock_fd);
exit(1);
}
printf("Waiting for challengers on port %d\n", port);
int host_sock_fd, addr_len;
struct sockaddr_in client_addr;
while ((host_sock_fd = accept(listen_sock_fd, (struct sockaddr*)&client_addr,
(socklen_t*)&addr_len)) >= 0){
print_log("HOST: Accepted connection");
close(listen_sock_fd);
return host_sock_fd;
}
}
int connect_as_general(char *hostname, char * port){
print_log("GENERAL: Attempt Connection");
struct addrinfo hints, *client_info, *ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int return_val;
if ((return_val = getaddrinfo(hostname, port, &hints, &client_info)) != 0){
perror("getaddrinfo");
exit(1);
}
printf("Connecting to %s on port %s\n", hostname, port);
int client_sock_fd;
for (ptr = client_info; ptr != NULL; ptr = ptr->ai_next) {
if ((client_sock_fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) < 0){
perror("Client: bad socket");
continue;
}
if (connect(client_sock_fd, ptr->ai_addr, ptr->ai_addrlen) < 0){
perror("client: bad connect");
close(client_sock_fd);
continue;
}
}
printf("Connected to %s\n", hostname);
return client_sock_fd;
}
void handle_signal(int signal){
print_log("SIGINT");
send_string(final_socket, "quit");
endwin();
exit(0);
}
void send_string(int sock, char *to_send){
print_log("sending: ");
print_log(to_send);
int len = strlen(to_send);
int flags = 0;
if (send(sock, to_send, len, flags) == -1){
perror("client send error!\n");
exit(1);
}
}
void send_int(int sock, int to_send){
to_send = htonl(to_send);
if (send(sock, &to_send, sizeof(to_send), 0) == -1){
perror("sendInt error");
exit(1);
}
}
int receive_with_check(int sock_fd, char *out_msg) {
int num_bytes_received;
char buf[MAX_LINE_LENGTH];
int len = sizeof(buf);
int flags = 0;
memset(buf, 0, len);
if ((num_bytes_received = recv(sock_fd, buf, len, flags)) == -1){
perror("receive error");
close(sock_fd);
exit(1);
}
if (num_bytes_received == 0){
printf("receive_with_check: zero bytes received\n");
close(sock_fd);
exit(1);
}
strcpy(out_msg, buf);
print_log("receiving: ");
print_log(out_msg);
return num_bytes_received;
}
void print_log(char *msg){
if (write_log){
FILE *f = fopen("log", "a");
fprintf(f, "%s \n", msg);
fclose(f);
}
}
void draw_board(int ball_x, int ball_y, int paddle_l_y, int paddle_r_y, int score_l, int score_r) {
int y;
for(y = 1; y < BOARD_HEIGHT-1; y++) {
mvwaddch(window, y, BOARD_WIDTH / 2, ACS_VLINE);
}
mvwprintw(window, 1, BOARD_WIDTH / 2 - 3, "%2d", score_l);
mvwprintw(window, 1, BOARD_WIDTH / 2 + 2, "%d", score_r);
mvwaddch(window, ball_y, ball_x, ACS_BLOCK);
for(y = 1; y < BOARD_HEIGHT - 1; y++) {
int ch = (y >= paddle_l_y - 2 && y <= paddle_l_y + 2)? ACS_BLOCK : ' ';
mvwaddch(window, y, PADDLE_L_X, ch);
}
for(y = 1; y < BOARD_HEIGHT - 1; y++) {
int ch = (y >= paddle_r_y - 2 && y <= paddle_r_y + 2)? ACS_BLOCK : ' ';
mvwaddch(window, y, PADDLE_R_X, ch);
}
wrefresh(window);
mvwaddch(window, ball_y, ball_x, ' ');
}
void reset_board() {
ball_x = BOARD_WIDTH / 2;
paddle_l_y = paddle_r_y = ball_y = BOARD_HEIGHT / 2;
dx = (rand() % 2) * 2 - 1;
dy = 0;
draw_board(ball_x, ball_y, paddle_l_y, paddle_r_y, score_l, score_r);
}
void countdown(const char *message) {
int h = 4;
int w = strlen(message) + 4;
WINDOW *popup = newwin(h, w, (LINES - h) / 2, (COLS - w) / 2);
box(popup, 0, 0);
mvwprintw(popup, 1, 2, message);
int countdown;
for(countdown = 3; countdown > 0; countdown--) {
mvwprintw(popup, 2, w / 2, "%d", countdown);
wrefresh(popup);
sleep(1);
}
wclear(popup);
wrefresh(popup);
delwin(popup);
paddle_l_y = paddle_r_y = BOARD_HEIGHT / 2;
}
void game_tick(int sock_fd) {
ball_x += dx;
ball_y += dy;
int pad_y = (ball_x < BOARD_WIDTH / 2) ? paddle_l_y : paddle_r_y;
int col_x = (ball_x < BOARD_WIDTH / 2) ? PADDLE_L_X + 1 : PADDLE_R_X - 1;
if(ball_x < 1 || ball_x >= BOARD_WIDTH - 1) {
if(ball_y >= pad_y - 2 && ball_y <= pad_y + 2) {
dx = -dx;
if(ball_y == pad_y - 2) dy = -1;
if(ball_y == pad_y + 2) dy = 1;
} else {
if(ball_x < 1) {
score_r++;
send_string(sock_fd, "scoreR");
send_int(sock_fd, score_r);
}
if(ball_x >= BOARD_WIDTH - 1) {
score_l++;
send_string(sock_fd, "scoreL");
send_int(sock_fd, score_l);
}
reset_board();
}
}
if(ball_y < 1 || ball_y >= BOARD_HEIGHT - 1) dy = -dy;
if (is_host) {
send_string(sock_fd, "ball");
send_int(sock_fd, dx);
send_int(sock_fd, dy);
send_int(sock_fd, ball_x);
send_int(sock_fd, ball_y);
}
draw_board(ball_x, ball_y, paddle_l_y, paddle_r_y, score_l, score_r);
}
void *listen_input(void *sock_fd) {
int sock = *(int*)sock_fd;
while(1) {
int ch = getch();
if(ch == KEY_UP) {
if(paddle_r_y > 3) paddle_r_y--;
if (is_host){
send_string(sock, "paddleR");
send_int(sock, paddle_r_y);
}
}
if(ch == KEY_DOWN) {
if(paddle_r_y < BOARD_HEIGHT - 4) paddle_r_y++;
if (is_host){
send_string(sock, "paddleR");
send_int(sock, paddle_r_y);
}
}
if(ch == 'w') {
if(paddle_l_y > 3) paddle_l_y--;
if (is_host){
send_string(sock, "paddleL");
send_int(sock, paddle_l_y);
}
}
if(ch == 's') {
if(paddle_l_y < BOARD_HEIGHT - 4) paddle_l_y++;
if (is_host){
send_string(sock, "paddleL");
send_int(sock, paddle_l_y);
}
}
}
}
void initialize_ncurses() {
initscr();
cbreak();
noecho();
curs_set(0);
timeout(0);
keypad(stdscr, TRUE);
if(has_colors()) {
start_color();
init_pair(1, COLOR_GREEN, COLOR_BLACK);
init_pair(2, COLOR_RED, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_CYAN, COLOR_BLACK);
}
window = newwin(BOARD_HEIGHT, BOARD_WIDTH, (LINES - BOARD_HEIGHT) / 2,
(COLS - BOARD_WIDTH) / 2);
box(window, 0, 0);
wrefresh(window);
}
This is the source code for netpong.The implementation was a lot easier in C++ so
that’s what I did. This is a screenshot of the program working utilizing the localhost and
2 separate terminals. The game is playable by using WASD on the server terminal and
arrows on the client terminal.
There was a bug I could not figure out where sometimes my paddle would disappear. I
would like to solve this, but with the time given on the assignment, this is all I could
muster up.
Here is the makefile:
all: netpong
netpong.o: netpong.cpp
g++ -std=c++11 -c -o netpong.o netpong.cpp
netpong: netpong.o
g++ -o netpong -std=c++11 -lncurses -lpthread -lz netpong.o
clean:
@echo Cleaning...
@rm netpong
@rm netpong.o
Download