Compare commits
16 Commits
v0.2
...
100dc94bd3
Author | SHA1 | Date | |
---|---|---|---|
100dc94bd3 | |||
2b911105a7 | |||
af242cb812 | |||
a7e252acd2 | |||
5fe24bffd9 | |||
6e0b7f8394 | |||
4b7880349d | |||
14131d8942 | |||
6331d81ba3 | |||
fa0cadfabf | |||
b113098c7b | |||
ab7b40f778 | |||
50ed0b89e9 | |||
1ab22651ae | |||
b6439bf7d5 | |||
1641cef13b |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||||
|
14
README.md
14
README.md
@@ -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/
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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")
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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
|
|
@@ -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;
|
||||||
|
9
main.cpp
9
main.cpp
@@ -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 */
|
||||||
|
@@ -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
1
netpong-serialization
Submodule
Submodule netpong-serialization added at c0c7e14aa6
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
15
sock.cpp
15
sock.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
todo.txt
20
todo.txt
@@ -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.
|
||||||
|
Reference in New Issue
Block a user