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.
This commit is contained in:
137
main.cpp
137
main.cpp
@@ -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);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
Reference in New Issue
Block a user