Compare commits
25 Commits
3ab97b3853
...
v0.2
Author | SHA1 | Date | |
---|---|---|---|
f42ac94a45 | |||
4ff840e91e | |||
fd4ad04aeb | |||
d842485103 | |||
3bf65ab8f9 | |||
f3dcbc3b3e | |||
463dfbd3e5 | |||
d3716536f9 | |||
8805402241 | |||
3d0aeac943 | |||
c490eaa301 | |||
0e7ebb4d78 | |||
c94138ad8b | |||
cfbc726dca | |||
ec2f3320e3 | |||
43ba4aba0c | |||
c2bedb0601 | |||
77a147e08f | |||
26999a1145 | |||
f41c3d22e2 | |||
9f1f313091 | |||
8401f74922 | |||
d2dd95b7cc | |||
5cf11ac014 | |||
0dbf8936fd |
@@ -1,4 +1,5 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
#include "includes/easysock.h"
|
#include "includes/easysock.h"
|
||||||
#include "includes/connect_code.hpp"
|
#include "includes/connect_code.hpp"
|
||||||
#include "includes/server.hpp"
|
#include "includes/server.hpp"
|
||||||
@@ -7,19 +8,20 @@
|
|||||||
#include "includes/raygui/raygui.h"
|
#include "includes/raygui/raygui.h"
|
||||||
#include "includes/exception_consts.hpp"
|
#include "includes/exception_consts.hpp"
|
||||||
#include "includes/raygui_helpers.hpp"
|
#include "includes/raygui_helpers.hpp"
|
||||||
|
#include "includes/display_text.hpp"
|
||||||
#include "includes/timer.h"
|
#include "includes/timer.h"
|
||||||
|
|
||||||
GameType check_server(char* ip_text, char* port_text) {
|
GameType check_server(char* ip_text, char* port_text, const int if_mode) {
|
||||||
GameType type;
|
GameType type;
|
||||||
std::string addr;
|
std::string addr;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
|
||||||
/* Check if IP address and port are in valid forms */
|
/* Check if IP address and port are in valid forms */
|
||||||
if (check_ip_ver(ip_text) < 0) {
|
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) {
|
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 */
|
/* From here on, we assume that the IP and port are valid */
|
||||||
@@ -32,7 +34,7 @@ GameType check_server(char* ip_text, char* port_text) {
|
|||||||
/* Create server socket and wait for client to connect */
|
/* Create server socket and wait for client to connect */
|
||||||
Server* server = new Server(ES_UDP, addr.data(), port);
|
Server* server = new Server(ES_UDP, addr.data(), port);
|
||||||
server->create_socket();
|
server->create_socket();
|
||||||
display_text_centered("Your code is " + code + "\nWaiting for connection...");
|
display_text("Your code is " + code + "\nWaiting for connection...", if_mode);
|
||||||
std::string response = "";
|
std::string response = "";
|
||||||
char* temp_response = NULL;
|
char* temp_response = NULL;
|
||||||
/* Wait for the client to connect. Since recvAll returns a char*, we need to create a temporary variable to check for NULL.
|
/* Wait for the client to connect. Since recvAll returns a char*, we need to create a temporary variable to check for NULL.
|
||||||
@@ -43,30 +45,29 @@ GameType check_server(char* ip_text, char* port_text) {
|
|||||||
response = std::string(temp_response);
|
response = std::string(temp_response);
|
||||||
|
|
||||||
server->sendAll("U2");
|
server->sendAll("U2");
|
||||||
display_text_centered("Connection received from " + server->get_peer_addr());
|
display_text("Connection received from " + server->get_peer_addr(), if_mode);
|
||||||
Timer timer = timer_init(3);
|
|
||||||
while (!timer_done(timer)); // Wait for five seconds
|
|
||||||
|
|
||||||
type.mode = M_SERVER;
|
type.mode = M_SERVER;
|
||||||
type.netsock = server;
|
type.netsock = server;
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameType check_client(char* code_text) {
|
GameType check_client(char* code_text, const int if_mode) {
|
||||||
GameType type;
|
GameType type;
|
||||||
std::vector<std::string> addr_port;
|
std::vector<std::string> addr_port;
|
||||||
std::string connect_code = std::string(code_text); /* The connect code is a special string, that contains the server address and port. It is given by the server. */
|
std::string connect_code = std::string(code_text); /* The connect code is a special string, that contains the server address and port. It is given by the server. */
|
||||||
try {
|
try {
|
||||||
addr_port = connect_code::decode(connect_code);
|
addr_port = connect_code::decode(connect_code);
|
||||||
|
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* client = new Client(ES_UDP, addr_port[0].data(), std::stoi(addr_port[1]));
|
||||||
client->create_socket();
|
client->create_socket();
|
||||||
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
|
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
|
||||||
client->sendAll("GG");
|
client->sendAll("GG");
|
||||||
std::string msg_from_server = client->recvAllNB();
|
std::string msg_from_server = client->recvAll();
|
||||||
if (msg_from_server == "U2") {
|
if (msg_from_server == "U2") {
|
||||||
display_text_centered("Connection made");
|
display_text("Connection made", if_mode);
|
||||||
Timer timer = timer_init(3);
|
|
||||||
while (!timer_done(timer));
|
|
||||||
} else {
|
} else {
|
||||||
throw std::invalid_argument("Server didn't respond with correct message.");
|
throw std::invalid_argument("Server didn't respond with correct message.");
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
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"
|
||||||
|
RAYLIB_DLL="$BASE_DIR/build/subprojects/raylib/libraylib.dll"
|
||||||
|
|
||||||
mkdir -p "$REL_DIR"
|
mkdir -p "$REL_DIR"
|
||||||
|
|
||||||
@@ -14,12 +15,15 @@ meson setup build/
|
|||||||
# Build the application
|
# Build the application
|
||||||
meson compile -C build/
|
meson compile -C build/
|
||||||
|
|
||||||
# Parse the output of the 'ldd' command, and create a file with the required DLL paths.
|
# 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}' > "$BASE_DIR/tmp_file.txt"
|
ldd build/pong.exe | awk ' NF == 4 {print $3}' | grep -i "dll" > "$BASE_DIR/tmp_file.txt"
|
||||||
|
|
||||||
# Copy the required DLLs.
|
# Copy the required DLLs.
|
||||||
cp $(cat "$BASE_DIR/tmp_file.txt") "$REL_DIR"
|
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
|
# Copy the executable itself
|
||||||
cp "$BASE_DIR/build/pong" "$REL_DIR"
|
cp "$BASE_DIR/build/pong" "$REL_DIR"
|
||||||
|
|
||||||
|
13
display_text.cpp
Normal file
13
display_text.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "includes/display_text.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void display_text(std::string to_disp, const int if_mode) {
|
||||||
|
if (if_mode == IF_CLI) {
|
||||||
|
std::cout << to_disp << std::endl;
|
||||||
|
}
|
||||||
|
if (if_mode == IF_GUI) {
|
||||||
|
display_text_raygui(to_disp);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
11
easysock.c
11
easysock.c
@@ -52,10 +52,11 @@ SOCKET create_socket(int network, char transport) {
|
|||||||
|
|
||||||
int newSock = socket(domain,type,0);
|
int newSock = socket(domain,type,0);
|
||||||
|
|
||||||
/* Set REUSEADDR flag, allowing program to be run twice */
|
/* Set REUSEADDR flag for TCP, allowing program to be run twice */
|
||||||
int set_opt = 1;
|
if (transport == ES_TCP) {
|
||||||
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
|
int set_opt = 1;
|
||||||
|
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
|
||||||
|
}
|
||||||
return newSock;
|
return newSock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ SOCKET create_remote (int network,char transport, const char* address,int port,s
|
|||||||
if (err_code != 0) {
|
if (err_code != 0) {
|
||||||
return (-1 * err_code);
|
return (-1 * err_code);
|
||||||
}
|
}
|
||||||
remote_addr_struct = 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);
|
||||||
} else {
|
} else {
|
||||||
create_addr(network,address,port,remote_addr_struct);
|
create_addr(network,address,port,remote_addr_struct);
|
||||||
|
@@ -17,12 +17,14 @@ typedef struct {
|
|||||||
|
|
||||||
/* This function checks the IP address and port passed to it, and returns a struct,
|
/* 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.
|
that contains information about the game mode, and contains the server socket.
|
||||||
It assumes that both ip_text and port_text are non-null
|
It assumes that both ip_text and port_text are non-null.
|
||||||
|
Any errors are printed using the display_text function, with the given if_type.
|
||||||
TODO - Add better error checking. */
|
TODO - Add better error checking. */
|
||||||
GameType check_server(char* ip_text, char* port_text);
|
GameType check_server(char* ip_text, char* port_text, const int if_type);
|
||||||
|
|
||||||
/* NOT IMPLEMENTED YET - This function checks the code given to it, and returns a struct
|
/* 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. */
|
that contains information about the game mode, and contains the client socket.
|
||||||
GameType check_client(char* code);
|
Any errors are printed using the display_text function, with the given if_type. */
|
||||||
|
GameType check_client(char* code, const int if_type);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
10
includes/display_text.hpp
Normal file
10
includes/display_text.hpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "includes/raygui_helpers.hpp"
|
||||||
|
|
||||||
|
/* Constants that can be used by caller function. */
|
||||||
|
const int IF_CLI = 1;
|
||||||
|
const int IF_GUI = 2;
|
||||||
|
|
||||||
|
/* This function is used to display text. It is used to abstract the differences
|
||||||
|
between GUI invocation and CLI invocation. The if_mode parameter is used to
|
||||||
|
determine whether the game was launched from GUI or CLI. */
|
||||||
|
void display_text(std::string to_disp, const int if_mode);
|
@@ -5,6 +5,9 @@ extern "C" {
|
|||||||
#define EASYSOCK_H_
|
#define EASYSOCK_H_
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#define NOGDI // All GDI defines and routines
|
||||||
|
#define NOUSER // All USER defines and routines
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
/* Display the given text, centered on the screen, as a label.
|
/* Display the given text, centered on the screen, as a label.
|
||||||
NEEDS RAYGUI LIBRARY. */
|
NEEDS RAYGUI LIBRARY. */
|
||||||
void display_text_centered(std::string to_disp);
|
void display_text_raygui(std::string to_disp);
|
||||||
|
|
||||||
/* Display the given string, and exit the game after 'time' seconds. */
|
/* Display the given string, and exit the game after 'time' seconds. */
|
||||||
void display_and_exit(std::string to_disp, int time);
|
void display_and_exit_raygui(std::string to_disp, int time);
|
||||||
#endif
|
#endif
|
||||||
|
199
main.cpp
199
main.cpp
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
#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/check_input.hpp"
|
||||||
#include "includes/raygui_helpers.hpp"
|
#include "includes/display_text.hpp"
|
||||||
#include "includes/easysock.h"
|
#include "includes/easysock.h"
|
||||||
#include "includes/serialization.h"
|
#include "includes/serialization.h"
|
||||||
#include "includes/timer.h"
|
#include "includes/timer.h"
|
||||||
@@ -48,6 +49,24 @@ 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);
|
||||||
|
|
||||||
|
std::string HELP_TEXT = "\nnetpong - A networked pong game for the internet era.\n"
|
||||||
|
"\n"
|
||||||
|
"Usage: \n"
|
||||||
|
"netpong [MODE] [ADDRESS PORT]|[CODE]\n"
|
||||||
|
"\n"
|
||||||
|
"MODE: \n"
|
||||||
|
"-S : Server mode. Starts a server to allow the other player to connect.\n"
|
||||||
|
"IP address and port must be specified.\n"
|
||||||
|
"\n"
|
||||||
|
"-C: Client mode. Connects to a server, using the provided connection code.\n"
|
||||||
|
"\n"
|
||||||
|
"If no mode is specified, single player mode is used as default.\n"
|
||||||
|
"\n"
|
||||||
|
"CONTROLS:"
|
||||||
|
"\'W\' and \'S\' control left paddle (AKA client paddle)\n"
|
||||||
|
"Up and Down arrow keys control right paddle (AKA server paddle)\n";
|
||||||
|
|
||||||
|
|
||||||
/* Simple function to return 1 if a value is positive, and -1 if it is negative */
|
/* Simple function to return 1 if a value is positive, and -1 if it is negative */
|
||||||
int signum(int num) {
|
int signum(int num) {
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
@@ -75,142 +94,68 @@ raylib::Vector2 changeVelocityAfterCollision(Paddle paddle, Ball ball) {
|
|||||||
return raylib::Vector2(new_x_vel, new_y_vel);
|
return raylib::Vector2(new_x_vel, new_y_vel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function checks the command-line arguments passed to the program.
|
/* Checks the number and type of the command-line arguments. Throws an exception
|
||||||
It then decides whether the game is in Server or Client mode (or neither), and
|
if the args are invalid. DOES NOT PROCESS VALID ARGUMENTS. */
|
||||||
instantiates the appropriate object. The (uninitialized) objects are passed to the
|
void check_num_args(int argc, char** argv) {
|
||||||
function as pointers. It returns a GameType struct, that indicates whether the game
|
if (argc > 4) {
|
||||||
is in server, client or single player mode, and contains the appropriate socket object. */
|
throw std::invalid_argument("ARGUMENT ERROR: Too many arguments. To view syntax, use -h or --help.");
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
if (argc > 1) { // Either server or client mode
|
||||||
/* GAME STARTED IN CLIENT MODE */
|
if (std::string(argv[1]) == "-S") {
|
||||||
if (strcmp(argv[1],"-C") == 0) {
|
if (argc < 4) { // Server mode but no address and/or port
|
||||||
if (argc < 3) { /* No address was provided */
|
throw std::invalid_argument("ARGUMENT ERROR: Server mode specified without any address or port.");
|
||||||
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();
|
else if (std::string(argv[1]) == "-C") {
|
||||||
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
|
if (argc < 3) { // Client mode but no code
|
||||||
client->sendAll("GG");
|
throw std::invalid_argument("ARGUMENT ERRROR: Client mode specified without any code.");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
else if (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help") {
|
||||||
|
throw std::invalid_argument(HELP_TEXT); // I am abusing the exception mechanism here, so that I can ensure that the caller quits the program after printing the help message.
|
||||||
/* 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 {
|
} else {
|
||||||
addr = std::string(argv[2]);
|
throw std::invalid_argument("Unrecognized argument.");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
else {
|
|
||||||
throw EXCEPT_INVALIDARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
/* Check if game was started in server or client mode, and set appropriate variables */
|
/* Check the number and validity of command-line arguments. Invalid arguments
|
||||||
|
will throw an exception. */
|
||||||
|
try {
|
||||||
|
check_num_args(argc, argv);
|
||||||
|
} catch (std::invalid_argument& inv) {
|
||||||
|
std::cout << inv.what() << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From here on, we assume that:
|
||||||
|
a. The program was started with no arguments (User is prompted in GUI), OR
|
||||||
|
b. The program was started in server mode, and an additional was given, OR
|
||||||
|
c. The program was started in client mode, and an additional argument was given. */
|
||||||
|
|
||||||
/* GameType struct, to define whether the game is in single or multi-player mode, and
|
/* GameType struct, to define whether the game is in single or multi-player mode, and
|
||||||
to hold the appropriate socket */
|
to hold the appropriate socket */
|
||||||
GameType type;
|
GameType type;
|
||||||
|
|
||||||
try {
|
/* Check if game was started in server or client mode, and call the appropriate function to process the arguments.
|
||||||
type = check_server_client(argc, argv);
|
If game was started in single-player mode (i.e. with no arguments), then the user is prompted in the GUI. */
|
||||||
} catch(int e) {
|
try { // I put this try-catch block outside the if-statement because the exception handling is the same for both client and server.
|
||||||
if (e == EXCEPT_TOOFEWARGS) {
|
if (argc > 1) { // Server or client mode
|
||||||
std::cout << "Started in client mode, but no address was specified." << std::endl;
|
if (std::string(argv[1]) == "-S") { // Server mode
|
||||||
return -1;
|
type = check_server(argv[2], argv[3], IF_CLI);
|
||||||
|
}
|
||||||
|
if (std::string(argv[1]) == "-C") { // Client mode
|
||||||
|
type = check_client(argv[2], IF_CLI);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (e == EXCEPT_INVALIDARGS) {
|
} catch (std::invalid_argument& inv) {
|
||||||
std::cout << "Invalid argument." << std::endl;
|
std::cout << inv.what() << std::endl;
|
||||||
return -2;
|
return -1;
|
||||||
}
|
} catch (int err) {
|
||||||
if (e == EXCEPT_INVALIDIP) {
|
std::cout << strerror(err) << std::endl;
|
||||||
std::cout << "Invalid IP address provided." << std::endl;
|
return -1;
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
if (e == EXCEPT_WRONGRESPONSE) {
|
|
||||||
std::cout << "The server didn't respond with the correct message. Are you sure you have used the right server?" << std::endl;
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cout << strerror(e) << std::endl;
|
|
||||||
return -7;
|
|
||||||
}
|
|
||||||
} catch(std::invalid_argument& inv) {
|
|
||||||
std::cout << inv.what() << std::endl;
|
|
||||||
return -8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize window and other variables */
|
/* Initialize window and other variables */
|
||||||
@@ -319,14 +264,14 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = check_server(ip_text, port_text);
|
type = check_server(ip_text, port_text, IF_GUI);
|
||||||
} catch (int e) {
|
} catch (int e) {
|
||||||
display_and_exit(std::string(std::strerror(e)) + "\nClosing game...", 2); // The server constructor throws the errno if it cannot create a socket
|
display_and_exit_raygui(std::string(std::strerror(e)) + "\nClosing game...", 2); // The server constructor throws the errno if it cannot create a socket
|
||||||
free(ip_text);
|
free(ip_text);
|
||||||
free(port_text);
|
free(port_text);
|
||||||
return -1;
|
return -1;
|
||||||
} catch (std::invalid_argument& inv) {
|
} catch (std::invalid_argument& inv) {
|
||||||
display_and_exit(std::string(inv.what()) + "\nClosing game...", 2);
|
display_and_exit_raygui(std::string(inv.what()) + "\nClosing game...", 2);
|
||||||
free(ip_text);
|
free(ip_text);
|
||||||
free(port_text);
|
free(port_text);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -359,12 +304,12 @@ int main(int argc, char** argv) {
|
|||||||
EndDrawing();
|
EndDrawing();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
type = check_client(code_text);
|
type = check_client(code_text, IF_GUI);
|
||||||
} catch (int e) {
|
} catch (int e) {
|
||||||
display_and_exit(std::string(std::strerror(e)) + "\nClosing game...", 2); // The client constructor throws the errno if it cannot create a socket
|
display_and_exit_raygui(std::string(std::strerror(e)) + "\nClosing game...", 2); // The client constructor throws the errno if it cannot create a socket
|
||||||
return -1;
|
return -1;
|
||||||
} catch (std::invalid_argument& inv) {
|
} catch (std::invalid_argument& inv) {
|
||||||
display_and_exit(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);
|
||||||
|
@@ -29,12 +29,12 @@ endif
|
|||||||
ws2_dep = compiler.find_library('ws2_32', required: false)
|
ws2_dep = compiler.find_library('ws2_32', required: false)
|
||||||
winmm = compiler.find_library('winmm', required: false)
|
winmm = compiler.find_library('winmm', required: false)
|
||||||
if build_machine.system() == 'windows'
|
if build_machine.system() == 'windows'
|
||||||
add_global_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
|
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',
|
'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',
|
'serialization.c', 'timer.c', 'easysock.c',
|
||||||
dependencies: [raylib, ws2_dep, winmm]
|
dependencies: [raylib, ws2_dep, winmm]
|
||||||
)
|
)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#include "includes/raygui_helpers.hpp"
|
#include "includes/raygui_helpers.hpp"
|
||||||
#include "includes/raygui/raygui.h"
|
#include "includes/raygui/raygui.h"
|
||||||
#include "includes/timer.h"
|
#include "includes/timer.h"
|
||||||
void display_text_centered(std::string to_disp) {
|
void display_text_raygui(std::string to_disp) {
|
||||||
const char* to_disp_cstr = to_disp.c_str();
|
const char* to_disp_cstr = to_disp.c_str();
|
||||||
Vector2 label_size = MeasureTextEx(GetFontDefault(), to_disp_cstr, GuiGetStyle(DEFAULT, TEXT_SIZE)+1, GuiGetStyle(DEFAULT, TEXT_SPACING)+1); // The '+1' is there to account for any rounding errors
|
Vector2 label_size = MeasureTextEx(GetFontDefault(), to_disp_cstr, GuiGetStyle(DEFAULT, TEXT_SIZE)+1, GuiGetStyle(DEFAULT, TEXT_SPACING)+1); // The '+1' is there to account for any rounding errors
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ void display_text_centered(std::string to_disp) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_and_exit(std::string to_disp, int time) {
|
void display_and_exit_raygui(std::string to_disp, int time) {
|
||||||
display_text_centered(to_disp);
|
display_text_raygui(to_disp);
|
||||||
Timer timer = timer_init(time);
|
Timer timer = timer_init(time);
|
||||||
while (!timer_done(timer));
|
while (!timer_done(timer));
|
||||||
return;
|
return;
|
||||||
|
10
server.cpp
10
server.cpp
@@ -72,8 +72,11 @@ char* Server::recvAllNB() {
|
|||||||
/* Convert the s_addr field of the caseted struct to host network-byte, and convert it to a dotted decimal */
|
/* 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)));
|
peer_addr = connect_code::dec_to_dotted_dec(std::to_string(htonl(temp_struct->sin_addr.s_addr)));
|
||||||
} else {
|
} else {
|
||||||
/* FOR IPv6 */
|
/* FOR IPv6 - Use the inet_ntop function, and convert the struct's address into a string */
|
||||||
peer_addr = "IPV6 NOT SUPPORTED YET";
|
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;
|
return to_return;
|
||||||
@@ -98,8 +101,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
|
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
|
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
|
of the failing function, and enables the caller to print a corresponding error message by
|
||||||
'catching' the thrown exception and using strerror().
|
'catching' the thrown exception and using strerror().*/
|
||||||
This function also sets a timeout of 100ms for UDP sockets */
|
|
||||||
|
|
||||||
void Server::create_socket() {
|
void Server::create_socket() {
|
||||||
Sock::create_socket();
|
Sock::create_socket();
|
||||||
|
13
sock.cpp
13
sock.cpp
@@ -20,7 +20,6 @@ Sock::~Sock() {}
|
|||||||
parameters. The address version (IPv4 or IPv6) is determined based on the given address. */
|
parameters. The address version (IPv4 or IPv6) is determined based on the given address. */
|
||||||
|
|
||||||
Sock::Sock(char protocol, const char* address, int port) {
|
Sock::Sock(char protocol, const char* address, int port) {
|
||||||
/* Error checking */
|
|
||||||
this->ip_ver = check_ip_ver(address);
|
this->ip_ver = check_ip_ver(address);
|
||||||
|
|
||||||
if (ip_ver != 4 && ip_ver != 6) {
|
if (ip_ver != 4 && ip_ver != 6) {
|
||||||
@@ -33,7 +32,6 @@ Sock::Sock(char protocol, const char* address, int port) {
|
|||||||
throw std::invalid_argument("Invalid protocol");
|
throw std::invalid_argument("Invalid protocol");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->ip_ver = ip_ver;
|
|
||||||
this->protocol = protocol;
|
this->protocol = protocol;
|
||||||
this->port = port;
|
this->port = port;
|
||||||
this->address = std::string(address);
|
this->address = std::string(address);
|
||||||
@@ -50,7 +48,9 @@ void Sock::sendAll(std::string to_send) {
|
|||||||
|
|
||||||
/* For UDP sockets */
|
/* For UDP sockets */
|
||||||
if (this->protocol == ES_UDP) {
|
if (this->protocol == ES_UDP) {
|
||||||
sendto(this->sock_fd, to_send.data(), str_length, 0, (struct sockaddr *)dest, addrlen);
|
if (sendto(this->sock_fd, to_send.data(), str_length, 0, (struct sockaddr *)dest, addrlen) == -1) {
|
||||||
|
throw errno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* For TCP sockets */
|
/* For TCP sockets */
|
||||||
else {
|
else {
|
||||||
@@ -58,7 +58,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 */
|
/* 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);
|
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) {
|
if (num_bytes_sent < 0) {
|
||||||
throw errno * -1;
|
throw errno;
|
||||||
}
|
}
|
||||||
total_bytes_sent += num_bytes_sent;
|
total_bytes_sent += num_bytes_sent;
|
||||||
}
|
}
|
||||||
@@ -89,6 +89,9 @@ char* Sock::recvAll() {
|
|||||||
if (num_bytes_received == 0) {
|
if (num_bytes_received == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (num_bytes_received < 0) {
|
||||||
|
throw errno;
|
||||||
|
}
|
||||||
/* Null-terminate the string */
|
/* Null-terminate the string */
|
||||||
*(buffer + num_bytes_received) = '\0';
|
*(buffer + num_bytes_received) = '\0';
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -106,7 +109,7 @@ char* Sock::recvAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (num_bytes_received < 0) {
|
if (num_bytes_received < 0) {
|
||||||
throw errno * -1;
|
throw errno;
|
||||||
}
|
}
|
||||||
total_bytes_received += num_bytes_received;
|
total_bytes_received += num_bytes_received;
|
||||||
has_been_read = true;
|
has_been_read = true;
|
||||||
|
14
todo.txt
14
todo.txt
@@ -1,10 +1,10 @@
|
|||||||
1. Sign Windows executable, to remove 'Unknown Publisher' warnings.
|
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.
|
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.
|
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).
|
4. Use the struct to establish a connection, and to start each round (instead of sending strings).
|
||||||
5. Use the struct to establish a connection, and to start each round (instead of sending strings).
|
5. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
|
||||||
6. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
|
6. Communicate the paddle reset position to the peer, after a round.
|
||||||
7. Communicate the paddle reset position to the peer, after a round.
|
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).
|
||||||
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. Allow the user to specify which paddle they want to control, in multi-player mode.
|
||||||
9. Allow the user to specify which paddle they want to control, in multi-player mode.
|
9. Try to make the ball go between screens.
|
||||||
10. Try to make the ball go between screens.
|
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).
|
||||||
|
Reference in New Issue
Block a user