Fully Working Game

This commit is contained in:
2026-04-26 18:39:43 -04:00
parent ab35c9216e
commit 5c2bb282ce
9 changed files with 334 additions and 119 deletions

View File

@@ -3,6 +3,8 @@
#include "network/networking.h" #include "network/networking.h"
#define NUM_OF_BALLS 2
// Setup game board // Setup game board
#define TOP_ROW 4 #define TOP_ROW 4
@@ -19,6 +21,8 @@ typedef struct
} GameData; } GameData;
long GetCurrentMS();
/** /**
* Setups the curses terminal environment * Setups the curses terminal environment
*/ */
@@ -45,7 +49,7 @@ void DrawWalls(GameData);
/** /**
* Draws the scoreboard and header * Draws the scoreboard and header
*/ */
void DrawHeader(GameData); void DrawHeader(GameData* data, int* scores, int numOfBalls);
/** /**
* Creates text at a location on screen * Creates text at a location on screen

View File

@@ -23,6 +23,8 @@ Server StartServer(int port);
*/ */
void RemoveClient(Server* s, int index); void RemoveClient(Server* s, int index);
void KillServer();
typedef struct { typedef struct {
int client_fd; int client_fd;
struct sockaddr_in address; struct sockaddr_in address;

View File

@@ -5,5 +5,12 @@
#include "../object.h" #include "../object.h"
int ProcessBallPacket(char* msg, Ball* ball, GameData* gd); int ProcessBallPacket(char* msg, Ball* ball, GameData* gd);
int ProcessAlert(char* msg);
int ProcessGMSG(char* msg);
int ProcessScore(char* msg, int* scores);
int ProcessBallLost(char* msg, int fd, GameData* gd, int* scores, int* allowSever, int numOfBalls);
int ProcessNumOfBalls(char* msg, int* numOfBalls);
void SendGameStatus(int fd, char* msg);
#endif #endif

View File

@@ -13,8 +13,8 @@ Ball CreateBall(GameData* gd)
b.lastCharX = 0; b.lastCharX = 0;
b.lastCharY = 0; b.lastCharY = 0;
b.vx = -0.2; b.vx = 0;
b.vy = 0.2; b.vy = 0;
return b; return b;
} }
@@ -108,6 +108,6 @@ void ServeBall(Ball* ball, GameData* gd) {
ball->vx = -0.2; ball->vx = -0.2;
ball->vy = 0.2; ball->vy = 0.09;
if (!gd->host) ball->vx *= -1; if (!gd->host) ball->vx *= -1;
} }

View File

@@ -25,9 +25,7 @@ void DrawWalls(GameData data)
refresh(); refresh();
} }
void DrawHeader(GameData data) void DrawHeader(GameData* data, int* scores, int numOfBalls)
{ {
move(TOP_ROW - 2, LEFT_COL); mvprintw(TOP_ROW-1, LEFT_COL, "SCORES: P1: %d P2: %d Balls Left: %d", scores[0],scores[1],numOfBalls);
addch('P');
addch('0' + (data.playerNumber + 1));
} }

140
src/gameClient.c Normal file
View File

