Compare commits

...

7 Commits

Author SHA1 Message Date
352d3f26f1 Moved struct definition to separate file, and added check for displaying GUI
I moved the GameType struct (and the Mode enum) to a separate file, as I will need
to use it in the check_server and check_client functions as well. I also added the
signum function (which was previously in sign.hpp) to this file, since it was the only
function in sign.hpp. Finally, I added a check, that will only display the GUI, if the
user didn't provide any command-line arguments.
2024-03-08 14:46:30 -05:00
788b334e7c Removed sign file, since it only contained one function. This function has been moved to main.cpp 2024-03-08 14:46:26 -05:00
9de9353936 Added include guards to timer header file 2024-03-08 14:45:18 -05:00
a3392308c4 Updated TODO 2024-03-08 14:44:53 -05:00
0e9088beb6 Added comments, and added a way to use the bundled raylib, even if we are building a dynamically linked version 2024-03-08 14:44:17 -05:00
7812611fe6 Created an implementation and header file to check the user input, if it is entered through the GUI 2024-03-08 14:43:45 -05:00
bc0d644399 Replaced compound literal initialization of 'Rectangle' and 'Vector2' types (which is only valid in C), with braced-initialization (valid in C++) 2024-03-08 08:20:52 -05:00
7 changed files with 192 additions and 110 deletions

