Compare commits

...

10 Commits

Author SHA1 Message Date
ff8dc1f1f7 Removed files related to cross-compilation.
I had a ton of issues related to cross-compilation (libraries not found, clashing function
names with raylib, improper linking), and I realized that it's too much work to cross-compile,
_and_ I'm using a build system. Instead, I'm probably just going to use my Windows VM to compile for Windows.
I still haven't decided what to do for Mac, though.
2024-02-29 16:40:49 -05:00
4b3d5387a1 Changed code to support Windows libraries and functions as well 2024-02-29 16:38:36 -05:00
ddbbc322a6 Deleted Makefile, switched over fully to meson build system 2024-02-29 16:38:01 -05:00
b90d37c73f Added relevant files for cross-compilation to windows 2024-02-29 16:37:26 -05:00
69e70eb206 Changed the recvAll return type from std::string to char pointer, and created a non-blocking version of the function 2024-02-28 00:05:53 -05:00
349b0b78db Created method to set position of paddle 2024-02-28 00:05:05 -05:00
a37ec79f09 Changed 'recvAll' return type from std::string to char pointer, and created a non-blocking version of the function 2024-02-28 00:04:43 -05:00
ba667d020d Game is mostly finished, added a ton of code for reading and applying peer position.
The most important addition is that the program now parses data in the
Serial_Data struct, and updates the positions accordingly. I also removed
the old implementation with strings, and fixed a bunch of bugs along the way.
2024-02-28 00:00:53 -05:00
effeea73b9 Made the serialization code cleaner, and fixed a bug where data was overwritten by the null pointer 2024-02-28 00:00:28 -05:00
c6bbe82d25 Made the 'recvAll' function return a char pointer instead of a std::string, this is better for portability. Also created a non-blocking version of the function. 2024-02-27 23:59:53 -05:00
14 changed files with 283 additions and 141 deletions

View File

@@ -1,11 +0,0 @@
LDLIBS = -lraylib
CXXFLAGS += -Wall -g -Wextra -pedantic -Werror
pong: main.o easysock.o paddle.hpp ball.hpp math-helpers.hpp
g++ $(CXXFLAGS) main.o easysock.o -o pong $(LDLIBS)
main.o : main.cpp
g++ $(CXXFLAGS) -c main.cpp -o main.o
easysock.o : easysock.c
g++ $(CXXFLAGS) -c easysock.c -o easysock.o

View File

