Compare commits
10 Commits
6d1565a020
...
ff8dc1f1f7
| Author | SHA1 | Date | |
|---|---|---|---|
| ff8dc1f1f7 | |||
| 4b3d5387a1 | |||
| ddbbc322a6 | |||
| b90d37c73f | |||
| 69e70eb206 | |||
| 349b0b78db | |||
| a37ec79f09 | |||
| ba667d020d | |||
| effeea73b9 | |||
| c6bbe82d25 |
11
Makefile
11
Makefile
@@ -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
|
||||
16
client.cpp
16
client.cpp
@@ -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;
|
||||
|
||||
21
easysock.cpp
21
easysock.cpp
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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). */
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ public:
|
||||
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();
|
||||
|
||||
@@ -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';
|
||||
@@ -34,8 +40,14 @@ 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();
|
||||
|
||||
132
main.cpp
132
main.cpp
@@ -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,8 +246,10 @@ 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 */
|
||||
if (type.mode == M_SERVER) {
|
||||
type.netsock->sendAll("S");
|
||||
}
|
||||
}
|
||||
|
||||
/* For client (wait for start message from server) */
|
||||
if (type.mode == M_CLIENT) {
|
||||
@@ -244,77 +264,71 @@ 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);
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
@@ -324,6 +338,9 @@ int main(int argc, char** argv) {
|
||||
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 (ball.pos.x + ball.radius >= window.GetWidth()) { /* Collision with right wall */
|
||||
pad1.incrementPoints();
|
||||
@@ -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();
|
||||
/* 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 */
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,9 +62,16 @@ 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_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;
|
||||
}
|
||||
|
||||
44
server.cpp
44
server.cpp
@@ -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 */
|
||||
|
||||
51
sock.cpp
51
sock.cpp
@@ -68,26 +68,35 @@ void Sock::sendAll(std::string to_send) {
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user