@ -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 \t 0 " ) ;
bool game_started = false ;
srand ( std : : time ( NULL ) ) ;
/* Variable s 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 ( pad 2. getRect ( ) . x , pad2 . getRect ( ) . y , 0 , 0 ) ;
to_send_data = Serial_create_data ( pad 1. 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 */