Compare commits

...

16 Commits

Author SHA1 Message Date
4dbe11ccf9 Change number of balls in game 2026-04-26 19:19:21 -04:00
6fbbdedebe Fix missing libraries 2026-04-26 19:17:06 -04:00
5541aeb799 ReadMe Info 2026-04-26 19:07:54 -04:00
96b1b6b848 Fix Warning 2026-04-26 19:06:54 -04:00
5c2bb282ce Fully Working Game 2026-04-26 18:39:43 -04:00
ab35c9216e Ball passing between courts 2026-04-26 16:35:09 -04:00
d79e73df8b Process Ball Transfer Packet 2026-04-26 16:34:52 -04:00
97021d0d4b Process Ball Transfer Packet 2026-04-26 16:34:41 -04:00
21324f72e6 VSCode Debugger Fix 2026-04-26 16:34:24 -04:00
049c2df51a Only one other client 2026-04-26 16:34:14 -04:00
ce6d47845e Paddle and Ball 2026-04-26 16:33:55 -04:00
745bce5ff4 Some Comments 2026-04-26 13:57:25 -04:00
44dbf7813a Better Build Script 2026-04-26 13:53:59 -04:00
8a4812973c Changing Project Structure 2026-04-26 13:48:26 -04:00
3607c4bf36 working server and gameloop 2026-04-26 13:44:54 -04:00
81488417d9 Working Server and Client 2026-04-26 12:14:31 -04:00
24 changed files with 1048 additions and 76 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
*.o
netpong
netpong.dSYM/
netpong.c
build/*
*.DS_Store

8
.vscode/launch.json vendored
View File

@@ -14,7 +14,12 @@
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"environment": [
{
"name": "TERM",
"value": "xterm-256color"
}
],
"externalConsole": false,
"MIMode": "lldb",
"setupCommands": [
@@ -30,6 +35,5 @@
}
]
}
]
}

4
.vscode/tasks.json vendored
View File

@@ -9,8 +9,8 @@
"-Wall",
"-g",
"main.c",
"court.c",
"network/*.c",
"src/*.c",
"src/network/*.c",
"-o",
"netpong",
"-lncurses"

View File

@@ -1,32 +1,33 @@
# Compiler and flags
CC = gcc
CFLAGS = -Wall -g
LIBS = -lncurses -lm
NETPONG_SRC = main.c court.c $(wildcard network/*.c)
# Source files
NETPONG_SRC = main.c $(wildcard src/*.c) $(wildcard src/network/*.c)
LIBS = -lncurses
# Object files
NETPONG_OBJ = $(NETPONG_SRC:.c=.o)
# Object files in build/
NETPONG_OBJ = $(patsubst %.c,build/%.o,$(NETPONG_SRC))
# Default target
all: netpong
# Build NetPong
# Link
netpong: $(NETPONG_OBJ)
$(CC) $(CFLAGS) -o netpong $(NETPONG_OBJ) $(LIBS)
$(CC) $(CFLAGS) -o $@ $(NETPONG_OBJ) $(LIBS)
# Generic compile rule
%.o: %.c smsh.h varlib.h
$(CC) $(CFLAGS) -c $<
# Compile rule (FULL PATH match)
build/%.o: %.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Clean up
# Clean
clean:
rm -f *.o netpong network/*.o
rm -rf build netpong
# Run targets
Server:
./netpong
./netpong 2001
Client:
./netpong -c 127.0.0.1
./netpong 127.0.0.1 2001

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# Netpong
## About
Netpong is a simple, terminal-based Pong game designed for two players. Each player runs the program on their own machine. One player starts the game in server mode, while the other connects to the host. Once connected, both players can enjoy a classic Pong match together in semi-real time.
This project is written in C and uses standard file descriptorbased sockets for network communication. It was developed to meet the requirements of a Systems Programming course at Kent State University. Netpong is intended to run in a Unix-like environment and supports gameplay over a local network or across the internet with proper port forwarding.
## The Game
![](images/Serve.png "Player serves the ball")
When the game starts, the connecting client is assigned the initial serve. After serving, that client controls the balls movement and uses their paddle to send it toward the opponent. Once the ball crosses the “net” boundary, control is transferred to the other player, and the ball is no longer displayed on the original clients screen.
![](images/Play.png "Play Ball")
This process repeats as players return the ball back and forth, with control switching each time it crosses sides. The rally continues until one player fails to return the ball.
![](images/End.png "Game Over")
When a player exhausts all opportunities to return the ball, the host determines that the game has ended and calculates the final score to declare a winner. Both applications then terminate, and a new session can be started by relaunching the program.
## Compilation
The program requires a Unix Environment as it relies on ncurses to "draw" to the terminal.
To make the program, navigate to the root of this project and just type `make`
You should now be able to run `./netpong`. Send the project or the executable to another computer to play between two machines
### Usage
```
Server: ./netpong <port>
Client: ./netpong <ip_address or domain_name> <port>
```

22
game.h
View File

@@ -1,22 +0,0 @@
#ifndef GAME_H
#define GAME_H 1
// Setup game board
#define TOP_ROW 4
#define BOT_ROW 21
#define LEFT_COL 9
#define RIGHT_COL 70
struct GameData
{
int playerNumber;
char* domain;
int port;
};
void DrawWalls(struct GameData);
void DrawHeader(struct GameData);
#endif

BIN
images/CMD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
images/Compile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
images/End.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
images/Play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
images/Serve.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

63
include/game.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef GAME_H
#define GAME_H 1
#include "network/networking.h"
#define NUM_OF_BALLS 6
// Setup game board
#define TOP_ROW 4
#define BOT_ROW 21
#define LEFT_COL 9
#define RIGHT_COL 70
typedef struct
{
int host;
int playerNumber;
char* domain;
int port;
} GameData;
long GetCurrentMS();
/**
* Setups the curses terminal environment
*/
void SetupTermWin();
/**
* Cleans up the curses terminal environment
*/
void CloseTermWin();
/**
* Creates the Game Server that is responsible for the Host's input and processing
*/
void CreateGameServer(Server* serv,GameData*gd);
/**
* Creates the Client game loop
*/
void RunGameClient(Client* c,GameData*gd);
/**
* Draws the walls based on player 1 or 2
*/
void DrawWalls(GameData);
/**
* Draws the scoreboard and header
*/
void DrawHeader(GameData* data, int* scores, int numOfBalls);
/**
* Creates text at a location on screen
*/
void MGameAlert(int y, int x, char* str);
/**
* Creates text at the top left of the screen for alerts
*/
void GameAlert(char* str);
#endif

