Compare commits

...

27 Commits

Author SHA1 Message Date
a0a658ca8a Added dynamic libraries for macos 2025-07-18 14:19:19 -04:00
100dc94bd3 Add function definition to header file 2025-07-18 11:11:45 -04:00
2b911105a7 Use 'send()' or 'sendto()' depending on whether or not the socket is
already connected to a remote address
2025-07-18 11:11:21 -04:00
af242cb812 Rename function 2025-07-18 11:10:49 -04:00
a7e252acd2 Add method to return whether or not a socket is connected to a remote
address
2025-07-18 11:09:56 -04:00
5fe24bffd9 Updated file paths 2024-04-11 13:26:26 -04:00
6e0b7f8394 Updated include path, and set the type if the game mode is M_SINGLE 2024-04-11 13:26:18 -04:00
4b7880349d Moved submodule location 2024-04-11 13:15:34 -04:00
14131d8942 Updated README 2024-04-11 13:13:06 -04:00
6331d81ba3 Updated serialization include 2024-04-11 13:10:08 -04:00
fa0cadfabf Moved serialization files into submodule 2024-04-11 13:09:51 -04:00
b113098c7b Added netpong-serialization submodule 2024-04-11 13:08:58 -04:00
ab7b40f778 Updated TODO 2024-03-26 10:06:34 -04:00
50ed0b89e9 Updated TODO 2024-03-20 00:10:40 -04:00
1ab22651ae Updated TODO 2024-03-20 00:07:23 -04:00
b6439bf7d5 Free memory allocated with malloc() 2024-03-20 00:07:23 -04:00
1641cef13b Changed zip command, so that only one parent directory is included 2024-03-19 10:02:18 -05:00
f42ac94a45 Added check to release script, to check if DLL exists 2024-03-19 09:02:48 -05:00
4ff840e91e Fixed boneheaded mistake, where I set REUSEADDR for UDP instead of TCP 2024-03-18 16:03:19 -04:00
fd4ad04aeb Removed unnecessary comment 2024-03-18 16:02:57 -04:00
d842485103 Throw errno instead of errno * -1, if an error is encountered with sending or receiving 2024-03-18 13:43:09 -04:00
3bf65ab8f9 Updated TODO 2024-03-18 13:27:14 -04:00
f3dcbc3b3e Removed check_server_client() function.
It has been replaced by check_server() and check_client().
2024-03-18 13:25:51 -04:00
463dfbd3e5 IPv6 support for RecvAllNB() 2024-03-18 13:22:26 -04:00
d3716536f9 Minor changes 2024-03-18 13:22:12 -04:00
8805402241 Return after catching exception 2024-03-18 13:22:01 -04:00
3d0aeac943 Ensure that REUSEADDR flag is only applied for TCP 2024-03-18 13:21:42 -04:00
15 changed files with 148 additions and 302 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "subprojects/raylib"]
path = subprojects/raylib
url = https://github.com/raysan5/raylib.git
[submodule "subprojects/netpong-serialization"]
path = netpong-serialization
url = https://gitea.twomorecents.org/Rockingcool/netpong-serialization.git

View File