@@ -0,0 +1,140 @@
#include "../include/game.h"
#include "../include/network/packet.h"
#include "../include/object.h"
#include <curses.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
void RunGameClient(Client* client, GameData* gd)
{
Client c = *client;
Paddle paddle = CreatePaddle(gd);
Ball ball = CreateBall(gd);
int scores[2] = { 0, 0 };
long lastBallUpdate = GetCurrentMS();
int numOfBalls = 1;
bool game_running = true;
SetupTermWin();
GameAlert("Connected to server");
MGameAlert(BOT_ROW + 1, LEFT_COL, "Press space to Serve");
int allowServe = true;
DrawWalls(*gd);
DrawPaddle(&paddle);
DrawHeader(gd, scores, numOfBalls);
SendGameStatus(c.client_fd,"Waiting for serve");
while (game_running) {
fd_set read_fds;
FD_ZERO(&read_fds);
// Watch server socket
FD_SET(c.client_fd, &read_fds);
int max_fd = c.client_fd;
// Small timeout (same idea as server)
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1000; // 1ms
int ret = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
if (ret < 0) {
perror("select");
break;
}
// Handle server messages
if (FD_ISSET(c.client_fd, &read_fds)) {
char buffer[1024];
int bytes = recv(c.client_fd, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
char msg[1024];
snprintf(msg, sizeof(msg), "Server says: %s\r", buffer);
GameAlert(buffer);
if (!ProcessBallPacket(buffer, &ball, gd)) {SendGameStatus(c.client_fd,"Ball is in other court");
MGameAlert(BOT_ROW+1,LEFT_COL,"");}
ProcessAlert(buffer);
ProcessGMSG(buffer);
if (!ProcessNumOfBalls(buffer, &numOfBalls)) {
DrawHeader(gd, scores, numOfBalls);
}
ProcessBallLost(buffer, c.client_fd, gd, scores, &allowServe, numOfBalls);
if (!ProcessScore(buffer, scores)) {
DrawHeader(gd, scores, numOfBalls);
}
} else if (bytes == 0) {
GameAlert("Server disconnected. Quitting...");
sleep(2);
break;
} else {
perror("recv");
break;
}
}
char ch = getch();
if (ch == 'w')
MovePaddleUP(&paddle);
if (ch == 's')
MovePaddleDown(&paddle);
// Process Ball only if on court
if (ball.visible) {
if (GetCurrentMS() > lastBallUpdate + 10) {
lastBallUpdate = GetCurrentMS();
UpdateBall(&ball, &paddle);
}
if (CheckBallCourtChange(gd, &ball)) {
GameAlert("Ball is in other court");
HideBall(&ball);
char msg[1024];
snprintf(msg, sizeof(msg), "BTRANS %f %f %f", ball.y, ball.vx, ball.vy);
send(c.client_fd, msg, strlen(msg), 0);
}
if (CheckBallLose(gd, &ball)) {
HideBall(&ball);
DrawWalls(*gd);
GameAlert("You lost the ball, other player serves");
char msg[1024];
snprintf(msg, sizeof(msg), "BALL_LOST Other player point");
send(c.client_fd, msg, strlen(msg), 0);
}
}
if (ch == 'q') {
GameAlert("Quitting...");
sleep(1);
game_running = false;
break;
}
if (ch == ' ' && allowServe) {
allowServe = false;
ServeBall(&ball, gd);
MGameAlert(BOT_ROW + 1, LEFT_COL, "");
SendGameStatus(c.client_fd,"Ball is in other court");
}
}
CloseTermWin();
}

View File

