Compare commits

16 Commits

Author SHA1 Message Date
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
14 changed files with 60 additions and 141 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "subprojects/raylib"] [submodule "subprojects/raylib"]
path = subprojects/raylib path = subprojects/raylib
url = https://github.com/raysan5/raylib.git 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: This application uses [Meson](https://mesonbuild.com/) as a build system. To build the application:
1. Install meson from the link above. 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 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 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/ meson configure -Ddefault_library=static build/

View File

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

View File

@@ -4,10 +4,10 @@
set -o errexit # Stop executing when a command fails set -o errexit # Stop executing when a command fails
BASE_DIR=$(dirname $0) 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" 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 # Set up the build directory
meson setup build/ meson setup build/
@@ -30,6 +30,9 @@ cp "$BASE_DIR/build/pong" "$REL_DIR"
# Remove the temporary file. # Remove the temporary file.
rm "$BASE_DIR/tmp_file.txt" rm "$BASE_DIR/tmp_file.txt"
#Zip the $REL_DIR folder # Go to the parent directory of $REL_DIR, and zip the $REL_DIR directory. This ensures
zip -r "$BASE_DIR/release/netpong-win.zip" "$REL_DIR" # 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

@@ -133,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; remote_addr_struct = (struct sockaddr_storage *)results->ai_addr;
network = inet_to_int(results->ai_family); network = inet_to_int(results->ai_family);
free(port_str);
} else { } else {
create_addr(network,address,port,remote_addr_struct); 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 */ /* Returns socket identifier */
int getSockFD(); 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 /* This is a pure virtual function (AKA an abstract function). It's purpose
is to be redefined by the children classes (client and server). */ is to be redefined by the children classes (client and server). */
virtual int get_type() = 0; virtual int get_type() = 0;

View File

@@ -34,7 +34,7 @@
#include "includes/check_input.hpp" #include "includes/check_input.hpp"
#include "includes/display_text.hpp" #include "includes/display_text.hpp"
#include "includes/easysock.h" #include "includes/easysock.h"
#include "includes/serialization.h" #include "netpong-serialization/includes/serialization.h"
#include "includes/timer.h" #include "includes/timer.h"
/* Global variables used to instantiate structs */ /* Global variables used to instantiate structs */
@@ -215,6 +215,9 @@ int main(int argc, char** argv) {
/* Single player mode */ /* Single player mode */
if (selected_item == M_SINGLE) { 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 GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // Enable text wrapping so that the long text, displayed below, will be wrapped
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK); ClearBackground(BLACK);
@@ -312,7 +315,7 @@ int main(int argc, char** argv) {
display_and_exit_raygui(std::string(inv.what()) + "\nClosing game...", 2); display_and_exit_raygui(std::string(inv.what()) + "\nClosing game...", 2);
return -1; return -1;
} }
free(code_text); free(code_text);
} }
} }
@@ -392,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 */ /* 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; std::cout << "NOTHING RECEIVED" << std::endl;
} }
free(response_array);
} }
/* Check to see if peer has quit the game */ /* Check to see if peer has quit the game */
@@ -428,6 +432,7 @@ int main(int argc, char** argv) {
/* Up */ /* Up */
if (IsKeyPressed(KEY_UP) && type.mode != M_CLIENT) { if (IsKeyPressed(KEY_UP) && type.mode != M_CLIENT) {
pad2.velocity.y = (-1) * PADDLE_SPEED; pad2.velocity.y = (-1) * PADDLE_SPEED;
} }
/* Stop */ /* Stop */

View File

@@ -35,6 +35,6 @@ endif
executable('pong', 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', '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', 'netpong-serialization/serialization.c', 'timer.c', 'easysock.c',
dependencies: [raylib, ws2_dep, winmm] dependencies: [raylib, ws2_dep, winmm]
) )

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 */ /* Destructor - closes any open sockets */
Server::~Server() { Server::~Server() {
free(dest);
close(this->other_socket); close(this->other_socket);
close(this->sock_fd); close(this->sock_fd);
} }

View File

@@ -37,6 +37,13 @@ Sock::Sock(char protocol, const char* address, int port) {
this->address = std::string(address); 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 /* 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. 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. */ This function needs more testing for TCP, as it focuses on UDP right now. */
@@ -48,7 +55,13 @@ void Sock::sendAll(std::string to_send) {
/* For UDP sockets */ /* For UDP sockets */
if (this->protocol == ES_UDP) { if (this->protocol == ES_UDP) {
if (sendto(this->sock_fd, to_send.data(), str_length, 0, (struct sockaddr *)dest, addrlen) == -1) { 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; throw errno;
} }
} }

View File

@@ -1,10 +1,10 @@
1. Sign Windows executable, to remove 'Unknown Publisher' warnings. 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. 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. Use free() to free allocated memory. 3. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
4. Use the struct to establish a connection, and to start each round (instead of sending strings). 4. Communicate the paddle reset position to the peer, after a round.
5. Figure out how to build statically-linked Mac binary, and create a build script for packaging it. 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. Communicate the paddle reset position to the peer, after a round. 6. Allow the user to specify which paddle they want to control, in multi-player mode.
7. 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). 7. Try to make the ball go between screens.
8. Allow the user to specify which paddle they want to control, in multi-player mode. 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. Try to make the ball go between screens. 9. Create (or find) an icon for the application.
10. 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). 10. [This can't really be fixed, since I'd need to purchase a developer certificate] Sign Windows executable, to remove 'Unknown Publisher' warnings.