57
check_input.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include <iostream>
#include "includes/easysock.hpp"
#include "includes/connect_code.hpp"
#include "includes/server.hpp"
#include "includes/client.hpp"
#include "includes/check_input.hpp"
#include "includes/raygui/raygui.h"
#include "includes/exception_consts.hpp"
/* Display the given text, centered on the screen, as a label */
void display_text_centered(std::string to_disp) {
const char* to_disp_cstr = to_disp.c_str();
Vector2 label_size = MeasureTextEx(GetFontDefault(), to_disp_cstr, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING));
BeginDrawing();
ClearBackground(BLACK);
GuiLabel(Rectangle{(GetScreenWidth()/2) - (label_size.x/2), (GetScreenHeight()/2) - (label_size.y/2), label_size.x, label_size.y}, to_disp_cstr);
EndDrawing();
return;
}
GameType check_server(char* ip_text, char* port_text) {
GameType type;
std::string addr;
uint16_t port;
addr = std::string(ip_text);
port = std::stoi(std::string(port_text));
/* 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));
display_text_centered("Your code is " + code);
/* Create server socket and wait for client to connect */
Server* server = new Server(4, 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. */
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;
}

27
includes/check_input.hpp Normal file
View File

@@ -0,0 +1,27 @@
#ifndef _CHECK_INPUT_H
#define _CHECK_INPUT_H
#include "includes/sock.hpp"
typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode;
/* This struct contains a Mode enum, which indicates the type of game we are
playing (Single player, client mode or server mode). The netsock parameter is
a 'Sock' object - Client and Server classes inherit from this object, so this
parameter can be instantiated to either a client or server, depending on the
game type. */
typedef struct {
Mode mode;
Sock* netsock;
} GameType;
/* This function checks the IP address and port passed to it, and returns a struct,
that contains information about the game mode, and contains the server socket.
TODO - Add better error checking. */
GameType check_server(char* ip_text, char* port_text);
/* NOT IMPLEMENTED YET - This function checks the code given to it, and returns a struct
that contains information about the game mode, and contains the client socket. */
GameType check_client(char* code);
#endif

View File

@@ -1,8 +0,0 @@
#ifndef _MATH_HELP
#define _MATH_HELP
int signum(int num) {
int retval = 0;
(num > 0) ? retval = 1 : retval = -1;
return retval;
}
#endif

View File

@@ -1,7 +1,8 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifndef _TIMER_H
#define _TIMER_H
/* This file defines a simple timer struct, and methods to initialize it, /* This file defines a simple timer struct, and methods to initialize it,
and keep track of time elapsed. and keep track of time elapsed.
It was copied from https://github.com/raysan5/raylib/wiki/Frequently-Asked-Questions#how-do-i-make-a-timer */ It was copied from https://github.com/raysan5/raylib/wiki/Frequently-Asked-Questions#how-do-i-make-a-timer */
@@ -30,6 +31,7 @@ double timer_get_elapsed(Timer timer)
return GetTime() - timer.start_time; return GetTime() - timer.start_time;
} }
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -23,15 +23,14 @@
#define RAYGUI_IMPLEMENTATION #define RAYGUI_IMPLEMENTATION
#include "includes/raygui/raygui.h" #include "includes/raygui/raygui.h"
#include "includes/raygui/style_dark.h" #include "includes/raygui/style_dark.h"
#include "includes/paddle.hpp" #include "includes/paddle.hpp"
#include "includes/ball.hpp" #include "includes/ball.hpp"
#include "includes/easysock.hpp"
#include "includes/sign.hpp"
#include "includes/connect_code.hpp" #include "includes/connect_code.hpp"
#include "includes/easysock.hpp"
#include "includes/client.hpp" #include "includes/client.hpp"
#include "includes/server.hpp" #include "includes/server.hpp"
#include "includes/exception_consts.hpp" #include "includes/exception_consts.hpp"
#include "includes/check_input.hpp"
#include "includes/serialization.h" #include "includes/serialization.h"
#include "includes/timer.h" #include "includes/timer.h"
@@ -47,18 +46,12 @@ const float BASE_BOUNCE_RAD = (BASE_BOUNCE_DEG / 180.0) * M_PI;
const float BASE_SPEED_COMPONENTS = 15; const float BASE_SPEED_COMPONENTS = 15;
const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2); const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2);
typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode; /* Simple function to return 1 if a value is positive, and -1 if it is negative */
int signum(int num) {
/* This struct contains a Mode enum, which indicates the type of game we are int retval = 0;
playing (Single player, client mode or server mode). The netsock parameter is (num > 0) ? retval = 1 : retval = -1;
a 'Sock' object - Client and Server classes inherit from this object, so this return retval;
parameter can be instantiated to either a client or server, depending on the }
game type. */
typedef struct {
Mode mode;
Sock* netsock;
} GameType;
raylib::Vector2 changeVelocityAfterCollision(Paddle paddle, Ball ball) { raylib::Vector2 changeVelocityAfterCollision(Paddle paddle, Ball ball) {
float paddle_mid_y = (paddle.getRect().y + paddle.getRect().GetHeight()) / 2.0; /* Middle y value of rectangle */ float paddle_mid_y = (paddle.getRect().y + paddle.getRect().GetHeight()) / 2.0; /* Middle y value of rectangle */
@@ -223,8 +216,8 @@ int main(int argc, char** argv) {
bool game_started = false; bool game_started = false;
srand(std::time(NULL)); srand(std::time(NULL));
/* If there were no command-line arguments, the user is prompted in the GUI */
if (argc == 1) {
/* Display a drop-down menu, to allow user to pick between Single player, server and client. This section of the code uses the raygui library, and is written in C. */ /* Display a drop-down menu, to allow user to pick between Single player, server and client. This section of the code uses the raygui library, and is written in C. */
GuiLoadStyleDark(); // Load the dark theme style GuiLoadStyleDark(); // Load the dark theme style
@@ -236,27 +229,27 @@ int main(int argc, char** argv) {
/* Set variables to position objects on screen */ /* Set variables to position objects on screen */
int selected_item = 0; // variable to hold the index of the selected item int selected_item = 0; // variable to hold the index of the selected item
char* text_to_display = "Select Game Mode"; // Text to display const char* text_to_display = "Select Game Mode"; // Text to display
/* Size of the label, drop down box and button */ /* Size of the label, drop down box and button */
Vector2 label_size = MeasureTextEx(GetFontDefault(), text_to_display, font_size, font_spacing); // Set the size based on the width of the string to print, the font size and the text spacing. I added 1 to font_size and font_spacing, to account for any possible rounding errors, since the function expects floats. Vector2 label_size = MeasureTextEx(GetFontDefault(), text_to_display, font_size, font_spacing); // Set the size based on the width of the string to print, the font size and the text spacing. I added 1 to font_size and font_spacing, to account for any possible rounding errors, since the function expects floats.
Vector2 box_size = (Vector2){label_size.x, HEIGHT / 20}; Vector2 box_size = Vector2{label_size.x, HEIGHT / 20};
bool is_being_edited = false; // Indicates whether the drop-down menu is being 'edited' i.e. whether an option is being selected bool is_being_edited = false; // Indicates whether the drop-down menu is being 'edited' i.e. whether an option is being selected
bool button_pressed = false; // Indicates whether the submit button has been pressed bool button_pressed = false; // Indicates whether the submit button has been pressed
while (button_pressed == false) { while (button_pressed == false) {
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK); ClearBackground(BLACK);
GuiLabel((Rectangle){(WIDTH/2)-(label_size.x/2), (HEIGHT/8), label_size.x, label_size.y}, text_to_display); // Label to display text on top GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/8), label_size.x, label_size.y}, text_to_display); // Label to display text on top
if (is_being_edited) { if (is_being_edited) {
GuiLock(); // If the drop-down menu is being 'edited', we need to prevent the user from modifying any other aspect of the UI GuiLock(); // If the drop-down menu is being 'edited', we need to prevent the user from modifying any other aspect of the UI
} }
/* Button that allows user to proceed */ /* Button that allows user to proceed */
button_pressed = GuiButton((Rectangle){(WIDTH/2)-(box_size.x/2), (HEIGHT/2) + (HEIGHT/8), box_size.x, box_size.y}, "Continue"); button_pressed = GuiButton(Rectangle{(WIDTH/2)-(box_size.x/2), (HEIGHT/2) + (HEIGHT/8), box_size.x, box_size.y}, "Continue");
/* Drop-down menu, that allows user to select game mode */ /* Drop-down menu, that allows user to select game mode */
if (GuiDropdownBox((Rectangle){(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/8), box_size.x, box_size.y}, "SINGLE;CLIENT;SERVER", &selected_item, is_being_edited)) { // This function returns != 0 if there was a mouse click inside the dropdown area if (GuiDropdownBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/8), box_size.x, box_size.y}, "SINGLE;CLIENT;SERVER", &selected_item, is_being_edited)) { // This function returns != 0 if there was a mouse click inside the dropdown area
is_being_edited = !is_being_edited; // If the dropdown menu was selected, then it is being edited (or not being edited, if it previously was). is_being_edited = !is_being_edited; // If the dropdown menu was selected, then it is being edited (or not being edited, if it previously was).
} }
@@ -269,7 +262,7 @@ int main(int argc, char** argv) {
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);
GuiLabel((Rectangle){(WIDTH/2)-(WIDTH/8), (HEIGHT/2)-(HEIGHT/8), WIDTH/4, HEIGHT/4}, "W and S control left paddle, Up and Down arrow keys control right paddle. Good luck!"); GuiLabel(Rectangle{(WIDTH/2)-(WIDTH/8), (HEIGHT/2)-(HEIGHT/8), WIDTH/4, HEIGHT/4}, "W and S control left paddle, Up and Down arrow keys control right paddle. Good luck!");
EndDrawing(); EndDrawing();
Timer timer = timer_init(5); Timer timer = timer_init(5);
while (!timer_done(timer)); while (!timer_done(timer));
@@ -280,8 +273,8 @@ int main(int argc, char** argv) {
button_pressed = false; // Whether submit button is pressed button_pressed = false; // Whether submit button is pressed
char* ip_text = (char *)calloc(100, sizeof(char)); // Holds input of IP text box char* ip_text = (char *)calloc(100, sizeof(char)); // Holds input of IP text box
char* port_text = (char *)calloc(20, sizeof(char)); // Holds input of port text box char* port_text = (char *)calloc(20, sizeof(char)); // Holds input of port text box
char* ip_label = "Local IP address"; const char* ip_label = "Local IP address";
char* port_label = "Port number (1024 - 65535)"; const char* port_label = "Port number (1024 - 65535)";
int port_label_x_size = MeasureTextEx(GetFontDefault(), port_label, font_size, font_spacing).x; // Custom size for port label, because it's long int port_label_x_size = MeasureTextEx(GetFontDefault(), port_label, font_size, font_spacing).x; // Custom size for port label, because it's long
bool editing_ip = false; // Indicates whether the IP address text box is being edited bool editing_ip = false; // Indicates whether the IP address text box is being edited
bool editing_port = false; // Indicates whether the port text box is being edited bool editing_port = false; // Indicates whether the port text box is being edited
@@ -289,25 +282,30 @@ int main(int argc, char** argv) {
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK); ClearBackground(BLACK);
/* Label and text box for IP address */ /* Label and text box for IP address */
GuiLabel((Rectangle){(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - (HEIGHT/6) - label_size.y - 10, label_size.x, label_size.y}, ip_label); // Label to display text on top GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - (HEIGHT/6) - label_size.y - 10, label_size.x, label_size.y}, ip_label); // Label to display text on top
/* The reason this if statement exists, is largely the same as the reasoning for the drop-down menu. We want to make the text box editable /* The reason this if statement exists, is largely the same as the reasoning for the drop-down menu. We want to make the text box editable
if it has been clicked. If it is already editable, we want to make it read-only if the user clicks outside the box. This functionality if it has been clicked. If it is already editable, we want to make it read-only if the user clicks outside the box. This functionality
is mostly handled in the GuiTextBox function. If the text box is in edit mode, this function returns nonzero if the user clicks INSIDE is mostly handled in the GuiTextBox function. If the text box is in edit mode, this function returns nonzero if the user clicks INSIDE
the box. If the text box is in editable mode, this function returns nonzero if the user clicks OUTSIDE the box. */ the box. If the text box is in editable mode, this function returns nonzero if the user clicks OUTSIDE the box. */
if (GuiTextBox((Rectangle){(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/6), box_size.x, box_size.y}, ip_text, 100, editing_ip)) { if (GuiTextBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/6), box_size.x, box_size.y}, ip_text, 100, editing_ip)) {
editing_ip = !editing_ip; editing_ip = !editing_ip;
} }
/* Label and text box for port. See above for explanation of if statement. */ /* Label and text box for port. See above for explanation of if statement. */
GuiLabel((Rectangle){(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - label_size.y, port_label_x_size }, port_label); // Label to display text on top GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - label_size.y, port_label_x_size }, port_label); // Label to display text on top
if (GuiTextBox((Rectangle){(WIDTH/2) - (box_size.x/2), (HEIGHT/2), box_size.x, box_size.y}, port_text, 100, editing_port)) { if (GuiTextBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2), box_size.x, box_size.y}, port_text, 100, editing_port)) {
editing_port = !editing_port; editing_port = !editing_port;
} }
button_pressed = GuiButton((Rectangle){(WIDTH/2) - (box_size.x/2), (HEIGHT/2) + (HEIGHT/6), box_size.x, box_size.y}, "Start Server"); button_pressed = GuiButton(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) + (HEIGHT/6), box_size.x, box_size.y}, "Start Server");
EndDrawing(); EndDrawing();
} }
type = check_server(ip_text, port_text);
free(ip_text);
free(port_text);
}
} }
/* Variable to store the response given by the other player */ /* Variable to store the response given by the other player */