View File

@@ -0,0 +1,38 @@
#ifndef GAME_NETWORKING_H
#define GAME_NETWORKING_H 1
#include <netinet/in.h>
#define MAX_SERVER_CLIENTS 1
typedef struct {
int server_fd;
struct sockaddr_in address;
int clients[2];
int clientID;
int client_count;
} Server;
/**
* Setup the actual socket server
*/
Server StartServer(int port);
/**
* Removes client and closes the connection
*/
void RemoveClient(Server* s, int index);
void KillServer();
typedef struct {
int client_fd;
struct sockaddr_in address;
} Client;
/**
* Creates the Client object
*/
Client StartClient(char* host, int port);
#endif

16
include/network/packet.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef GAME_PACKET_H
#define GAME_PACKET_H 1
#include "../game.h"
#include "../object.h"
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

37
include/object.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef GAME_PADDLE_H
#define GAME_PADDLE_H 1
#include "game.h"
typedef struct {
int column;
int pos;
int height;
char c;
} Paddle;
Paddle CreatePaddle(GameData* gd);
void DrawPaddle(Paddle*);
void MovePaddleUP(Paddle*);
void MovePaddleDown(Paddle*);
typedef struct {
float x;
float y;
int lastCharX;
int lastCharY;
char c;
int visible;
float vx;
float vy;
} Ball;
Ball CreateBall(GameData* gd);
void DrawBall(Ball*);
void UpdateBall(Ball* ball, Paddle* paddle);
int CheckBallLose(GameData* gd,Ball* ball);
int CheckBallCourtChange(GameData* gd,Ball* ball);
void HideBall(Ball* ball);
void ServeBall(Ball* ball, GameData* gd);
#endif

54
main.c
View File