@@ -16,7 +16,7 @@ address specified in the constructor, and will throw an exception if that fails.
The exception thrown is an integer, that corresponds to the errno returned by the failing
function. This enables a client to 'catch' the thrown exception, and print the corresponding
error message using strerror().
*/
This function also sets a timeout of 100ms for UDP sockets. */
void Client::create_socket() {
Sock::create_socket();
@@ -24,6 +24,12 @@ void Client::create_socket() {
if (this->sock_fd < 0) {
throw (this->sock_fd * -1);
}
// if (protocol == ES_UDP) {
// struct timeval tv;
// tv.tv_sec = 0;
// tv.tv_usec = 10000;
// setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// }
}
/* Sends given data to the peer socket. This method is overriden, because a TCP
@@ -39,11 +45,17 @@ void Client::sendAll(std::string to_send) {
/* Receives data from peer socket, and returns it. See above for better
explanation of why this method is overriden. */
std::string Client::recvAll() {
char* Client::recvAll() {
this->other_socket = this->sock_fd;
return Sock::recvAll();
}
/* Same as function above, but calls Sock::recvAllNB() */
char* Client::recvAllNB() {
this->other_socket = this->sock_fd;
return Sock::recvAllNB();
}
/* Returns the type of socket based on the global constants set in sock.hpp */
int Client::get_type() {
return SOCK_CLIENT;

View File

@@ -5,7 +5,7 @@
#include <cerrno>
#include <fcntl.h>
#ifndef _WIN_32
#ifndef _WIN32
const int INVALID_SOCKET = -1;
#endif
@@ -27,10 +27,8 @@ int sock_quit(void) {
}
/* Function to create a socket - Accepts IP version(4 or 6), protocol
type (PROTO_TCP or PROTO_UDP) and a flag to indicate whether the socket
should be set in blocking mode or not. This flag is ONLY FOR TCP. It does
nothing if the protocol is UDP.*/
int create_socket(int network, char transport, bool is_blocking) {
type (PROTO_TCP or PROTO_UDP). */
SOCKET create_socket(int network, char transport) {
sock_init();
int domain;
int type;
@@ -57,11 +55,6 @@ int create_socket(int network, char transport, bool is_blocking) {
int set_opt = 1;
setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, (char *)&set_opt, sizeof(set_opt));
if (is_blocking && transport == ES_TCP) {
int flags = fcntl(newSock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(newSock,F_SETFL,flags);
}
return newSock;
}
@@ -90,8 +83,8 @@ int create_addr(int network, char* address, int port,struct sockaddr* dest) {
}
int create_local (int network, char transport, char* address, int port,struct sockaddr* addr_struct, bool is_blocking) {
int socket = create_socket(network,transport, is_blocking);
SOCKET create_local (int network, char transport, char* address, int port,struct sockaddr* addr_struct) {
int socket = create_socket(network,transport);
if (socket < 0) {
return (-1 * errno);
}
@@ -116,7 +109,7 @@ int create_local (int network, char transport, char* address, int port,struct so
return socket;
}
int create_remote (int network,char transport,char* address,int port,struct sockaddr* remote_addr_struct, bool is_blocking) {
SOCKET create_remote (int network,char transport,char* address,int port,struct sockaddr* remote_addr_struct) {
struct addrinfo hints; /* Used to tell getaddrinfo what kind of address we want */
struct addrinfo* results; /* Used by getaddrinfo to store the addresses */
@@ -142,7 +135,7 @@ int create_remote (int network,char transport,char* address,int port,struct sock
create_addr(network,address,port,remote_addr_struct);
}
int socket = create_socket(network,transport, is_blocking);
int socket = create_socket(network,transport);
if (socket < 0) {
return (-1 * errno);
}

View File

@@ -21,7 +21,10 @@ public:
void sendAll(std::string to_send);
std::string recvAll();
char* recvAll();
/* Non-blocking receive */
char* recvAllNB();
/* Return the type of socket */
int get_type() override;

View File

@@ -1,10 +1,12 @@
#ifndef EASYSOCK_HPP_
#define EASYSOCK_HPP_
#ifdef _WIN_32
#ifdef _WIN32
#include <winsock2.h>
#include <Ws2tcpip.h>
#else
#include <winsock.h>
#include <ws2tcpip.h>
#endif
#ifdef linux
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@@ -12,7 +14,7 @@
#include <arpa/inet.h>
#endif
#ifndef _WIN_32
#ifndef _WIN32
typedef int SOCKET;
#endif
@@ -25,13 +27,12 @@ const char ES_UDP = 'U';
a layer 3 - network layer - integer, which must be '4' for IPv4
and 6 for IPv6;
a layer 4 - transport layer - character, which must be 'T' for
TCP or 'U' for UDP; and
a bool that indicates whether the socket should be blocking or non-blocking.
TCP or 'U' for UDP.
It creates a _blocking_ socket, and returns the created socket, or -1
if the socket creation failed.*/
It returns the created socket, or -1 if the socket creation failed.*/
SOCKET create_socket(int network, char transport, bool is_blocking = false);
SOCKET create_socket(int network, char transport);
/* This function fills in the sockaddr struct 'dest' based on the given information.
@@ -43,7 +44,7 @@ and dest is a pointer to the sockaddr struct that will be filled in.
The function returns with -202 if the network parameter contained neither '4'
nor '6'. */
SOCKET create_addr(int network, char* address, int port,struct sockaddr* dest);
int create_addr(int network, char* address, int port,struct sockaddr* dest);
@@ -53,7 +54,7 @@ same as above.
It prints the error returned by 'bind' if something went wrong, and returns ( -1 * errno ).*/
SOCKET create_local (int network, char transport, char* address, int port,struct sockaddr* addr_struct, bool is_blocking = false);
SOCKET create_local (int network, char transport, char* address, int port,struct sockaddr* addr_struct);
/* This function utilizes the same functions as 'create_local' but _connects_ to the
@@ -62,7 +63,7 @@ as above. This function needs an empty 'sockaddr *' structure passed to it, whic
If something goes wrong, this function returns with ( -1 * errno ). */
SOCKET create_remote (int network,char transport,char* address,int port,struct sockaddr* remote_addr_struct, bool is_blocking = false);
SOCKET create_remote (int network,char transport,char* address,int port,struct sockaddr* remote_addr_struct);
/* check_ip_ver - This function checks if the given string is an IPv4 address (returns 4),
IPv6 address (returns 6) or neither (returns -1). */

View File

@@ -25,6 +25,9 @@ public:
/* Increments the number of points that this paddle has earned */
void incrementPoints();
/* Sets the paddle position */
void setPosition(int x, int y);
/* Resets the paddle position */
void reset();

View File

@@ -30,10 +30,13 @@ public:
FOR UDP - Send data to the client, from which data was received.
FOR UDP, this function MUST be called after recvAll() */
void sendAll(std::string to_send);
/* Receive data from peer socket */
std::string recvAll();
char* recvAll();
/* Non-blocking receive */
char* recvAllNB();
/* Return the address of the peer */
std::string get_peer_addr();

View File

@@ -2,7 +2,13 @@
#define _SOCK_CLASS
#include <string>
#include <sys/socket.h>
#ifdef linux
#include <sys/socket.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
/* Global constants - can be used by children classes as return values, and by any clients to check what type the socket is of */
const int SOCK_CLIENT = 'C';
@@ -33,9 +39,15 @@ public:
/* Method to send data in 'to_send' through the 'other_socket' socket */
void sendAll(std::string to_send);
/* Same as method above, with buffer and buffer size */
void sendAll(char* buffer, int size);
/* Method to receive data sent to the 'other_socket' socket */
std::string recvAll();
char* recvAll();
/* Non-blocking receive method - calls the method above after polling for data */
char* recvAllNB();
/* Returns socket identifier */
int getSockFD();

156
main.cpp
View File

@@ -1,3 +1,18 @@
#if defined(_WIN32)
#define NOGDI // All GDI defines and routines
#define NOUSER // All USER defines and routines
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // or any library that uses Windows.h
#endif
#if defined(_WIN32) // raylib uses these names as function parameters
#undef near
#undef far
#endif
#include <iostream>
#include <cmath>
#include <cstring>
@@ -25,6 +40,7 @@ const float BASE_BOUNCE_DEG = 45;
const float BASE_BOUNCE_RAD = (BASE_BOUNCE_DEG / 180.0) * M_PI;
const float BASE_SPEED_COMPONENTS = 18;
const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2);
typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode;
/* This struct contains a Mode enum, which indicates the type of game we are
@@ -135,11 +151,13 @@ GameType check_server_client(int argc, char** argv) {
Server* server = new Server(4, ES_UDP, addr.data(), port);
server->create_socket();
std::cout << "Waiting for connection..." << std::endl;
std::string response;
/* Wait for the right client to connect */
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 {
response = server->recvAll();
} while (response != "GG");
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");
@@ -193,15 +211,15 @@ int main(int argc, char** argv) {
SetTraceLogLevel(LOG_NONE);
raylib::Window window = raylib::Window(WIDTH, HEIGHT, "Pong");
window.ClearBackground(BLACK);
SetTargetFPS(5);
SetTargetFPS(10);
SetExitKey(KEY_Q);
std::string points_str = std::string("0\t\t0");
bool game_started = false;
srand(std::time(NULL));
/* Variables to store the response given by the other player */
/* Variable to store the response given by the other player */
std::string response;
std::istringstream response_stream;
Serial_Data response_data;
/* Vector to store peer paddle position */
raylib::Vector2 peer_pos;
@@ -228,7 +246,9 @@ int main(int argc, char** argv) {
if ((type.mode == M_SERVER || type.mode == M_SINGLE) && IsKeyDown(KEY_SPACE)) {
game_started = true;
/* Send a start message to the client */
type.netsock->sendAll("S");
if (type.mode == M_SERVER) {
type.netsock->sendAll("S");
}
}
/* For client (wait for start message from server) */
@@ -244,87 +264,84 @@ int main(int argc, char** argv) {
if (game_started) {
/* Serialize the data that we need to send, and then send it to the peer paddle */
if (type.mode == M_SERVER) {
/* Serial_create_data creates a Serial_Data struct from our values, and Serial_serialize serializes it.
The 'sendAll' function accepts an std::string, so the byte array (uint8_t *) has to be cast to (char *), then converted to an std::string */
to_send_data = Serial_create_data(pad1.getRect().x, pad1.getRect().y, ball.pos.x, ball.pos.y);
/* Serial_create_data creates a Serial_Data struct from our values, and Serial_serialize serializes it Paddle 2 is controled by the server, Paddle 1, by the client.*/
to_send_data = Serial_create_data(pad2.getRect().x, pad2.getRect().y, ball.pos.x, ball.pos.y);
}
if (type.mode == M_CLIENT) {
else if (type.mode == M_CLIENT) {
/* The _server_ is the authoritative peer for the ball position, so the client sends (0, 0) as the ball position instead of actually sending a position */
to_send_data = Serial_create_data(pad2.getRect().x, pad2.getRect().y, 0, 0);
to_send_data = Serial_create_data(pad1.getRect().x, pad1.getRect().y, 0, 0);
}
to_send_string = std::string((char *)Serial_serialize(to_send_data));
type.netsock->sendAll(to_send_string);
/* Create a stream from the response of the server, and use that to create the vector of peer position */
response = type.netsock->recvAll();
if (response[0] == 'P') {
/* Create a stream, excluding the first character */
response_stream = std::istringstream(response.substr(1));
response_stream >> peer_pos.x >> peer_pos.y;
std::cout << "X position of peer is: " << peer_pos.x << " Y position is: " << peer_pos.y << std::endl;
/* Only send and receive data if the game is not in single player mode */
if (type.mode != M_SINGLE) {
type.netsock->sendAll((char *)Serial_serialize(to_send_data), 9);
/* Create Serial_data struct from the response of the server. Since recvAll returns a char*, we need to convert it to a byte array */
uint8_t* response_array = (uint8_t *)(type.netsock->recvAll());
if (response_array != NULL) {
/* If the response is NULL, that means it timed-out. In this case, there's no value to print */
response_data = Serial_deserialize(response_array);
std::cout << response_data.pad_x << "\t" << response_data.pad_y << "\t" << response_data.ball_x << "\t" << response_data.ball_y << std::endl;
} else {
std::cout << "NOTHING RECEIVED" << std::endl;
}
}
/* Since I have already received the data, I need to write code to handle it, in case it isn't 'P' */
/* When updating the paddle positions, update the peer paddle's positions based on the vector set earlier */
std::string to_send = "";
/* Update paddle velocity */
/* Left paddle (controlled by client) */
/* Up motion */
if (IsKeyPressed(KEY_S) || response == "D1") {
/* Left paddle (controlled by client) - I use type.mode != M_SERVER, because I also want the single player
mode to be able to control the paddle. Therefore, the only mode that _can't_ control the paddle is the server
mode. */
/* Down motion */
if (IsKeyPressed(KEY_S) && type.mode != M_SERVER) {
pad1.velocity.y = PADDLE_SPEED; /* Set positive (downward) velocity, since (0,0) is top-left */
if (type.mode == M_CLIENT) { /* If this machine controls this paddle, Send the key press to the other machine */
to_send = "D1";
}
}
/* Down motion */
if (IsKeyPressed(KEY_W) || response == "U1") {
/* Up motion */
if (IsKeyPressed(KEY_W) && type.mode != M_SERVER) {
pad1.velocity.y = (-1) * PADDLE_SPEED; /* Set negative (upward) velocity */
if (type.mode == M_CLIENT) {
to_send = "U1";
}
}
/* Stop */
if ((IsKeyReleased(KEY_S) || IsKeyReleased(KEY_W)) || (response == "S1")) {
if (((IsKeyReleased(KEY_S) || IsKeyReleased(KEY_W))) && (type.mode != M_SERVER)) {
pad1.velocity.y = 0;
if (type.mode == M_CLIENT) {
to_send = "S1";
}
}
/* Right paddle - controlled by server*/
if (IsKeyPressed(KEY_DOWN)) {
if (type.mode == M_SERVER) {
type.netsock->sendAll(std::string("D"));
}
/* Right paddle - controlled by server - See above for why I used '!= M_CLIENT' instead of '== M_SERVER' */
/* Down */
if (IsKeyPressed(KEY_DOWN) && type.mode != M_CLIENT) {
pad2.velocity.y = PADDLE_SPEED;
}
if (IsKeyPressed(KEY_UP)) {
if (type.mode == M_SERVER) {
type.netsock->sendAll(std::string("U"));
}
/* Up */
if (IsKeyPressed(KEY_UP) && type.mode != M_CLIENT) {
pad2.velocity.y = (-1) * PADDLE_SPEED;
}
if (IsKeyReleased(KEY_UP) || IsKeyReleased(KEY_DOWN)) {
if (type.mode == M_SERVER) {
type.netsock->sendAll(std::string("S"));
}
/* Stop */
if ((IsKeyReleased(KEY_UP) || IsKeyReleased(KEY_DOWN)) && type.mode != M_CLIENT) {
pad2.velocity.y = 0;
}
/* Update ball velocity based on collision detection */
if (pad1.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 1 */
ball.pos.x = pad1.getRect().x + pad1.getRect().GetWidth() + ball.radius + 1; /* Ensuring that the ball doesn't get stuck inside the paddle */
ball.vel = changeVelocityAfterCollision(pad1, ball);
/* The client should set the ball position based on the data sent by the server. It doesn't have to do any calculations of its own. */
if (type.mode != M_CLIENT) {
/* Update ball velocity based on collision detection */
if (pad1.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 1 */
ball.pos.x = pad1.getRect().x + pad1.getRect().GetWidth() + ball.radius + 1; /* Ensuring that the ball doesn't get stuck inside the paddle */
ball.vel = changeVelocityAfterCollision(pad1, ball);
}
if (pad2.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 2 */
ball.pos.x = pad2.getRect().x - ball.radius - 1;
ball.vel = changeVelocityAfterCollision(pad2, ball);
}
} else {
ball.setPosition(raylib::Vector2(response_data.ball_x, response_data.ball_y));
}
if (pad2.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 2 */
ball.pos.x = pad2.getRect().x - ball.radius - 1;
ball.vel = changeVelocityAfterCollision(pad2, ball);
}
if (ball.pos.x + ball.radius >= window.GetWidth()) { /* Collision with right wall */
pad1.incrementPoints();
game_started = false;
@@ -349,11 +366,18 @@ int main(int argc, char** argv) {
ball.vel.y = ball.vel.y * -1;
}
/* Update positions based on velocities */
pad1.updatePosition();
pad2.updatePosition();
ball.updatePosition();
/* Update positions based on velocities - Client only updates pad1, server updates pad2 and ball */
if (type.mode != M_CLIENT) {
ball.updatePosition();
pad2.updatePosition();
} else {
pad2.setPosition(response_data.pad_x, response_data.pad_y);
}
if (type.mode != M_SERVER) {
pad1.updatePosition();
} else {
pad1.setPosition(response_data.pad_x, response_data.pad_y);
}
}
/* Draw objects */

View File

@@ -1,8 +1,11 @@
project('Pong', ['cpp', 'c'])
add_global_arguments('-g', '-Wall', '-pedantic', '-Wno-unused-function', '-Werror', language : ['cpp', 'c'])
raylib = dependency('raylib')
raylib = dependency('raylib', native: true)
static_lib_path = ''
executable('pong',
'main.cpp', 'easysock.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp',
'serialization.c',
dependencies: raylib,
dependencies: raylib,
)

View File

@@ -22,6 +22,12 @@ void Paddle::incrementPoints() {
return;
}
void Paddle::setPosition(int x, int y) {
this->rectangle.x = x;
this->rectangle.y = y;
return;
}
void Paddle::reset() {
this->rectangle.x = this->initial_pos.x;
this->rectangle.y = this->initial_pos.y;

View File

@@ -1,7 +1,15 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef linux
#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) {
Serial_Data data;
@@ -13,20 +21,37 @@ Serial_Data Serial_create_data(float pad_x, float pad_y, float ball_x, float bal
return data;
}
/* Serializes a 'Data' struct into a byte array */
/* 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));
/* Store the data into the pointer, by using an incremented memory address for each successive store */
*serialized = data.pad_x;
*(serialized + sizeof(uint16_t)) = data.pad_y;
*(serialized + 2 * sizeof(uint16_t)) = data.ball_x;
*(serialized + 3 * sizeof(uint16_t)) = data.ball_y;
uint8_t* serialized = malloc(4 * sizeof(uint16_t) + 1);
uint8_t* pad_x_ptr;
uint8_t* pad_y_ptr;
uint8_t* ball_x_ptr;
uint8_t* ball_y_ptr;
memset(serialized, 0, 4 * sizeof(uint16_t) + 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);
*((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));
*(ball_y_ptr + sizeof(uint16_t)) = '\0';
return serialized;
}
/* Deserialize a byte array into a 'Data' struct */
/* 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 */
@@ -37,10 +62,17 @@ Serial_Data Serial_deserialize(uint8_t* serialized) {
/* Dereference (and cast) the pointers, and store them into the struct */
deserialized.pad_x = *((uint16_t *)pad_x_ptr);
deserialized.pad_y = *((uint16_t *)pad_y_ptr);
deserialized.ball_x = *((uint16_t *)ball_x_ptr);
deserialized.ball_y = *((uint16_t *)ball_y_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);
return deserialized;
}

View File

@@ -1,4 +1,9 @@
#include <sys/socket.h>
#ifdef linux
#include <sys/socket.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
#include <fcntl.h>
#include "includes/sock.hpp"
#include "includes/server.hpp"
@@ -15,7 +20,6 @@ Server::~Server() {
/* Sends given data through the peer socket - This method is overriden from the
base method, because a different socket must be used. In the server's case, the
'peer' socket i.e. the socket returned after calling 'accept', must be used. */
void Server::sendAll(std::string to_send) {
Sock::sendAll(to_send);
}
@@ -28,13 +32,36 @@ of a peer socket, and so the regular server socket (the one created in create_so
is used instead. This function also sets the 'peer_addr' string to the address of the
peer socket, handling both TCP and UDP. */
std::string Server::recvAll() {
char* Server::recvAll() {
if (this->protocol == ES_UDP) {
this->other_socket = this->sock_fd;
}
/* Call receive method of parent */
std::string to_return = Sock::recvAll();
char* to_return = Sock::recvAll();
/* Set the peer address of the socket */
if (this->ip_ver == 4) {
/* FOR IPv4 */
struct sockaddr_in* temp_struct = (struct sockaddr_in*)this->dest;
/* 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";
}
return to_return;
}
/* Same as function above, but calls Sock::recvAllNB() instead */
char* Server::recvAllNB() {
if (this->protocol == ES_UDP) {
this->other_socket = this->sock_fd;
}
/* Call receive method of parent */
char* to_return = Sock::recvAllNB();
/* Set the peer address of the socket */
if (this->ip_ver == 4) {
@@ -69,7 +96,8 @@ 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(). */
'catching' the thrown exception and using strerror().
This function also sets a timeout of 100ms for UDP sockets */
void Server::create_socket() {
Sock::create_socket();
@@ -80,6 +108,12 @@ void Server::create_socket() {
if (protocol == ES_TCP) {
listen(sock_fd, 10);
}
// if (protocol == ES_UDP) {
// struct timeval tv;
// tv.tv_sec = 0;
// tv.tv_usec = 10000;
// setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// }
}
/* Returns the address of the peer socket as a string, can be used for debugging */

View File

@@ -64,30 +64,39 @@ void Sock::sendAll(std::string to_send) {
total_bytes_sent += num_bytes_sent;
}
}
return;
}
/* Receives data from 'other_socket' into a string, and returns that string. For TCP, the
'recv' method is called until all the data has been read. For UDP, the 'recvfrom'
method is only called once.
This function also needs more testing for TCP. */
/* This method receives a (char *) and a size, and creates a std::string with it.
It then calls the method above, passing that string as a parameter. */
void Sock::sendAll(char* buffer, int size) {
std::string to_send = std::string(buffer, size);
sendAll(to_send);
}
std::string Sock::recvAll() {
int num_bytes_received;
std::string string = std::string();
/* Receives data from 'other_socket' into a char *, and returns that char *. For TCP, the
'recv' method is called until all the data has been read. For UDP, the 'recvfrom'
method is only called once. The 'select' function is used to poll data for UDP
This function also needs more testing for TCP. */
char* Sock::recvAll() {
int num_bytes_received = 0;
int total_bytes_received = 0;
char* buffer = (char *)malloc(100);
bool has_been_read = false;
if (this->protocol == ES_UDP) {
num_bytes_received = recvfrom(this->sock_fd, buffer, 100, 0, dest, &addrlen);
num_bytes_received = recvfrom(this->sock_fd, buffer, 99, 0, dest, &addrlen);
if (num_bytes_received == 0) {
return NULL;
}
/* Null-terminate the string */
*(buffer + num_bytes_received) = '\0';
string.append(std::string(buffer));
return buffer;
}
/* For TCP sockets */
else {
while ((num_bytes_received = recv(this->other_socket, buffer, 100, 0)) != 0) {
while ((num_bytes_received = recv(this->other_socket, buffer + total_bytes_received, 100 - total_bytes_received, 0)) != 0) {
if ((errno == EAGAIN || errno == EWOULDBLOCK)) {
if (has_been_read) {
@@ -100,13 +109,31 @@ std::string Sock::recvAll() {
if (num_bytes_received < 0) {
throw errno * -1;
}
string.append(std::string(buffer));
total_bytes_received += num_bytes_received;
has_been_read = true;
}
}
return string;
return buffer;
}
/* Non-blocking recv call - Uses 'select' to poll for data from the FD. */
char* Sock::recvAllNB() {
struct timeval tv;
fd_set readfs;
tv.tv_sec = 0; // Set to 0 to poll instead of wait
tv.tv_usec = 0; // Set to 0 to poll instead of wait
FD_ZERO(&readfs);
FD_SET(this->sock_fd, &readfs);
select(this->sock_fd + 1, &readfs, NULL, NULL, &tv);
if (FD_ISSET(this->sock_fd, &readfs)) {
return Sock::recvAll();
} else {
return NULL;
}
}
int Sock::getSockFD() {