Compare commits

...

14 Commits

Author SHA1 Message Date
4001135451 Integrated upstream changes, since I forgot to pull before making local changes. 2024-03-11 01:32:56 -05:00
66d7585297 Removed IP version checking code (since this is handled in the Sock constructor), and allowed server (but not client, yet) to quit game before round start 2024-03-11 01:29:56 -05:00
727aeafdb9 Updated Server and Client constructor calls 2024-03-11 01:28:35 -05:00
3bdfdb114c Updated TODO 2024-03-11 01:28:04 -05:00
f840ff9c00 Updated comment explaining function 2024-03-10 21:57:58 -05:00
986e386098 Updated Server and Client constructor calls, so that they don't pass in the IP version 2024-03-10 21:56:03 -05:00
53282727ec Removed testing code 2024-03-10 21:55:09 -05:00
d43dc41f25 Updated TODO 2024-03-10 21:54:47 -05:00
0058e7e411 Removed ip_ver parameter
I removed this because I realized I could just check the IP version inside
the constructor. The Sock constructor now checks the address passed to it.
Like before, if the address is neither v4 nor v6, an exception is thrown.
Since the Server and Client constructors call the Sock constructor, no change
was required in these files, except passing the right number of parameters.
2024-03-10 21:53:06 -05:00
764f343f5d Updated TODO 2024-03-10 19:27:46 -05:00
cdd1db6808 Implemented IPv6 address decoding algorithm 2024-03-10 19:27:17 -05:00
ae044c1905 Convert character to upper-case before converting to decimal 2024-03-10 19:26:39 -05:00
8011c5e8b9 Finished script to create and package statically linked binary on Linux 2024-03-10 16:13:45 -05:00
24eda2d16a Updated TODO 2024-03-10 16:13:26 -05:00
10 changed files with 92 additions and 57 deletions

View File

