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_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 +136,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 +196,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 +231,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,85 +249,82 @@ 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);
}
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);
/* 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 (ball.pos.x + ball.radius >= window.GetWidth()) { /* Collision with right wall */
@ -349,11 +351,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 */

Loading…
Cancel
Save