@@ -1,6 +1,6 @@
#include "../include/game.h" #include "../include/game.h"
#include "../include/object.h"
#include "../include/network/packet.h" #include "../include/network/packet.h"
#include "../include/object.h"
#include <curses.h> #include <curses.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@@ -26,7 +26,9 @@ void CreateGameServer(Server* serv, GameData* gd)
Ball ball = CreateBall(gd); Ball ball = CreateBall(gd);
long lastBallUpdate = GetCurrentMS(); long lastBallUpdate = GetCurrentMS();
int scores[2] = { 0, 0 };
int numOfBalls = 1;
bool game_running = true; bool game_running = true;
fd_set fds; fd_set fds;
@@ -38,6 +40,7 @@ void CreateGameServer(Server* serv, GameData* gd)
SetupTermWin(); SetupTermWin();
DrawWalls(*gd); DrawWalls(*gd);
DrawHeader(gd, scores, 0);
DrawPaddle(&paddle); DrawPaddle(&paddle);
// DrawBall(&ball); // DrawBall(&ball);
@@ -82,6 +85,17 @@ void CreateGameServer(Server* serv, GameData* gd)
char msg[64]; char msg[64];
snprintf(msg, sizeof(msg), "New client connected: %d\n", client_fd); snprintf(msg, sizeof(msg), "New client connected: %d\n", client_fd);
GameAlert(msg); GameAlert(msg);
numOfBalls = NUM_OF_BALLS;
HideBall(&ball);
for (int i = 0; i < 2; ++i)
scores[i] = 0;
DrawWalls(*gd);
DrawHeader(gd, scores, numOfBalls);
char bmsg[1024];
snprintf(bmsg, sizeof(bmsg), "NUM_OF_BALLS %d", numOfBalls);
send(s.clients[0], bmsg, strlen(bmsg), 0);
} }
} }
@@ -100,7 +114,22 @@ void CreateGameServer(Server* serv, GameData* gd)
snprintf(msg, sizeof(msg), "Client %d says: %s\r", fd, buffer); snprintf(msg, sizeof(msg), "Client %d says: %s\r", fd, buffer);
GameAlert(msg); GameAlert(msg);
ProcessBallPacket(buffer,&ball,gd); if (!ProcessBallPacket(buffer, &ball, gd)) {
SendGameStatus(s.clients[0], "Ball is in other court");
MGameAlert(BOT_ROW + 1, LEFT_COL, "");
}
ProcessAlert(buffer);
ProcessGMSG(buffer);
if (!ProcessBallLost(buffer, s.clients[0], gd, scores, &allowServe, numOfBalls)) {
numOfBalls--;
DrawHeader(gd, scores, numOfBalls);
char bmsg[1024];
snprintf(bmsg, sizeof(bmsg), "NUM_OF_BALLS %d", numOfBalls);
send(s.clients[0], bmsg, strlen(bmsg), 0);
}
if (!ProcessScore(buffer, scores)) {
DrawHeader(gd, scores, numOfBalls);
}
// Example: echo back // Example: echo back
// send(fd, buffer, bytes, 0); // send(fd, buffer, bytes, 0);
@@ -139,6 +168,40 @@ void CreateGameServer(Server* serv, GameData* gd)
snprintf(msg, sizeof(msg), "BTRANS %f %f %f", ball.y, ball.vx, ball.vy); snprintf(msg, sizeof(msg), "BTRANS %f %f %f", ball.y, ball.vx, ball.vy);
send(s.clients[0], msg, strlen(msg), 0); send(s.clients[0], msg, strlen(msg), 0);
} }
if (CheckBallLose(gd, &ball)) {
HideBall(&ball);
DrawWalls(*gd);
numOfBalls--;
DrawHeader(gd, scores, numOfBalls);
GameAlert("You lost the ball, other player serves");
char bmsg[1024];
snprintf(bmsg, sizeof(bmsg), "NUM_OF_BALLS %d", numOfBalls);
send(s.clients[0], bmsg, strlen(bmsg), 0);
sleep(1);
char msg[1024];
snprintf(msg, sizeof(msg), "BALL_LOST Other player point");
send(s.clients[0], msg, strlen(msg), 0);
}
}
if (numOfBalls == 0) {
sleep(1);
if (scores[0] == scores[1]) {
MGameAlert(BOT_ROW + 1, LEFT_COL, "Game over, it's a tie");
SendGameStatus(s.clients[0], "Game over, it's a tie");
}
if (scores[0] > scores[1]) {
MGameAlert(BOT_ROW + 1, LEFT_COL, "Game over, you win!");
SendGameStatus(s.clients[0], "Game over, you lost :(");
}
if (scores[0] < scores[1]) {
MGameAlert(BOT_ROW + 1, LEFT_COL, "Game over, you lost :(");
SendGameStatus(s.clients[0], "Game over, you win!");
}
sleep(3);
ch = 'q';
} }
if (ch == 'q') { if (ch == 'q') {
@@ -148,6 +211,13 @@ void CreateGameServer(Server* serv, GameData* gd)
break; break;
} }
if (ch == ' ' && allowServe) {
allowServe = false;
ServeBall(&ball, gd);
MGameAlert(BOT_ROW + 1, LEFT_COL, "");
SendGameStatus(s.clients[0], "Ball is in other court");
}
// if (s.client_count < 1) { // if (s.client_count < 1) {
// char msg[64]; // char msg[64];
// snprintf(msg, sizeof(msg), "Waiting for other player\n"); // snprintf(msg, sizeof(msg), "Waiting for other player\n");
@@ -160,106 +230,3 @@ void CreateGameServer(Server* serv, GameData* gd)
CloseTermWin(); CloseTermWin();
} }
void RunGameClient(Client* client, GameData* gd)
{
Client c = *client;
Paddle paddle = CreatePaddle(gd);
Ball ball = CreateBall(gd);
long lastBallUpdate = GetCurrentMS();
bool game_running = true;
SetupTermWin();
GameAlert("Connected to server");
int allowServe = true;
DrawWalls(*gd);
DrawPaddle(&paddle);
while (game_running) {
fd_set read_fds;
FD_ZERO(&read_fds);
// Watch server socket
FD_SET(c.client_fd, &read_fds);
int max_fd = c.client_fd;
// Small timeout (same idea as server)
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1000; // 1ms
int ret = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
if (ret < 0) {
perror("select");
break;
}
// Handle server messages
if (FD_ISSET(c.client_fd, &read_fds)) {
char buffer[1024];
int bytes = recv(c.client_fd, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
char msg[1024];
snprintf(msg, sizeof(msg), "Server says: %s\r", buffer);
GameAlert(msg);
ProcessBallPacket(buffer,&ball,gd);
} else if (bytes == 0) {
GameAlert("Server disconnected");
break;
} else {
perror("recv");
break;
}
}
char ch = getch();
if (ch == 'w')
MovePaddleUP(&paddle);
if (ch == 's')
MovePaddleDown(&paddle);
// Process Ball only if on court
if (ball.visible) {
if (GetCurrentMS() > lastBallUpdate + 10) {
lastBallUpdate = GetCurrentMS();
UpdateBall(&ball, &paddle);
}
if (CheckBallCourtChange(gd,&ball)) {
GameAlert("Ball is in other court");
HideBall(&ball);
char msg[1024];
snprintf(msg, sizeof(msg), "BTRANS %f %f %f", ball.y,ball.vx,ball.vy);
send(c.client_fd, msg, strlen(msg), 0);
}
}
if (ch == 'q') {
GameAlert("Quitting...");
sleep(1);
game_running = false;
break;
}
if (ch == ' ') {
allowServe = false;
ServeBall(&ball,gd);
}
}
CloseTermWin();
}