@@ -30,7 +30,7 @@ GameType check_server(char* ip_text, char* port_text) {
std::string code = connect_code::encode(addr, std::to_string(port));
/* Create server socket and wait for client to connect */
Server* server = new Server(check_ip_ver(addr.data()), ES_UDP, addr.data(), port);
Server* server = new Server(ES_UDP, addr.data(), port);
server->create_socket();
display_text_centered("Your code is " + code + "\nWaiting for connection...");
std::string response = "";
@@ -58,7 +58,7 @@ GameType check_client(char* code_text) {
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 {
addr_port = connect_code::decode(connect_code);
Client* client = new Client(4, 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();
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
client->sendAll("GG");

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cstdint>
@@ -82,9 +83,12 @@ namespace connect_code {
std::string encode(std::string address, std::string port) {
std::string addr_coded;
/* Convert the address to decimal, and convert that to hex */
std::string addr_coded = "";
if (check_ip_ver(address.data()) == 4) {
/* First, convert the address into a decimal format. Then convert this decimal format
into base-32, and also convert the port number into base-32. Join these together with
a "_". */
/* I don't really have a reason to use my own function (dotted_dec_to_dec()
and dec_to_dotted_dec()), to convert the IP address from text to binary.
The inet_pton() and inet_ntop() functions can do this just fine, and also
@@ -101,10 +105,9 @@ namespace connect_code {
Then, tokenize the string, using colons as the delimiters.
Finally, take each token in the string, and convert it from base-16 to base-32, appending a '-' as a delimiter. */
std::string addr_expanded = expand_ip6_addr(address);
std::string addr_coded = "";
std::vector<std::string> addr_tokenized = tokenize_str(addr_expanded, ":");
for (int i = 0; i < addr_tokenized.size()-1; i++ ) {
for (size_t i = 0; i < addr_tokenized.size()-1; i++ ) {
addr_coded += base_convert(addr_tokenized[i], 16, 32);
addr_coded += "-";
}
@@ -113,9 +116,6 @@ namespace connect_code {
/* TODO - Check if the IP address is actually converted properly, and test if the server socket is created correctly.
Also do the same for client side, and check client-server connection. */
std::cout << addr_coded << std::endl;
abort();
}
/* Convert the port to hex */
@@ -127,32 +127,59 @@ namespace connect_code {
}
std::vector<std::string> decode(std::string connect_code) {
//<AIRPLANE_CODE>
if (connect_code.find("_") == std::string::npos) {
throw std::invalid_argument("Invalid code entered."); // There must be an underscore, to separate the address part from the port part
}
int ip_ver = 0;
if (connect_code.find("-") == connect_code.npos) {
if (connect_code.find("-") != std::string::npos) {
ip_ver = 6; // If the string contains hyphens, it must be an IPv6 address encoding.
} else {
ip_ver = 4;
}
//</AIRPLANE_CODE>
if (connect_code.find("_") == std::string::npos) {
throw std::invalid_argument("Invalid code entered.");
}
std::vector<std::string> result = tokenize_str(connect_code, "_"); /* Split the string into address and port */
std::string address = result[0]; /* Address (in base 16) */
std::string port = result[1]; /* Port (in base 16) */
/* Base 16 to base 10 - These lines convert the string to a base 10 number, and convert the result back into a string */
address = std::to_string(std::stoul(address, 0, 32));
port = std::to_string(std::stoul(port, 0, 32));
/* Convert decimal address to dotted decimal */
address = dec_to_dotted_dec(address);
std::string address = result[0]; /* Address (in base 32) */
std::string port = result[1]; /* Port (in base 32) */
std::vector<std::string> ret_val;
ret_val.push_back(address);
ret_val.push_back(port);
/* The IPv6 and IPv4 encodings are slightly different - I use a hyphen as a delimiter
for IPv6, while there is no delimiter for IPv4. This is why I need to check if the address
is IPv4 or IPv6. */
if (ip_ver == 4) {
/* Base 32 to base 10 - These lines convert the string to a base 10 number, and convert the result back into a string */
address = std::to_string(std::stoul(address, 0, 32));
port = std::to_string(std::stoul(port, 0, 32));
/* Convert decimal address to dotted decimal */
address = dec_to_dotted_dec(address);
/* Create a vector containing the address and the port, which will be returned */
ret_val.push_back(address);
ret_val.push_back(port);
} else {
/* IPv6 */
/* There are three main steps to decoding for IPv6:
1. Tokenize the address using the delimiter set while encoding ('-', in my case).
2. Convert each token from base-32 to base-16.
3. Join the string vector back together into a string, this time using ':' as a delimiter. This will give us our IP address. */
std::string conv_addr = ""; // Stores the final address
std::vector<std::string> address_tokenized = tokenize_str(address, "-"); // Step 1
for (size_t i = 0; i < address_tokenized.size()-1; i++) {
address_tokenized[i] = base_convert(address_tokenized[i], 32, 16); // Step 2
conv_addr += address_tokenized[i] + ":"; // Step 3
}
conv_addr += base_convert(address_tokenized[address_tokenized.size()-1], 32, 16); // Add the last token
port = std::to_string(std::stoul(port, 0, 32));
ret_val.push_back(conv_addr);
ret_val.push_back(port);
}
return ret_val;
}
}

View File

@@ -17,10 +17,10 @@ meson compile -C "$BASE_DIR/build/"
# Package the application:
# 1. Copy the executable to REL_DIR
# 2. Create a tarball
# 2. Create a tarball after cd'ing into the parent directory.
cp "$BASE_DIR/build/pong" "$REL_DIR"
tar -czf "$BASE_DIR/release/pong.tar.gz" "$REL_DIR"
tar -C "$BASE_DIR/release/static" -czf "$BASE_DIR/release/pong.tar.gz" "pong/"
# Reset default build target to shared
meson configure -Ddefault_library=shared "$BASE_DIR/build/"

View File

@@ -15,7 +15,7 @@ public:
~Client();
/* Normal constructor that calls the parent constructor to set the given values */
Client(int ip_ver, char protocol, const char* address, int port) : Sock(ip_ver, protocol, address, port) {}
Client(char protocol, const char* address, int port) : Sock(protocol, address, port) {}
void create_socket() override;

View File

@@ -15,7 +15,7 @@ public:
/* Constructors */
Server() {}
Server(int ip_ver, char protocol, const char* address, int port) : Sock(ip_ver, protocol, address, port) {}
Server(char protocol, const char* address, int port) : Sock(protocol, address, port) {}
/* Destructor */
~Server();

View File

@@ -38,7 +38,7 @@ public:
virtual ~Sock();
/* Regular constructor - defined in sock.cpp */
Sock(int ip_ver, char protocol, const char* address, int port);
Sock(char protocol, const char* address, int port);
/* Method to send data in 'to_send' through the 'other_socket' socket */
void sendAll(std::string to_send);

View File

@@ -99,7 +99,11 @@ GameType check_server_client(int argc, char** argv) {
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);
Client* client = new Client(4, ES_UDP, addr_port[0].data(), std::stoi(addr_port[1]));
/* Check IP address version */
if (check_ip_ver(addr_port[0].data()) < 0) {
throw std::invalid_argument("Invalid code entered.");
}
Client* client = new Client(ES_UDP, addr_port[0].data(), std::stoi(addr_port[1]));
client->create_socket();
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
client->sendAll("GG");
@@ -148,7 +152,7 @@ GameType check_server_client(int argc, char** argv) {
std::cout << "Your code is " << code << std::endl;
/* Create server socket and wait for client to connect */
Server* server = new Server(4, ES_UDP, addr.data(), port);
Server* server = new Server(ES_UDP, addr.data(), port);
server->create_socket();
std::cout << "Waiting for connection..." << std::endl;
std::string response = "";
@@ -376,11 +380,18 @@ int main(int argc, char** argv) {
}
}
/* For client (wait for start message from server) */
/* For client (wait for start or quit message from server): When the peer quits the
game, it sends a serialized struct, containing all zeros, with the last bit turned
on as a flag. We catch this zero bit, as it indicates that the peer quit the game. */
if (type.mode == M_CLIENT) {
do {
response = type.netsock->recvAll();
} while (response[0] != 'S');
} while (response[0] != 'S' && response[0] != 0);
if (response[0] == 0) {
CloseWindow();
std::cout << "Peer unexpectedly quit game." << std::endl;
return -1;
}
game_started = true;
std::cout << "Game has been started by server." << std::endl;
}

View File

@@ -1,6 +1,7 @@
#include "includes/numeric_base.hpp"
#include <string>
#include <cmath>
#include <cctype>
#include <algorithm>
std::string possible_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -12,10 +13,10 @@ unsigned int to_decimal(std::string num, int from_base) {
/* Here, we convert 'num' to decimal (base 10) - Find the index of
every character in the string, in 'possible_chars' and
compute the value using */
compute the value using the position of the character in the number. */
for (int i=0; i < (int)num.length(); i++) {
current_char = num.at(i);
index = possible_chars.find(current_char);
index = possible_chars.find(toupper(current_char)); // Convert the character to upper-case, so that the earliest match is detected
value += pow(from_base, num.length() - i - 1) * index;
}

View File

@@ -18,11 +18,12 @@ Sock::~Sock() {}
/* Constructor - This function initializes the object attributes with the given
parameters. It throws an exception if an IPv4 address was given, but the type
given is IPv6 (or the other way around). */
parameters. The address version (IPv4 or IPv6) is determined based on the given address. */
Sock::Sock(int ip_ver, 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);
if (ip_ver != 4 && ip_ver != 6) {
throw std::invalid_argument("Invalid IP address type");
}
@@ -37,11 +38,6 @@ Sock::Sock(int ip_ver, char protocol, const char* address, int port) {
this->protocol = protocol;
this->port = port;
this->address = std::string(address);
/* Check to see if the given IP address matches the given ip_ver */
if ((check_ip_ver(address) != 6 && ip_ver == 6) || (check_ip_ver(address) != 4 && ip_ver == 4)) {
throw std::invalid_argument("Invalid IP address for given type.");
}
}
/* This method sends the given data, through the 'other_sockt' variable.. Client

View File

@@ -1,13 +1,13 @@
1. Try to make the ball go between screens.
2. ----SHOULD BE DONE---- Add code to zip the dist/ folder inside the release_build script.
3. Sign Windows executable, to remove 'Unknown Publisher' warnings.
4. Create and publish statically-linked Linux binary, and create a build script for packaging it.
5. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
6. ----IN PROGRESS---- Figure out how to input game mode and (if applicable) IP address and port through the GUI, instead of the command-line.
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. Allow the user to quit before the game actually starts i.e. while they are inputting the game mode.
9. Add better error checking in check_server and check_client functions in check_input.cpp.
10. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.
11. Allow the user to specify which paddle they want to control, in multi-player mode.
12. Add IPv6 support for the server and client sockets (and everything that goes along with it, such as error handling for IP addresses).
13. Figure out how to make 'tar' not include the entire directory structure, when creating the archive in create_static_linux.sh.
4. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
5. ----IN PROGRESS---- Figure out how to input game mode and (if applicable) IP address and port through the GUI, instead of the command-line.
6. 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. Allow the user to quit before the game actually starts i.e. while they are inputting the game mode.
8. Add better error checking in check_server and check_client functions in check_input.cpp. e.g. If client fails to connect, display a message in GUI.
9. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.
10. Allow the user to specify which paddle they want to control, in multi-player mode.
11. Add IPv6 support for the server and client sockets (and everything that goes along with it, such as error handling for IP addresses).
12. Communicate the paddle reset position to the peer, after a round.
13. Test with valgrind.
14. Use the struct to establish a connection, and to start each round (instead of sending strings).