@@ -1,22 +1,23 @@
#include "game.h"
#include <curses.h>
#include <signal.h>
#include "include/game.h"
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/**
* Main Fucntion
*/
int main(int argc, char* argv[])
{
// Create GameData Object
struct GameData data;
GameData data = { false, 0, NULL, 0 };
// Detect Server or Client
switch (argc) {
case 2:
// Start Server
data.host = true;
data.playerNumber = 0;
data.port = atoi(argv[1]);
break;
@@ -34,27 +35,26 @@ int main(int argc, char* argv[])
return 1;
}
srand(getpid());
// Setup Terminal
if (initscr() == NULL) {
perror("initscr failed");
exit(1);
if (data.host) {
// Create Server
Server s = StartServer(data.port);
printf("Server Started\n");
CreateGameServer(&s,&data);
close(s.server_fd);
} else {
printf("Connecting to %s:%i\n", data.domain, data.port);
Client c = StartClient(data.domain, data.port);
RunGameClient(&c,&data);
close(c.client_fd);
return 0;
}
noecho();
cbreak();
nodelay(stdscr, TRUE);
DrawWalls(data);
DrawHeader(data);
int socket_fd = 0;
while (1) {
int ch = getch();
if (ch == 'q')
break;
}
endwin();
}

112
src/ball.c Normal file
View File

@@ -0,0 +1,112 @@
#include "../include/object.h"
#include <curses.h>
#include <math.h>
Ball CreateBall(GameData* gd)
{
Ball b;
b.visible = false;
b.c = 'O';
b.y = TOP_ROW + 3;
b.x = RIGHT_COL - 3;
b.lastCharX = 0;
b.lastCharY = 0;
b.vx = 0;
b.vy = 0;
return b;
}
void UpdateBall(Ball* ball, Paddle* p)
{
int x = ball->x;
int y = ball->y;
float nx = ball->x + ball->vx;
float ny = ball->y + ball->vy;
if (floor(ny) == TOP_ROW || floor(ny) == BOT_ROW) {
ball->vy = -ball->vy;
ny = ball->y + ball->vy;
}
if (floor(nx) == p->column) {
if (ny >= p->pos && ny < p->pos + p->height) {
ball->vx = -ball->vx;
nx = ball->x + ball->vx;
}
}
ball->x = nx;
ball->y = ny;
int ncx = ball->x;
int ncy = ball->y;
// if x y changes then re-draw
if (ncx != x || ncy != y)
DrawBall(ball);
}
int CheckBallLose(GameData* gd, Ball* ball)
{
if (gd->host) {
if (ball->x <= LEFT_COL) {
return true;
}
} else if (ball->x >= RIGHT_COL)
return true;
return false;
}
int CheckBallCourtChange(GameData* gd, Ball* ball)
{
if (gd->host) {
if (ball->x >= RIGHT_COL) {
return true;
}
} else if (ball->x <= LEFT_COL)
return true;
return false;
}
void DrawBall(Ball* ball)
{
int x = ball->x;
int y = ball->y;
char lastChar = mvinch(ball->lastCharY, ball->lastCharX) & A_CHARTEXT;
if (lastChar == ball->c)
mvaddch(ball->lastCharY, ball->lastCharX, ' ');
mvaddch(y, x, ball->c);
ball->lastCharY = y;
ball->lastCharX = x;
refresh();
}
void HideBall(Ball* ball)
{
ball->visible = false;
char lastChar = mvinch(ball->lastCharY, ball->lastCharX) & A_CHARTEXT;
if (lastChar == ball->c)
mvaddch(ball->lastCharY, ball->lastCharX, ' ');
}
void ServeBall(Ball* ball, GameData* gd) {
ball->visible = true;
float middle = TOP_ROW + (BOT_ROW-TOP_ROW)/2;
ball->y = middle;
if (gd->host)
ball->x = RIGHT_COL-2;
else ball->x = LEFT_COL + 2;
ball->vx = -0.2;
ball->vy = 0.09;
if (!gd->host) ball->vx *= -1;
}

View File

@@ -1,4 +1,4 @@
#include "game.h"
#include "../include/game.h"
#include <curses.h>
#include <signal.h>
@@ -6,7 +6,7 @@
#include <stdlib.h>
#include <unistd.h>
void DrawWalls(struct GameData data)
void DrawWalls(GameData data)
{
for (int i = LEFT_COL; i <= RIGHT_COL; i++) {
move(TOP_ROW, i);
@@ -25,9 +25,7 @@ void DrawWalls(struct GameData data)
refresh();
}
void DrawHeader(struct GameData data)
void DrawHeader(GameData* data, int* scores, int numOfBalls)
{
move(TOP_ROW - 2, LEFT_COL);
addch('P');
addch('0' + (data.playerNumber + 1));
mvprintw(TOP_ROW-1, LEFT_COL, "SCORES: P1: %d P2: %d Balls Left: %d", scores[0],scores[1],numOfBalls);
}

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();
}

230
src/gameServer.c Normal file
View File

@@ -0,0 +1,230 @@
#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>
long GetCurrentMS()
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
long ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
return ms;
}
void CreateGameServer(Server* serv, GameData* gd)
{
Server s = *serv;
Paddle paddle = CreatePaddle(gd);
Ball ball = CreateBall(gd);
long lastBallUpdate = GetCurrentMS();
int scores[2] = { 0, 0 };
int numOfBalls = 1;
bool game_running = true;
fd_set fds;
FD_ZERO(&fds);
FD_SET(s.server_fd, &fds);
SetupTermWin();
DrawWalls(*gd);
DrawHeader(gd, scores, 0);
DrawPaddle(&paddle);
// DrawBall(&ball);
GameAlert("Waiting for Client");
int allowServe = false;
while (game_running) {
fd_set read_fds;
FD_ZERO(&read_fds);
// Add server socket
FD_SET(s.server_fd, &read_fds);
int max_fd = s.server_fd;
// Add client sockets
for (int i = 0; i < s.client_count; i++) {
int fd = s.clients[i];
FD_SET(fd, &read_fds);
if (fd > max_fd)
max_fd = fd;
}
// Small timeout (prevents 100% CPU usage)
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;
}
// New connection
if (FD_ISSET(s.server_fd, &read_fds)) {
int client_fd = accept(s.server_fd, NULL, NULL);
if (client_fd >= 0 && s.client_count < MAX_SERVER_CLIENTS) {
s.clients[s.client_count++] = client_fd;
char msg[64];
snprintf(msg, sizeof(msg), "New client connected: %d\n", client_fd);
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);
}
}
// Handle client data
for (int i = 0; i < s.client_count; i++) {
int fd = s.clients[i];
if (FD_ISSET(fd, &read_fds)) {
char buffer[1024];
int bytes = recv(fd, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
char msg[1024];
snprintf(msg, sizeof(msg), "Client %d says: %s\r", fd, buffer);
GameAlert(msg);
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
// send(fd, buffer, bytes, 0);
} else if (bytes == 0) {
char msg[64];
snprintf(msg, sizeof(msg), "Client %d disconnected\n", fd);
GameAlert(msg);
RemoveClient(&s, i);
i--; // re-check swapped client
} else {
perror("recv");
RemoveClient(&s, i);
i--;
}
}
}
char ch = getch();
if (ch == 'w')
MovePaddleUP(&paddle);
if (ch == 's')
MovePaddleDown(&paddle);
// Process Ball only if on court
if (ball.visible && s.client_count > 0) {
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(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') {
GameAlert("Quitting...");
sleep(1);
game_running = false;
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) {
// char msg[64];
// snprintf(msg, sizeof(msg), "Waiting for other player\n");
// GameAlert(msg);
// } else {
// // Process Game Data
// }
}
CloseTermWin();
}

