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:
		
							
								
								
									
										141
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								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,87 +249,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 +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