@@ -10,15 +10,23 @@ The game has only one runtime dependency: The [raylib](https://www.raylib.com/)
This application uses [Meson](https://mesonbuild.com/) as a build system. To build the application:
1. Install meson from the link above.
2. Set up the build directory.
2. Clone the repository.
3. Update all submodules:
```
git submodule update --init --recursive
```
3. Set up the build directory.
```
meson setup build
```
3. Compile the application. Meson should use a system installation of raylib, if it exists. If not, it falls back to a bundled version.
4. Compile the application. Meson should use a system installation of raylib, if it exists. If not, it falls back to a bundled version.
```
meson compile -C build
```
4. You can also create a statically-linked version of the game (with no runtime dependencies) on Linux by running the following commands:
5. You can also create a statically-linked version of the game (with no runtime dependencies) on Linux by running the following commands:
```
meson configure -Ddefault_library=static build/

View File

@@ -18,10 +18,10 @@ GameType check_server(char* ip_text, char* port_text, const int if_mode) {
/* Check if IP address and port are in valid forms */
if (check_ip_ver(ip_text) < 0) {
throw std::invalid_argument("Invalid IP address");
throw std::invalid_argument("Invalid IP address.");
}
if (port_to_num(port_text) < 0) {
throw std::invalid_argument("Invalid port");
throw std::invalid_argument("Invalid port.");
}
/* From here on, we assume that the IP and port are valid */

View File

@@ -5,6 +5,7 @@
/* Destructor - closes any open sockets */
Client::~Client() {
free(dest);
close(this->other_socket);
close(this->sock_fd);
}

View File

@@ -4,9 +4,10 @@
set -o errexit # Stop executing when a command fails
BASE_DIR=$(dirname $0)
REL_DIR="$BASE_DIR/release/dist"
REL_DIR="$BASE_DIR/release/dist/pong"
RAYLIB_DLL="$BASE_DIR/build/subprojects/raylib/libraylib.dll"
mkdir -p "$REL_DIR"
rm -r "$REL_DIR"; mkdir -p "$REL_DIR"
# Set up the build directory
meson setup build/
@@ -14,18 +15,24 @@ meson setup build/
# Build the application
meson compile -C build/
# Parse the output of the 'ldd' command, and create a file with the required DLL paths.
ldd build/pong.exe | awk ' NF == 4 {print $3}' > "$BASE_DIR/tmp_file.txt"
# Parse the output of the 'ldd' command (using only DLLs that are found) and create a file with the required DLL paths.
ldd build/pong.exe | awk ' NF == 4 {print $3}' | grep -i "dll" > "$BASE_DIR/tmp_file.txt"
# Copy the required DLLs.
cp $(cat "$BASE_DIR/tmp_file.txt") "$REL_DIR"
# Copy the raylib DLL, if it does not exist in the directory
cp -n "$RAYLIB_DLL" "$REL_DIR"
# Copy the executable itself
cp "$BASE_DIR/build/pong" "$REL_DIR"
# Remove the temporary file.
rm "$BASE_DIR/tmp_file.txt"
#Zip the $REL_DIR folder
zip -r "$BASE_DIR/release/netpong-win.zip" "$REL_DIR"
# Go to the parent directory of $REL_DIR, and zip the $REL_DIR directory. This ensures
# that the parent directories aren't included in the zip file.
# The command is enclosed in parantheses, to ensure that the main shell's directory
# isn't changed.
(cd "$REL_DIR/.." && zip -r "./netpong-win.zip" "./pong")

View File

@@ -52,10 +52,11 @@ SOCKET create_socket(int network, char transport) {
int newSock = socket(domain,type,0);
/* Set REUSEADDR flag, allowing program to be run twice */
int set_opt = 1;
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
/* Set REUSEADDR flag for TCP, allowing program to be run twice */
if (transport == ES_TCP) {
int set_opt = 1;
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
}
return newSock;
}
@@ -132,6 +133,7 @@ SOCKET create_remote (int network,char transport, const char* address,int port,s
}
remote_addr_struct = (struct sockaddr_storage *)results->ai_addr;
network = inet_to_int(results->ai_family);
free(port_str);
} else {
create_addr(network,address,port,remote_addr_struct);
}

View File

@@ -1,33 +0,0 @@
#ifndef _SERIALIZATION_H
#define _SERIALIZATION_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
/* Struct used to hold the data that will be sent between sockets */
typedef struct {
uint16_t pad_x; // X-coordinate of sending paddle
uint16_t pad_y; // Y-coordinate of sending paddle
uint16_t ball_x; // X-coordinate of ball (only the server fills this in)
uint16_t ball_y; // Y-coordinate of ball (only the server fills this in)
bool should_quit; // Flag to indicate whether game should be quit or not
} Serial_Data;
/* Create a Serial_Data struct from float values */
Serial_Data Serial_create_data(float pad_x, float pad_y, float ball_x, float ball_y, bool should_quit);
/* Serialize a struct into a byte array, that can be sent through a socket */
uint8_t* Serial_serialize(Serial_Data data);
/* Deserialize a byte array into a struct, and return the struct */
Serial_Data Serial_deserialize(uint8_t* serialized);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -56,6 +56,9 @@ public:
/* Returns socket identifier */
int getSockFD();
/* Returns whether or not the given socket is connected to a remote address */
bool has_remote_address();
/* This is a pure virtual function (AKA an abstract function). It's purpose
is to be redefined by the children classes (client and server). */
virtual int get_type() = 0;

151
main.cpp
View File

@@ -34,7 +34,7 @@
#include "includes/check_input.hpp"
#include "includes/display_text.hpp"
#include "includes/easysock.h"
#include "includes/serialization.h"
#include "netpong-serialization/includes/serialization.h"
#include "includes/timer.h"
/* Global variables used to instantiate structs */
@@ -120,109 +120,6 @@ void check_num_args(int argc, char** argv) {
return;
}
/* This function checks the command-line arguments passed to the program.
It then decides whether the game is in Server or Client mode (or neither), and
instantiates the appropriate object. The (uninitialized) objects are passed to the
function as pointers. It returns a GameType struct, that indicates whether the game
is in server, client or single player mode, and contains the appropriate socket object. */
GameType check_server_client(int argc, char** argv) {
std::string connect_code;
std::vector<std::string> addr_port; /* Vector to store (IPv4) address and port */
GameType type;
if (argc < 2) { /* Game was not started in client or server mode */
type.mode = M_SINGLE;
type.netsock = nullptr;
return type;
}
/* GAME STARTED IN CLIENT MODE */
if (strcmp(argv[1],"-C") == 0) {
if (argc < 3) { /* No address was provided */
throw EXCEPT_TOOFEWARGS;
}
connect_code = std::string(argv[2]); /* The connect code is a special string, that contains the server address and port. It is given by the server. */
try {
addr_port = connect_code::decode(connect_code);
/* Check IP address version */
if (check_ip_ver(addr_port[0].data()) < 0) {
throw std::invalid_argument("Invalid code entered.");
}
Client* client = new Client(ES_UDP, addr_port[0].data(), std::stoi(addr_port[1]));
client->create_socket();
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
client->sendAll("GG");
std::string msg_from_server = client->recvAll();
if (msg_from_server == "U2") {
std::cout << "Connection made. Waiting for server to begin game..." << std::endl;
} else {
throw EXCEPT_WRONGRESPONSE;
}
type.mode = M_CLIENT;
type.netsock = client;
return type;
} catch (int e) {
throw;
} catch (std::exception& e) {
throw;
}
}
/* GAME STARTED IN SERVER MODE */
else if (strcmp(argv[1],"-S") == 0) {
std::string addr;
uint16_t port;
/* No IP address or port specified */
if (argc < 3) {
throw EXCEPT_TOOFEWARGS;
}
/* IP address but no port */
else if (argc < 4) {
std::cout << "No port specified, using 6500..." << std::endl;
addr = std::string(argv[2]);
port = 6500;
} else {
addr = std::string(argv[2]);
port = std::stoi(std::string(argv[3]));
}
/* Check if IP is valid */
if (check_ip_ver(addr.data()) < 0) {
throw EXCEPT_INVALIDIP;
}
std::string code = connect_code::encode(addr, std::to_string(port));
std::cout << "Your code is " << code << std::endl;
/* Create server socket and wait for client to connect */
Server* server = new Server(ES_UDP, addr.data(), port);
server->create_socket();
std::cout << "Waiting for connection..." << std::endl;
std::string response = "";
char* temp_response = NULL;
/* Wait for the right client to connect. Since recvAll returns a char*, we need to create a temporary variable to check for NULL.
TODO - Check that the client actually sends 'GG'. */
do {
temp_response = server->recvAll();
} while (temp_response == NULL);
response = std::string(temp_response);
std::cout << "Connection received from " << server->get_peer_addr() << std::endl;
server->sendAll("U2");
type.mode = M_SERVER;
type.netsock = server;
return type;
}
else {
throw EXCEPT_INVALIDARGS;
}
}
int main(int argc, char** argv) {
/* Check the number and validity of command-line arguments. Invalid arguments
will throw an exception. */
@@ -244,33 +141,22 @@ int main(int argc, char** argv) {
/* Check if game was started in server or client mode, and call the appropriate function to process the arguments.
If game was started in single-player mode (i.e. with no arguments), then the user is prompted in the GUI. */
try { // I put this try-catch block outside the if-statement because the exception handling is the same for both client and server.
if (argc > 1) { // Server or client mode
if (std::string(argv[1]) == "-S") { // Server mode
type = check_server(argv[2], argv[3], IF_CLI);
}
if (std::string(argv[1]) == "-C") { // Client mode
type = check_client(argv[2], IF_CLI);
}
try { // I put this try-catch block outside the if-statement because the exception handling is the same for both client and server.
if (argc > 1) { // Server or client mode
if (std::string(argv[1]) == "-S") { // Server mode
type = check_server(argv[2], argv[3], IF_CLI);
}
if (std::string(argv[1]) == "-C") { // Client mode
type = check_client(argv[2], IF_CLI);
}
} catch (std::invalid_argument& inv) {
std::cout << inv.what() << std::endl;
} catch (int err) {
std::cout << strerror(err) << std::endl;
}
//try {
//type = check_server_client(argc, argv);
//} catch(int e) {
//else {
//std::cout << strerror(e) << std::endl;
//return -7;
//}
//} catch(std::invalid_argument& inv) {
//std::cout << inv.what() << std::endl;
//return -8;
//}
} catch (std::invalid_argument& inv) {
std::cout << inv.what() << std::endl;
return -1;
} catch (int err) {
std::cout << strerror(err) << std::endl;
return -1;
}
/* Initialize window and other variables */
SetTraceLogLevel(LOG_NONE);
@@ -329,6 +215,9 @@ int main(int argc, char** argv) {
/* Single player mode */
if (selected_item == M_SINGLE) {
type.mode = M_SINGLE;
type.netsock = NULL;
GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // Enable text wrapping so that the long text, displayed below, will be wrapped
BeginDrawing();
ClearBackground(BLACK);
@@ -426,7 +315,7 @@ int main(int argc, char** argv) {
display_and_exit_raygui(std::string(inv.what()) + "\nClosing game...", 2);
return -1;
}
free(code_text);
free(code_text);
}
}
@@ -506,6 +395,7 @@ int main(int argc, char** argv) {
/* If the response is NULL, that means it timed-out. In this case, there's no value to print */
std::cout << "NOTHING RECEIVED" << std::endl;
}
free(response_array);
}
/* Check to see if peer has quit the game */
@@ -542,6 +432,7 @@ int main(int argc, char** argv) {
/* Up */
if (IsKeyPressed(KEY_UP) && type.mode != M_CLIENT) {
pad2.velocity.y = (-1) * PADDLE_SPEED;
}
/* Stop */

View File

@@ -4,6 +4,45 @@ add_global_arguments('-std=c++11', language: ['cpp'])
compiler = meson.get_compiler('cpp')
cmake = import('cmake')
# For macOS only - define extra dependencies early
if build_machine.system() == 'darwin'
objc_dep = dependency('objc', required: false)
if not objc_dep.found()
objc_dep = compiler.find_library('objc', required: true)
endif
# Add other macOS frameworks that might be needed
foundation_dep = dependency('Foundation', required: false)
if not foundation_dep.found()
foundation_dep = compiler.find_library('Foundation', required: false)
endif
cocoa_dep = dependency('Cocoa', required: false)
if not cocoa_dep.found()
cocoa_dep = compiler.find_library('Cocoa', required: false)
endif
iokit_dep = dependency('IOKit', required: false)
if not iokit_dep.found()
iokit_dep = compiler.find_library('IOKit', required: false)
endif
extra_deps = [objc_dep]
if foundation_dep.found()
extra_deps += [foundation_dep]
endif
if cocoa_dep.found()
extra_deps += [cocoa_dep]
endif
if iokit_dep.found()
extra_deps += [iokit_dep]
endif
else
extra_deps = []
endif
# For Windows only
ws2_dep = compiler.find_library('ws2_32', required: false)
winmm = compiler.find_library('winmm', required: false)
# Handle raylib dependency based on library type
# if we are building a shared library
if get_option('default_library') == 'shared'
raylib = dependency('raylib', required: false) # Try to find dependency with pkg-config
@@ -17,24 +56,20 @@ if get_option('default_library') == 'shared'
raylib_proj = cmake.subproject('raylib', options: opt_var)
raylib = raylib_proj.dependency('raylib')
endif
else
# For static library (default case)
opt_var = cmake.subproject_options()
opt_var.add_cmake_defines({'BUILD_SHARED_LIBS' : false})
raylib_proj = cmake.subproject('raylib', options: opt_var)
raylib = raylib_proj.dependency('raylib')
endif
# I we are building a static library
if get_option('default_library') == 'static'
raylib_proj = cmake.subproject('raylib')
raylib = raylib_proj.dependency('raylib')
endif
#For Windows only
ws2_dep = compiler.find_library('ws2_32', required: false)
winmm = compiler.find_library('winmm', required: false)
if build_machine.system() == 'windows'
add_project_arguments('-Wl,--subsystem,windows', '-mwindows', language: ['cpp', 'c']) # Prevent opening console when game is run
add_project_arguments('-Wl,--subsystem,windows', '-mwindows', language: ['cpp', 'c']) # Prevent opening console when game is run
endif
executable('pong',
'main.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp', 'check_input.cpp', 'raygui_helpers.cpp', 'display_text.cpp',
'serialization.c', 'timer.c', 'easysock.c',
dependencies: [raylib, ws2_dep, winmm]
'main.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp', 'check_input.cpp', 'raygui_helpers.cpp', 'display_text.cpp',
'netpong-serialization/serialization.c', 'timer.c', 'easysock.c',
dependencies: [raylib, ws2_dep, winmm] + extra_deps
)

1
netpong-serialization Submodule

Submodule netpong-serialization added at c0c7e14aa6

View File

@@ -1,87 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#include <arpa/inet.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
#include "includes/serialization.h"
/* Takes in float values, casts them to uint16_t and creates a Serial_Data struct */
Serial_Data Serial_create_data(float pad_x, float pad_y, float ball_x, float ball_y, bool should_quit) {
Serial_Data data;
data.pad_x = (uint16_t)pad_x;
data.pad_y = (uint16_t)pad_y;
data.ball_x = (uint16_t)ball_x;
data.ball_y = (uint16_t)ball_y;
data.should_quit = should_quit;
return data;
}
/* Serializes a 'Data' struct into a byte array, converted to network-byte order */
uint8_t* Serial_serialize(Serial_Data data) {
/* Create a pointer that can fit the entire struct */
uint8_t* serialized = malloc(sizeof(Serial_Data) + 1);
uint8_t* pad_x_ptr;
uint8_t* pad_y_ptr;
uint8_t* ball_x_ptr;
uint8_t* ball_y_ptr;
uint8_t* should_quit_ptr;
memset(serialized, 0, sizeof(Serial_Data) + 1); // Zero out the memory
pad_x_ptr = serialized;
pad_y_ptr = pad_x_ptr + sizeof(uint16_t);
ball_x_ptr = pad_y_ptr + sizeof(uint16_t);
ball_y_ptr = ball_x_ptr + sizeof(uint16_t);
should_quit_ptr = ball_y_ptr + sizeof(uint16_t);
*((uint16_t *)pad_x_ptr) = data.pad_x;
*((uint16_t *)pad_x_ptr) = htons(*((uint16_t *)pad_x_ptr));
*((uint16_t *)pad_y_ptr) = data.pad_y;
*((uint16_t *)pad_y_ptr) = htons(*((uint16_t *)pad_y_ptr));
*((uint16_t *)ball_x_ptr) = data.ball_x;
*((uint16_t *)ball_x_ptr) = htons(*((uint16_t *)ball_x_ptr));
*((uint16_t *)ball_y_ptr) = data.ball_y;
*((uint16_t *)ball_y_ptr) = htons(*((uint16_t *)ball_y_ptr));
*((bool *)should_quit_ptr) = data.should_quit;
*(should_quit_ptr + sizeof(bool)) = '\0';
return serialized;
}
/* Deserialize a byte array into a 'Data' struct, converted to host byte order */
Serial_Data Serial_deserialize(uint8_t* serialized) {
Serial_Data deserialized;
/* Use successive chunks of memory address to create pointers to the data */
uint8_t* pad_x_ptr = serialized;
uint8_t* pad_y_ptr = serialized + sizeof(uint16_t);
uint8_t* ball_x_ptr = pad_y_ptr + sizeof(uint16_t);
uint8_t* ball_y_ptr = ball_x_ptr + sizeof(uint16_t);
uint8_t* should_quit_ptr = ball_y_ptr + sizeof(uint16_t);
/* Dereference (and cast) the pointers, and store them into the struct */
deserialized.pad_x = *((uint16_t *)pad_x_ptr);
deserialized.pad_x = ntohs(deserialized.pad_x);
deserialized.pad_y = *((uint16_t *)pad_y_ptr);
deserialized.pad_y = ntohs(deserialized.pad_y);
deserialized.ball_x = *((uint16_t *)ball_x_ptr);
deserialized.ball_x = ntohs(deserialized.ball_x);
deserialized.ball_y = *((uint16_t *)ball_y_ptr);
deserialized.ball_y = ntohs(deserialized.ball_y);
deserialized.should_quit = *((bool *)should_quit_ptr);
return deserialized;
}

View File

@@ -12,6 +12,7 @@
/* Destructor - closes any open sockets */
Server::~Server() {
free(dest);
close(this->other_socket);
close(this->sock_fd);
}
@@ -72,8 +73,11 @@ char* Server::recvAllNB() {
/* Convert the s_addr field of the caseted struct to host network-byte, and convert it to a dotted decimal */
peer_addr = connect_code::dec_to_dotted_dec(std::to_string(htonl(temp_struct->sin_addr.s_addr)));
} else {
/* FOR IPv6 */
peer_addr = "IPV6 NOT SUPPORTED YET";
/* FOR IPv6 - Use the inet_ntop function, and convert the struct's address into a string */
struct sockaddr_in6* temp_struct = (struct sockaddr_in6*)this->dest;
char* temp_buf = (char *)malloc(sizeof(char) * (INET6_ADDRSTRLEN + 1));
peer_addr = std::string(inet_ntop(AF_INET6, temp_struct->sin6_addr.s6_addr, temp_buf, INET6_ADDRSTRLEN));
free(temp_buf);
}
return to_return;
@@ -98,8 +102,7 @@ called immediately after the constructor. If the socket is TCP, it also sets the
socket to listen for incoming connections. This function throws an exception if
the socket could not be created. The excpetion is an integer corresponding to the errno
of the failing function, and enables the caller to print a corresponding error message by
'catching' the thrown exception and using strerror().
This function also sets a timeout of 100ms for UDP sockets */
'catching' the thrown exception and using strerror().*/
void Server::create_socket() {
Sock::create_socket();

View File

@@ -20,7 +20,6 @@ Sock::~Sock() {}
parameters. The address version (IPv4 or IPv6) is determined based on the given address. */
Sock::Sock(char protocol, const char* address, int port) {
/* Error checking */
this->ip_ver = check_ip_ver(address);
if (ip_ver != 4 && ip_ver != 6) {
@@ -33,12 +32,18 @@ Sock::Sock(char protocol, const char* address, int port) {
throw std::invalid_argument("Invalid protocol");
}
this->ip_ver = ip_ver;
this->protocol = protocol;
this->port = port;
this->address = std::string(address);
}
/* This method returns whether or not the socket is connected to a remote address */
bool Sock::has_remote_address() {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
return getpeername(this->sock_fd, (struct sockaddr*)&addr, &len) == 0;
}
/* This method sends the given data, through the 'other_sockt' variable.. Client
and server classes extend this method, by setting this variable to different values.
This function needs more testing for TCP, as it focuses on UDP right now. */
@@ -50,7 +55,15 @@ void Sock::sendAll(std::string to_send) {
/* For UDP sockets */
if (this->protocol == ES_UDP) {
sendto(this->sock_fd, to_send.data(), str_length, 0, (struct sockaddr *)dest, addrlen);
int retval;
if (this->has_remote_address()) {
retval = send(this->sock_fd, to_send.data(), str_length, 0);
} else {
retval = sendto(this->sock_fd, to_send.data(), str_length, 0, (struct sockaddr *)dest, addrlen);
}
if (retval == -1) {
throw errno;
}
}
/* For TCP sockets */
else {
@@ -58,7 +71,7 @@ void Sock::sendAll(std::string to_send) {
/* Send the data to the 'other_socket' variable, which should be set by the client and server methods */
num_bytes_sent = send(this->other_socket, to_send.substr(total_bytes_sent).c_str(), str_length - total_bytes_sent, 0);
if (num_bytes_sent < 0) {
throw errno * -1;
throw errno;
}
total_bytes_sent += num_bytes_sent;
}
@@ -89,6 +102,9 @@ char* Sock::recvAll() {
if (num_bytes_received == 0) {
return NULL;
}
if (num_bytes_received < 0) {
throw errno;
}
/* Null-terminate the string */
*(buffer + num_bytes_received) = '\0';
return buffer;
@@ -106,7 +122,7 @@ char* Sock::recvAll() {
}
if (num_bytes_received < 0) {
throw errno * -1;
throw errno;
}
total_bytes_received += num_bytes_received;
has_been_read = true;

View File

@@ -1,14 +1,10 @@
1. Sign Windows executable, to remove 'Unknown Publisher' warnings.
2. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.
3. Use free() to free allocated memory.
4. Use check_client() and check_server() for CLI invocation as well, and pass a flag that indicataes whether the parameters were entered through GUI or CLI (also probably create a function to handle printing vs. GUI display).
5. Use the struct to establish a connection, and to start each round (instead of sending strings).
6. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
7. Communicate the paddle reset position to the peer, after a round.
8. Clean up / refactor the raygui code in main.cpp, that asks user for game mode. Instead of just having a giant blob of code in main.cpp, maybe split it into a function, or move it to another file. It should be easy to split it into a different function, since none of the functions take any specific parameters. The text box function, for example, only takes in the rectangle coordinates, and the text to display. I can move the code to a function, and then pass in any parameters that I need to pass in (I don't think I need to pass many parameters, though).
9. Allow the user to specify which paddle they want to control, in multi-player mode.
10. Try to make the ball go between screens.
11. Change the networking code, so that a single server can connect two clients with each other. The server should provide player 1 with a code, and player 2 can connect with player 1 using that code (essentially like a room).
12. Add a --help option, that displays information about the game and how to run it.
13. Add better error-checking to check_server() and check_client() (Use check_server_client() as inspiration).
14. Ensure that check_server() and check_client() work properly for command-line invocation, then remove check_server_client().
1. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.
2. Use the struct to establish a connection, and to start each round (instead of sending strings).
3. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
4. Communicate the paddle reset position to the peer, after a round.
5. Clean up / refactor the raygui code in main.cpp, that asks user for game mode. Instead of just having a giant blob of code in main.cpp, maybe split it into a function, or move it to another file. It should be easy to split it into a different function, since none of the functions take any specific parameters. The text box function, for example, only takes in the rectangle coordinates, and the text to display. I can move the code to a function, and then pass in any parameters that I need to pass in (I don't think I need to pass many parameters, though).
6. Allow the user to specify which paddle they want to control, in multi-player mode.
7. Try to make the ball go between screens.
8. Change the networking code, so that a single server can connect two clients with each other. The server should provide player 1 with a code, and player 2 can connect with player 1 using that code (essentially like a room).
9. Create (or find) an icon for the application.
10. [This can't really be fixed, since I'd need to purchase a developer certificate] Sign Windows executable, to remove 'Unknown Publisher' warnings.