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.
master
Aadhavan Srinivasan 10 months ago
parent effeea73b9
commit ba667d020d

@ -25,6 +25,7 @@ const float BASE_BOUNCE_DEG = 45;
const float BASE_BOUNCE_RAD = (BASE_BOUNCE_DEG / 180.0) * M_PI; const float BASE_BOUNCE_RAD = (BASE_BOUNCE_DEG / 180.0) * M_PI;
const float BASE_SPEED_COMPONENTS = 18; const float BASE_SPEED_COMPONENTS = 18;
const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2); const float BASE_SPEED = sqrt(powf(BASE_SPEED_COMPONENTS, 2) * 2);
typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode; typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode;
/* This struct contains a Mode enum, which indicates the type of game we are /* This struct contains a Mode enum, which indicates the type of game we are
@ -135,11 +136,13 @@ GameType check_server_client(int argc, char** argv) {
Server* server = new Server(4, ES_UDP, addr.data(), port); Server* server = new Server(4, ES_UDP, addr.data(), port);
server->create_socket(); server->create_socket();
std::cout << "Waiting for connection..." << std::endl; std::cout << "Waiting for connection..." << std::endl;
std::string response; std::string response = "";
/* Wait for the right client to connect */ 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 { do {
response = server->recvAll(); temp_response = server->recvAll();
} while (response != "GG"); } while (temp_response == NULL);
response = std::string(temp_response);
std::cout << "Connection received from " << server->get_peer_addr() << std::endl; std::cout << "Connection received from " << server->get_peer_addr() << std::endl;
server->sendAll("U2"); server->sendAll("U2");
@ -193,15 +196,15 @@ int main(int argc, char** argv) {
SetTraceLogLevel(LOG_NONE); SetTraceLogLevel(LOG_NONE);
raylib::Window window = raylib::Window(WIDTH, HEIGHT, "Pong"); raylib::Window window = raylib::Window(WIDTH, HEIGHT, "Pong");
window.ClearBackground(BLACK); window.ClearBackground(BLACK);
SetTargetFPS(5); SetTargetFPS(10);
SetExitKey(KEY_Q); SetExitKey(KEY_Q);
std::string points_str = std::string("0\t\t0"); std::string points_str = std::string("0\t\t0");
bool game_started = false; bool game_started = false;
srand(std::time(NULL)); 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::string response;
std::istringstream response_stream; Serial_Data response_data;
/* Vector to store peer paddle position */ /* Vector to store peer paddle position */
raylib::Vector2 peer_pos; raylib::Vector2 peer_pos;
@ -228,8 +231,10 @@ int main(int argc, char** argv) {
if ((type.mode == M_SERVER || type.mode == M_SINGLE) && IsKeyDown(KEY_SPACE)) { if ((type.mode == M_SERVER || type.mode == M_SINGLE) && IsKeyDown(KEY_SPACE)) {
game_started = true; game_started = true;
/* Send a start message to the client */ /* Send a start message to the client */
if (type.mode == M_SERVER) {
type.netsock->sendAll("S"); type.netsock->sendAll("S");
} }
}
/* For client (wait for start message from server) */ /* For client (wait for start message from server) */
if (type.mode == M_CLIENT) { if (type.mode == M_CLIENT) {
@ -244,77 +249,71 @@ int main(int argc, char** argv) {
if (game_started) { if (game_started) {
/* Serialize the data that we need to send, and then send it to the peer paddle */ /* Serialize the data that we need to send, and then send it to the peer paddle */
if (type.mode == M_SERVER) { if (type.mode == M_SERVER) {
/* Serial_create_data creates a Serial_Data struct from our values, and Serial_serialize serializes it. /* 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.*/
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(pad2.getRect().x, pad2.getRect().y, ball.pos.x, ball.pos.y);
to_send_data = Serial_create_data(pad1.getRect().x, pad1.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 */ /* 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 */ /* When updating the paddle positions, update the peer paddle's positions based on the vector set earlier */
std::string to_send = ""; std::string to_send = "";
/* Update paddle velocity */ /* Update paddle velocity */
/* Left paddle (controlled by client) */ /* Left paddle (controlled by client) - I use type.mode != M_SERVER, because I also want the single player
/* Up motion */ mode to be able to control the paddle. Therefore, the only mode that _can't_ control the paddle is the server
if (IsKeyPressed(KEY_S) || response == "D1") { 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 */ 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 */ /* Up motion */
if (IsKeyPressed(KEY_W) || response == "U1") { if (IsKeyPressed(KEY_W) && type.mode != M_SERVER) {
pad1.velocity.y = (-1) * PADDLE_SPEED; /* Set negative (upward) velocity */ pad1.velocity.y = (-1) * PADDLE_SPEED; /* Set negative (upward) velocity */
if (type.mode == M_CLIENT) {
to_send = "U1";
}
} }
/* Stop */ /* 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; pad1.velocity.y = 0;
if (type.mode == M_CLIENT) {
to_send = "S1";
}
} }
/* Right paddle - controlled by server*/
if (IsKeyPressed(KEY_DOWN)) { /* Right paddle - controlled by server - See above for why I used '!= M_CLIENT' instead of '== M_SERVER' */
if (type.mode == M_SERVER) { /* Down */
type.netsock->sendAll(std::string("D")); if (IsKeyPressed(KEY_DOWN) && type.mode != M_CLIENT) {
}
pad2.velocity.y = PADDLE_SPEED; pad2.velocity.y = PADDLE_SPEED;
} }
if (IsKeyPressed(KEY_UP)) {
if (type.mode == M_SERVER) { /* Up */
type.netsock->sendAll(std::string("U")); if (IsKeyPressed(KEY_UP) && type.mode != M_CLIENT) {
}
pad2.velocity.y = (-1) * PADDLE_SPEED; pad2.velocity.y = (-1) * PADDLE_SPEED;
} }
if (IsKeyReleased(KEY_UP) || IsKeyReleased(KEY_DOWN)) {
if (type.mode == M_SERVER) { /* Stop */
type.netsock->sendAll(std::string("S")); if ((IsKeyReleased(KEY_UP) || IsKeyReleased(KEY_DOWN)) && type.mode != M_CLIENT) {
}
pad2.velocity.y = 0; 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 */ /* Update ball velocity based on collision detection */
if (pad1.getRect().CheckCollision(ball.pos, ball.radius)) { /* Collision with paddle 1 */ 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.pos.x = pad1.getRect().x + pad1.getRect().GetWidth() + ball.radius + 1; /* Ensuring that the ball doesn't get stuck inside the paddle */
@ -324,6 +323,9 @@ int main(int argc, char** argv) {
ball.pos.x = pad2.getRect().x - ball.radius - 1; ball.pos.x = pad2.getRect().x - ball.radius - 1;
ball.vel = changeVelocityAfterCollision(pad2, ball); 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 */ if (ball.pos.x + ball.radius >= window.GetWidth()) { /* Collision with right wall */
pad1.incrementPoints(); pad1.incrementPoints();
@ -349,11 +351,18 @@ int main(int argc, char** argv) {
ball.vel.y = ball.vel.y * -1; ball.vel.y = ball.vel.y * -1;
} }
/* Update positions based on velocities */ /* Update positions based on velocities - Client only updates pad1, server updates pad2 and ball */
pad1.updatePosition(); if (type.mode != M_CLIENT) {
pad2.updatePosition();
ball.updatePosition(); 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 */ /* Draw objects */

Loading…
Cancel
Save