106
src/network/networking.c Normal file
View File

@@ -0,0 +1,106 @@
#include "../../include/network/networking.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
static Server *g_server = NULL;
Server StartServer(int port) {
Server s;
g_server = &s;
s.clientID = 0;
s.client_count = 0;
// Create Socket
if ((s.server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Set Address
s.address.sin_family = AF_INET;
s.address.sin_addr.s_addr = INADDR_ANY; // listen on all interfaces
s.address.sin_port = htons(port);
signal(SIGINT, KillServer); // ctrl+c
signal(SIGTERM, KillServer);
// Bind Port
if (bind(s.server_fd, (struct sockaddr *)&s.address, sizeof(s.address)) < 0) {
perror("bind failed");
close(s.server_fd);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(s.server_fd, 3) < 0) {
perror("listen");
close(s.server_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", port);
return s;
}
void RemoveClient(Server* s, int index) {
close(s->clients[index]);
s->clients[index] = s->clients[s->client_count - 1];
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 c;
// Create socket
if ((c.client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Set address info
c.address.sin_family = AF_INET;
c.address.sin_port = htons(port);
// Convert hostname/IP to binary form
if (inet_pton(AF_INET, host, &c.address.sin_addr) <= 0) {
// If not a direct IP, try resolving hostname
struct hostent* he = gethostbyname(host);
if (he == NULL) {
perror("gethostbyname failed");
close(c.client_fd);
exit(EXIT_FAILURE);
}
memcpy(&c.address.sin_addr, he->h_addr_list[0], he->h_length);
}
// Connect to server
if (connect(c.client_fd, (struct sockaddr*)&c.address, sizeof(c.address)) < 0) {
perror("connect failed");
close(c.client_fd);
exit(EXIT_FAILURE);
}
printf("Connected to %s:%d\n", host, port);
return c;
}

110
src/network/packet.c Normal file
View File

@@ -0,0 +1,110 @@
#include "../../include/network/packet.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ProcessBallPacket(char* msg, Ball* ball, GameData* gd)
{
if (strncmp(msg, "BTRANS", 6) != 0) {
return 1;
}
int matched = sscanf(msg, "BTRANS %f %f %f",
&ball->y, &ball->vx, &ball->vy);
if (matched != 3) {
printf("Parsing failed\n");
return 1;
}
if (gd->host)
ball->x = RIGHT_COL - 1;
else
ball->x = LEFT_COL + 1;
ball->visible = 1;
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);
}

69
src/paddle.c Normal file
View File

@@ -0,0 +1,69 @@
#include "../include/object.h"
#include <curses.h>
Paddle CreatePaddle(GameData* gd)
{
Paddle p;
GameData data = *gd;
// Set Column
if (data.playerNumber == 0)
p.column = LEFT_COL + 2;
else
p.column = RIGHT_COL - 2;
// Paddle height
p.height = 5;
// Find Middle of screen;
float middle = (BOT_ROW - TOP_ROW) / 2.0f;
middle -= p.height / 2;
// Set Y value to the top of the screen
p.pos = TOP_ROW + middle;
// Set char
p.c = '#';
return p;
}
void DrawPaddle(Paddle* paddle)
{
Paddle p = *paddle;
for (int i = 0; i < p.height; ++i) {
move(p.pos + i, p.column);
addch(p.c);
}
char topchar = mvinch(p.pos - 1, p.column) & A_CHARTEXT;
if (topchar == p.c)
mvaddch(p.pos - 1, p.column, ' ');
char bottomchar = mvinch(p.pos + p.height, p.column) & A_CHARTEXT;
if (bottomchar == p.c)
mvaddch(p.pos + p.height, p.column, ' ');
move(p.pos, p.column);
refresh();
}
void MovePaddleUP(Paddle* paddle)
{
int newPos = paddle->pos - 1;
if (newPos > TOP_ROW) {
paddle->pos = newPos;
}
DrawPaddle(paddle);
}
void MovePaddleDown(Paddle* paddle)
{
int newPos = paddle->pos + 1;
if (newPos + paddle->height <= BOT_ROW) {
paddle->pos = newPos;
}
DrawPaddle(paddle);
}

38
src/term.c Normal file
View File

@@ -0,0 +1,38 @@
#include "../include/game.h"
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void SetupTermWin()
{
srand(getpid());
// Setup Terminal
if (initscr() == NULL) {
perror("initscr failed");
exit(1);
}
noecho();
cbreak();
nodelay(stdscr, TRUE);
}
void CloseTermWin()
{
endwin();
}
void GameAlert(char* str)
{
MGameAlert(0,0,str);
}
void MGameAlert(int y, int x, char* str)
{
move(y, x);
clrtoeol();
mvaddstr(y, x, str);
refresh();
}