Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inter-process communication #8

Merged
merged 2 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 96 additions & 25 deletions client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
#include "../source/board.h"
#include "../source/menu.h"
#include "../source/utils.h"
#include "../source/message.h"

#define SERVER_IP "127.0.0.1" //localhost
#define SERVER_IP "127.0.0.1" // localhost

int initialize_client(int port) {
int initialize_client(int port)
{
int sock;
struct sockaddr_in server_addr;

// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("IC: Socket creation failed");
exit(EXIT_FAILURE);
}
Expand All @@ -25,67 +28,135 @@ int initialize_client(int port) {
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);

if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
//TODO Adam: do we need this?
if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0)
{
perror("IC: Invalid address or address not supported");
exit(EXIT_FAILURE);
}

// Connect to server
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("IC: Connection failed");
exit(EXIT_FAILURE);
}

return sock;
}

void play_game(int sock) {
void play_game(int sock)
{
board b_own;
board b_enemy;

bool game_over = false;
bool my_turn = false;
Message msg;

// Initialize game boards
board_init(&b_own, 10);
board_init(&b_enemy, 10);

// Place ships
place_ships(&b_own);

// Game loop
bool requestGameEnd = false;
while (!requestGameEnd) {
// TODO Adam: Implement game loop with server communication
// Display initial board state
// board_display(&b_own, &b_enemy);
while (!game_over)
{
// receive message from server
if (recv(sock, &msg, sizeof(Message), 0) <= 0) {
perror("Play Game: Connection lost");
break;
}

switch(msg.type) {
case MSG_WAIT_PLAYER:
show_message("Waiting for other player...");
break;

case MSG_START_GAME:
clear_screen();
show_message("Game started!");
break;

case MSG_YOUR_TURN:
my_turn = true;
break;

case MSG_SHOT:
// Received shot from opponent
int hit =receive_shot(msg.x, msg.y, &b_own);

// Send result back
Message result = {
.type = MSG_RESULT,
.x = msg.x,
.y = msg.y,
.hit = hit
};
send(sock, &result, sizeof(Message), 0);
break;

case MSG_RESULT:
// Result of our shot
mark_hit(msg.x, msg.y, msg.hit, &b_enemy);
break;

case MSG_GAME_OVER:
game_over = true;
break;
}

if (my_turn) {
board_display(&b_own, &b_enemy);
char* shot = shoot(&b_enemy);
// TODO refactor?
int x = shot[0] - 'A';
int y = shot[1] - '0';
free(shot); // Add this to prevent memory leak

// Send shot
Message msg = {
.type = MSG_SHOT,
.x = x,
.y = y
};
send(sock, &msg, sizeof(Message), 0);
my_turn = false;
}
}

// Cleanup
board_destroy(&b_own);
board_destroy(&b_enemy);
}

int main(int argc, char** argv) {
int main(int argc, char **argv)
{
int mode;

// Handle menu
mode = handle_menu();
if (mode == 0) // Quit
if (mode == 0) // Quit
return 0;

clear_screen();

if (mode == 1) { // Computer game mode

if (mode == 1) // Computer game mode
{
show_message("Computer mode not yet implemented");
sleep(3);
clear_screen();
//TODO Waffle: implement
// TODO Waffle: implement
return 0;
}

int port = 8536;
if(argc >= 2)
if (argc >= 2)
port = atoi(argv[1]);

if (mode == 2) { // Human vs Human (network) mode
if (mode == 2) // Human vs Human (network) mode
{
show_message("Please wait, connecting...");
sleep(1);
int sock = initialize_client(port);
Expand Down
155 changes: 141 additions & 14 deletions server/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,43 @@
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include "../source/message.h"

#define MAX_PLAYERS 2

atomic_int player_count = 0; // Atomic variable to track connected players

typedef struct {
int socket;
int opponent_socket;
bool has_opponent;
} Player;

Player players[MAX_PLAYERS];
pthread_mutex_t players_mutex = PTHREAD_MUTEX_INITIALIZER;

int find_player_slot(int socket) {
for(int i = 0; i < MAX_PLAYERS; i++) {
if(players[i].socket == socket) {
return i;
}
}
return -1;
}

int find_empty_slot() {
for(int i = 0; i < MAX_PLAYERS; i++) {
if(players[i].socket == 0) {
return i;
}
}
return -1;
}

int initialize_server(int port)
{
int server_fd;
struct sockaddr_in address;
int opt = 1;

// Create socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
Expand Down Expand Up @@ -49,29 +76,128 @@ int initialize_server(int port)

void *handle_client(void *arg)
{
//Dereference the pointer to get the socket
int client_socket = *(int *)arg;
free(arg); // Free memory allocated for client_socket

// Register player
pthread_mutex_lock(&players_mutex);
int player_slot = find_empty_slot();
if(player_slot == -1) {
pthread_mutex_unlock(&players_mutex);
close(client_socket);
return NULL;
}

players[player_slot].socket = client_socket;
players[player_slot].has_opponent = false;

//Try to find opponent, assign opponent socket and set has_opponent to true
for(int i = 0; i < MAX_PLAYERS; i++) {
if(i != player_slot && players[i].socket != 0 && !players[i].has_opponent) {
players[player_slot].opponent_socket = players[i].socket;
players[player_slot].has_opponent = true;
players[i].opponent_socket = client_socket;
players[i].has_opponent = true;
break;
}
}
pthread_mutex_unlock(&players_mutex);

// Wait for opponent
while(!players[player_slot].has_opponent) {
Message msg = {.type = MSG_WAIT_PLAYER};
send(client_socket, &msg, sizeof(Message), 0);
sleep(1);

pthread_mutex_lock(&players_mutex);
if(atomic_load(&player_count) < 2) {
pthread_mutex_unlock(&players_mutex);
continue;
}
pthread_mutex_unlock(&players_mutex);
}

char buffer[1024] = {0};
// Start game
Message start_msg = {.type = MSG_START_GAME};
send(client_socket, &start_msg, sizeof(Message), 0);

// Wait a moment to ensure both players have received START_GAME
usleep(100000); // 100ms

// First player gets first turn, but only after both are ready
if(player_slot == 0) {
Message turn_msg = {.type = MSG_YOUR_TURN};
send(client_socket, &turn_msg, sizeof(Message), 0);
} else {
Message wait_msg = {.type = MSG_WAIT_PLAYER};
send(client_socket, &wait_msg, sizeof(Message), 0);
}

// Game loop
while(true) {
Message msg;
if(recv(client_socket, &msg, sizeof(Message), 0) <= 0) {
break;
}

// Read message from client
read(client_socket, buffer, 1024);
printf("HC: Message from client: %s\n", buffer);
pthread_mutex_lock(&players_mutex);
int opponent_socket = players[player_slot].opponent_socket;
pthread_mutex_unlock(&players_mutex);

switch(msg.type) {
case MSG_SHOT:
Message wait_msg = {.type = MSG_WAIT_PLAYER};
send(client_socket, &wait_msg, sizeof(Message), 0);

// Forward shot to opponent
send(opponent_socket, &msg, sizeof(Message), 0);
break;

case MSG_RESULT:
// Forward result to shooter
send(opponent_socket, &msg, sizeof(Message), 0);
// Send turn message to the player who just got shot at
Message turn_msg = {.type = MSG_YOUR_TURN};
send(client_socket, &turn_msg, sizeof(Message), 0);
break;
}
}

// Send response to client
const char* message = "Hello from server";
send(client_socket, message, strlen(message), 0);
printf("HC: Message sent to client\n");
pthread_mutex_lock(&players_mutex);
int opponent_socket = players[player_slot].opponent_socket;

// TODO Adam: check if player receives game over message
// Send game over to opponent if they're still connected
if(players[player_slot].has_opponent) {
Message game_over = {.type = MSG_GAME_OVER};
send(opponent_socket, &game_over, sizeof(Message), 0);
}

// Destroy player slot
players[player_slot].socket = 0;
players[player_slot].opponent_socket = 0;
players[player_slot].has_opponent = false;

// destroy opponent's data
for(int i = 0; i < MAX_PLAYERS; i++) {
if(players[i].socket == opponent_socket) {
players[i].opponent_socket = 0;
players[i].has_opponent = false;
break;
}
}
pthread_mutex_unlock(&players_mutex);

// Decrement the player count atomically
atomic_fetch_sub(&player_count, 1);

close(client_socket);
return NULL;
}

int main(int argc, char** argv)
{
// Initialize players array with 0s
memset(players, 0, sizeof(players));

int port = 8536;
if(argc >= 2)
port = atoi(argv[1]);
Expand Down Expand Up @@ -107,13 +233,14 @@ int main(int argc, char** argv)
if (pthread_create(&thread_id, NULL, handle_client, new_socket) != 0)
{
perror("Server: Thread creation failed");
atomic_fetch_sub(&player_count, 1); // Decrement count if thread creation fails
atomic_fetch_sub(&player_count, 1);
close(*new_socket);
free(new_socket);
}
else
{
pthread_detach(thread_id); // Detach the thread to avoid memory leaks
// Launch the thread but dont wait for it to finish
pthread_detach(thread_id);
}
}
else
Expand Down
Loading