View File

@@ -6,8 +6,12 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
static Server *g_server = NULL;
Server StartServer(int port) { Server StartServer(int port) {
Server s; Server s;
g_server = &s;
s.clientID = 0; s.clientID = 0;
s.client_count = 0; s.client_count = 0;
@@ -23,6 +27,9 @@ Server StartServer(int port) {
s.address.sin_addr.s_addr = INADDR_ANY; // listen on all interfaces s.address.sin_addr.s_addr = INADDR_ANY; // listen on all interfaces
s.address.sin_port = htons(port); s.address.sin_port = htons(port);
signal(SIGINT, KillServer); // ctrl+c
signal(SIGTERM, KillServer);
// Bind Port // Bind Port
if (bind(s.server_fd, (struct sockaddr *)&s.address, sizeof(s.address)) < 0) { if (bind(s.server_fd, (struct sockaddr *)&s.address, sizeof(s.address)) < 0) {
perror("bind failed"); perror("bind failed");
@@ -48,6 +55,15 @@ void RemoveClient(Server* s, int index) {
s->client_count--; s->client_count--;
} }
void KillServer() {
close(g_server->server_fd);
int c = g_server->client_count;
for (int i = 0; i < c; ++i) {
RemoveClient(g_server,i);
}
}
Client StartClient(char* host, int port) { Client StartClient(char* host, int port) {
Client c; Client c;

View File

@@ -5,10 +5,7 @@
int ProcessBallPacket(char* msg, Ball* ball, GameData* gd) int ProcessBallPacket(char* msg, Ball* ball, GameData* gd)
{ {
// 1. Verify prefix
if (strncmp(msg, "BTRANS", 6) != 0) { if (strncmp(msg, "BTRANS", 6) != 0) {
printf("Invalid prefix\n");
printf("\nMSG: %s\n\n\n", msg);
return 1; return 1;
} }
@@ -27,3 +24,87 @@ int ProcessBallPacket(char* msg, Ball* ball, GameData* gd)
return 0; return 0;
} }
int ProcessAlert(char* msg)
{
if (strncmp(msg, "ALERT:", 6) != 0) {
return 1;
}
char* alertText = msg + 6;
if (*alertText == ' ') {
alertText++;
}
GameAlert(alertText);
return 0;
}
int ProcessGMSG(char* msg)
{
if (strncmp(msg, "GMSG:", 5) != 0) {
return 1;
}
char* alertText = msg + 5;
if (*alertText == ' ') {
alertText++;
}
MGameAlert(BOT_ROW+1,LEFT_COL,alertText);
return 0;
}
int ProcessScore(char* msg, int* scores)
{
if (strncmp(msg, "SCORE", 5) != 0) {
return 1;
}
int matched = sscanf(msg, "SCORE %d %d", &scores[0], &scores[1]);
if (matched != 2) {
printf("Parsing failed");
return 1;
}
return 0;
}
int ProcessBallLost(char* msg, int fd, GameData* gd, int* scores, int* allowserve, int numOfBalls)
{
if (strncmp(msg, "BALL_LOST", 9) != 0) {
return 1;
}
scores[!gd->host]++;
*allowserve = 1;
char rmsg[1024];
snprintf(rmsg, sizeof(rmsg), "SCORE %d %d", scores[0], scores[1]);
send(fd, rmsg, strlen(rmsg), 0);
DrawHeader(gd, scores,numOfBalls);
GameAlert("You scored a point!");
MGameAlert(BOT_ROW + 1, LEFT_COL, "Press space to Serve");
return 0;
}
int ProcessNumOfBalls(char* msg, int* numOfBalls) {
if (strncmp(msg, "NUM_OF_BALLS", 12) != 0) {
return 1;
}
int matched = sscanf(msg, "NUM_OF_BALLS %d", numOfBalls);
if (matched != 1) {
printf("Parsing failed");
return 1;
}
return 0;
}
void SendGameStatus(int fd, char* msg) {
char rmsg[1024];
snprintf(rmsg, sizeof(rmsg), "GMSG:%s", msg);
send(fd, rmsg, strlen(rmsg), 0);
}