Compare commits

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

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
*.o *.o
netpong netpong
netpong.dSYM/ netpong.dSYM/
netpong.c
build/*
*.DS_Store
+6 -2
View File
@@ -14,7 +14,12 @@
], ],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"environment": [], "environment": [
{
"name": "TERM",
"value": "xterm-256color"
}
],
"externalConsole": false, "externalConsole": false,
"MIMode": "lldb", "MIMode": "lldb",
"setupCommands": [ "setupCommands": [
@@ -30,6 +35,5 @@
} }
] ]
} }
] ]
} }
+2 -2
View File
@@ -9,8 +9,8 @@
"-Wall", "-Wall",
"-g", "-g",
"main.c", "main.c",
"court.c", "src/*.c",
"network/*.c", "src/network/*.c",
"-o", "-o",
"netpong", "netpong",
"-lncurses" "-lncurses"
+15 -14
View File
@@ -1,32 +1,33 @@
# Compiler and flags # Compiler and flags
CC = gcc CC = gcc
CFLAGS = -Wall -g CFLAGS = -Wall -g
NETPONG_SRC = main.c court.c $(wildcard network/*.c)
LIBS = -lncurses LIBS = -lncurses
# Source files
NETPONG_SRC = main.c $(wildcard src/*.c) $(wildcard src/network/*.c)
# Object files # Object files in build/
NETPONG_OBJ = $(NETPONG_SRC:.c=.o) NETPONG_OBJ = $(patsubst %.c,build/%.o,$(NETPONG_SRC))
# Default target # Default target
all: netpong all: netpong
# Build NetPong # Link
netpong: $(NETPONG_OBJ) netpong: $(NETPONG_OBJ)
$(CC) $(CFLAGS) -o netpong $(NETPONG_OBJ) $(LIBS) $(CC) $(CFLAGS) -o $@ $(NETPONG_OBJ) $(LIBS)
# Generic compile rule # Compile rule (FULL PATH match)
%.o: %.c smsh.h varlib.h build/%.o: %.c
$(CC) $(CFLAGS) -c $< @mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Clean up # Clean
clean: clean:
rm -f *.o netpong network/*.o rm -rf build netpong
# Run targets
Server: Server:
./netpong ./netpong 2001
Client: Client:
./netpong -c 127.0.0.1 ./netpong 127.0.0.1 2001
+29
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 descriptor–based 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 ball’s 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 client’s 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
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
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

+63
View File
@@ -0,0 +1,63 @@
#ifndef GAME_H
#define GAME_H 1
#include "network/networking.h"
#define NUM_OF_BALLS 2
// 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
+38
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
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
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
+27 -27
View File
@@ -1,22 +1,23 @@
#include "game.h" #include "include/game.h"
#include <curses.h>
#include <signal.h> #include <unistd.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <string.h>
/** /**
* Main Fucntion * Main Fucntion
*/ */
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
// Create GameData Object // Create GameData Object
struct GameData data; GameData data = { false, 0, NULL, 0 };
// Detect Server or Client // Detect Server or Client
switch (argc) { switch (argc) {
case 2: case 2:
// Start Server // Start Server
data.host = true;
data.playerNumber = 0; data.playerNumber = 0;
data.port = atoi(argv[1]); data.port = atoi(argv[1]);
break; break;
@@ -34,27 +35,26 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
srand(getpid());
// Setup Terminal
if (initscr() == NULL) {
perror("initscr failed"); if (data.host) {
exit(1); // 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
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;
}
+4 -6
View File
@@ -1,4 +1,4 @@
#include "game.h" #include "../include/game.h"
#include <curses.h> #include <curses.h>
#include <signal.h> #include <signal.h>
@@ -6,7 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
void DrawWalls(struct GameData data) void DrawWalls(GameData data)
{ {
for (int i = LEFT_COL; i <= RIGHT_COL; i++) { for (int i = LEFT_COL; i <= RIGHT_COL; i++) {
move(TOP_ROW, i); move(TOP_ROW, i);
@@ -25,9 +25,7 @@ void DrawWalls(struct GameData data)
refresh(); refresh();
} }
void DrawHeader(struct 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
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
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();
}
+105
View File
@@ -0,0 +1,105 @@
#include "../../include/network/networking.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.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
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
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
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();
}