View File

@@ -3,19 +3,23 @@ add_global_arguments('-g', '-Wall', '-pedantic', '-Wno-unused-function', '-Wno-n
compiler = meson.get_compiler('cpp') compiler = meson.get_compiler('cpp')
cmake = import('cmake') cmake = import('cmake')
# if we are building a shared library
if get_option('default_library') == 'shared' if get_option('default_library') == 'shared'
raylib = dependency('raylib', required: false) # Try to find dependency with pkg-config raylib = dependency('raylib', required: false) # Try to find dependency with pkg-config
if not raylib.found() if not raylib.found()
raylib = compiler.find_library('raylib', has_headers: ['raylib.h', 'raymath.h'], required: true) # Try to manually search for the dependency message('===========+RAYLIB NOT FOUND===============')
raylib = compiler.find_library('raylib', has_headers: ['raylib.h', 'raymath.h'], required: false) # Try to manually search for the dependency
endif
if not raylib.found()
opt_var = cmake.subproject_options()
opt_var.add_cmake_defines({'BUILD_SHARED_LIBS' : true})
opt_var.add_cmake_defines({'CMAKE_SKIP_RPATH' : true})
raylib_proj = cmake.subproject('raylib', options: opt_var)
raylib = raylib_proj.dependency('raylib')
endif endif
# if not raylib.found()
# opt_var = cmake.subproject_options()
# opt_var.add_cmake_defines({'BUILD_SHARED_LIBS' : true})
# opt_var.add_cmake_defines({'CMAKE_SKIP_RPATH' : true})
# raylib_proj = cmake.subproject('raylib', options: opt_var)
# raylib = raylib_proj.dependency('raylib')
# endif
endif endif
# I we are building a static library
if get_option('default_library') == 'static' if get_option('default_library') == 'static'
raylib_proj = cmake.subproject('raylib') raylib_proj = cmake.subproject('raylib')
raylib = raylib_proj.dependency('raylib') raylib = raylib_proj.dependency('raylib')
@@ -30,7 +34,7 @@ if build_machine.system() == 'windows'
endif endif
executable('pong', executable('pong',
'main.cpp', 'easysock.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp', 'main.cpp', 'easysock.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp', 'check_input.cpp',
'serialization.c', 'serialization.c',
dependencies: [raylib, ws2_dep, winmm] dependencies: [raylib, ws2_dep, winmm]
) )

View File

@@ -8,3 +8,5 @@
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). 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 quit before the game actually starts i.e. while they are inputting the game mode. 9. Allow the user to quit before the game actually starts i.e. while they are inputting the game mode.
10. Move 'timer.h' implementation functions to a 'timer.c' file (or 'timer.cpp', if you want to use OOP). 10. Move 'timer.h' implementation functions to a 'timer.c' file (or 'timer.cpp', if you want to use OOP).
11. Add better error checking in check_server function in check_input.hpp (also, probably move it to a separate implementation file).
12. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.