Compare commits
9 Commits
c490eaa301
...
4ff840e91e
Author | SHA1 | Date | |
---|---|---|---|
4ff840e91e | |||
fd4ad04aeb | |||
d842485103 | |||
3bf65ab8f9 | |||
f3dcbc3b3e | |||
463dfbd3e5 | |||
d3716536f9 | |||
8805402241 | |||
3d0aeac943 |
@@ -18,10 +18,10 @@ GameType check_server(char* ip_text, char* port_text, const int if_mode) {
|
||||
|
||||
/* Check if IP address and port are in valid forms */
|
||||
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) {
|
||||
throw std::invalid_argument("Invalid port");
|
||||
throw std::invalid_argument("Invalid port.");
|
||||
}
|
||||
|
||||
/* From here on, we assume that the IP and port are valid */
|
||||
|
@@ -52,10 +52,11 @@ SOCKET create_socket(int network, char transport) {
|
||||
|
||||
int newSock = socket(domain,type,0);
|
||||
|
||||
/* Set REUSEADDR flag, allowing program to be run twice */
|
||||
int set_opt = 1;
|
||||
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
|
||||
|
||||
/* Set REUSEADDR flag for TCP, allowing program to be run twice */
|
||||
if (transport == ES_TCP) {
|
||||
int set_opt = 1;
|
||||
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
|
||||
}
|
||||
return newSock;
|
||||
}
|
||||
|
||||
|
142
main.cpp
142
main.cpp
@@ -120,109 +120,6 @@ void check_num_args(int argc, char** argv) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* This function checks the command-line arguments passed to the program.
|
||||
It then decides whether the game is in Server or Client mode (or neither), and
|
||||
instantiates the appropriate object. The (uninitialized) objects are passed to the
|
||||
function as pointers. It returns a GameType struct, that indicates whether the game
|
||||
is in server, client or single player mode, and contains the appropriate socket object. */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* GAME STARTED IN CLIENT MODE */
|
||||
if (strcmp(argv[1],"-C") == 0) {
|
||||
if (argc < 3) { /* No address was provided */
|
||||
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();
|
||||
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
|
||||
client->sendAll("GG");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
addr = std::string(argv[2]);
|
||||
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;
|
||||
}
|
||||
|
||||
else {
|
||||
throw EXCEPT_INVALIDARGS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
/* Check the number and validity of command-line arguments. Invalid arguments
|
||||
will throw an exception. */
|
||||
@@ -244,33 +141,22 @@ int main(int argc, char** argv) {
|
||||
|
||||
/* Check if game was started in server or client mode, and call the appropriate function to process the arguments.
|
||||
If game was started in single-player mode (i.e. with no arguments), then the user is prompted in the GUI. */
|
||||
try { // I put this try-catch block outside the if-statement because the exception handling is the same for both client and server.
|
||||
if (argc > 1) { // Server or client mode
|
||||
if (std::string(argv[1]) == "-S") { // Server mode
|
||||
type = check_server(argv[2], argv[3], IF_CLI);
|
||||
}
|
||||
if (std::string(argv[1]) == "-C") { // Client mode
|
||||
type = check_client(argv[2], IF_CLI);
|
||||
}
|
||||
try { // I put this try-catch block outside the if-statement because the exception handling is the same for both client and server.
|
||||
if (argc > 1) { // Server or client mode
|
||||
if (std::string(argv[1]) == "-S") { // Server mode
|
||||
type = check_server(argv[2], argv[3], IF_CLI);
|
||||
}
|
||||
if (std::string(argv[1]) == "-C") { // Client mode
|
||||
type = check_client(argv[2], IF_CLI);
|
||||
}
|
||||
} catch (std::invalid_argument& inv) {
|
||||
std::cout << inv.what() << std::endl;
|
||||
} catch (int err) {
|
||||
std::cout << strerror(err) << std::endl;
|
||||
}
|
||||
|
||||
//try {
|
||||
//type = check_server_client(argc, argv);
|
||||
//} catch(int e) {
|
||||
|
||||
//else {
|
||||
//std::cout << strerror(e) << std::endl;
|
||||
//return -7;
|
||||
//}
|
||||
//} catch(std::invalid_argument& inv) {
|
||||
//std::cout << inv.what() << std::endl;
|
||||
//return -8;
|
||||
//}
|
||||
} catch (std::invalid_argument& inv) {
|
||||
std::cout << inv.what() << std::endl;
|
||||
return -1;
|
||||
} catch (int err) {
|
||||
std::cout << strerror(err) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize window and other variables */
|
||||
SetTraceLogLevel(LOG_NONE);
|
||||
|
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 */
|
||||
peer_addr = connect_code::dec_to_dotted_dec(std::to_string(htonl(temp_struct->sin_addr.s_addr)));
|
||||
} else {
|
||||
/* FOR IPv6 */
|
||||
peer_addr = "IPV6 NOT SUPPORTED YET";
|
||||
/* FOR IPv6 - Use the inet_ntop function, and convert the struct's address into a string */
|
||||
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;
|
||||
@@ -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
|
||||
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
|
||||
'catching' the thrown exception and using strerror().
|
||||
This function also sets a timeout of 100ms for UDP sockets */
|
||||
'catching' the thrown exception and using strerror().*/
|
||||
|
||||
void Server::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. */
|
||||
|
||||
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) {
|
||||
@@ -33,7 +32,6 @@ Sock::Sock(char protocol, const char* address, int port) {
|
||||
throw std::invalid_argument("Invalid protocol");
|
||||
}
|
||||
|
||||
this->ip_ver = ip_ver;
|
||||
this->protocol = protocol;
|
||||
this->port = port;
|
||||
this->address = std::string(address);
|
||||
@@ -50,7 +48,9 @@ void Sock::sendAll(std::string to_send) {
|
||||
|
||||
/* For UDP sockets */
|
||||
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 */
|
||||
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 */
|
||||
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) {
|
||||
throw errno * -1;
|
||||
throw errno;
|
||||
}
|
||||
total_bytes_sent += num_bytes_sent;
|
||||
}
|
||||
@@ -89,6 +89,9 @@ char* Sock::recvAll() {
|
||||
if (num_bytes_received == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (num_bytes_received < 0) {
|
||||
throw errno;
|
||||
}
|
||||
/* Null-terminate the string */
|
||||
*(buffer + num_bytes_received) = '\0';
|
||||
return buffer;
|
||||
@@ -106,7 +109,7 @@ char* Sock::recvAll() {
|
||||
}
|
||||
|
||||
if (num_bytes_received < 0) {
|
||||
throw errno * -1;
|
||||
throw errno;
|
||||
}
|
||||
total_bytes_received += num_bytes_received;
|
||||
has_been_read = true;
|
||||
|
18
todo.txt
18
todo.txt
@@ -1,14 +1,10 @@
|
||||
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.
|
||||
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).
|
||||
5. Use the struct to establish a connection, and to start each round (instead of sending strings).
|
||||
6. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
|
||||
7. Communicate the paddle reset position to the peer, after a round.
|
||||
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 specify which paddle they want to control, in multi-player mode.
|
||||
10. Try to make the ball go between screens.
|
||||
11. 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).
|
||||
12. Add a --help option, that displays information about the game and how to run it.
|
||||
13. Add better error-checking to check_server() and check_client() (Use check_server_client() as inspiration).
|
||||
14. Ensure that check_server() and check_client() work properly for command-line invocation, then remove check_server_client().
|
||||
4. 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. 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. 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. 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