diff --git a/Spacewar-Client.c b/Spacewar-Client.c deleted file mode 100644 index 6da32f1..0000000 --- a/Spacewar-Client.c +++ /dev/null @@ -1,328 +0,0 @@ -// Spacewar Client, Barra Ó Catháin. -// =================================== -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xyVector.h" -#include "spacewarPlayer.h" -#include "spacewarGraphics.h" - -int main(int argc, char ** argv) -{ - SDL_Event event; - bool quit = false, rotatingClockwise = false, rotatingAnticlockwise = false, accelerating = false; - int width = 0, height = 0; - uint32_t rendererFlags = SDL_RENDERER_ACCELERATED; - uint64_t thisFrameTime = SDL_GetPerformanceCounter(), lastFrameTime = 0; - long positionX = 512, positionY = 512, starPositionX = 0, starPositionY = 0; - xyVector positionVector = {512, 512}, velocityVector = {1, 0}, gravityVector = {0, 0}, - engineVector = {0.04, 0}, upVector = {0, 0.1}, starPosition = {0, 0}; - - // Create the socket: - int receiveSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (receiveSocket < 0) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - printf("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n"); - - // Make the socket timeout: - struct timeval read_timeout; - read_timeout.tv_sec = 0; - read_timeout.tv_usec = 8; - setsockopt(receiveSocket, SOL_SOCKET, SO_RCVTIMEO, &read_timeout, sizeof(read_timeout)); - - // Create and fill the information needed to bind to the socket: - struct sockaddr_in serverAddress; - memset(&serverAddress, 0, sizeof(serverAddress)); - serverAddress.sin_family = AF_INET; // IPv4 - serverAddress.sin_addr.s_addr = INADDR_ANY; - serverAddress.sin_port = htons(12000); - - // Bind to the socket: - if (bind(receiveSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0) - { - perror("bind failed"); - exit(EXIT_FAILURE); - } - - // Create the socket: - int sendSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (sendSocket < 0) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - printf("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n"); - - - // Create and fill the information needed to bind to the socket: - struct sockaddr_in sendAddress; - memset(&sendAddress, 0, sizeof(sendAddress)); - sendAddress.sin_family = AF_INET; // IPv4 - sendAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); - sendAddress.sin_port = htons(12001); - - // Get the initial - ship shipA; - ship shipB; - - // Initialize the SDL library, video, sound, and input: - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) - { - printf("SDL Initialization Error: %s\n", SDL_GetError()); - } - - // Initialize image loading: - IMG_Init(IMG_INIT_PNG); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); - - // Initialize font support: - TTF_Init(); - - // Create an SDL window and rendering context in that window: - SDL_Window * window = SDL_CreateWindow("SDL_TEST", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 450, 0); - SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, rendererFlags); - SDL_SetWindowTitle(window, "Spacewar!"); - - // Load in all of our textures: - SDL_Texture * idleTexture, * acceleratingTexture, * clockwiseTexture, * anticlockwiseTexture, * currentTexture, - * acceleratingTexture2, * blackHoleTexture; - - idleTexture = IMG_LoadTexture(renderer, "./Images/Ship-Idle.png"); - clockwiseTexture = IMG_LoadTexture(renderer, "./Images/Ship-Clockwise.png"); - acceleratingTexture = IMG_LoadTexture(renderer, "./Images/Ship-Accelerating.png"); - anticlockwiseTexture = IMG_LoadTexture(renderer, "./Images/Ship-Anticlockwise.png"); - acceleratingTexture2 = IMG_LoadTexture(renderer, "./Images/Ship-Accelerating-Frame-2.png"); - - // Load the starfield texture: - SDL_Texture * starfieldTexture = IMG_LoadTexture(renderer, "./Images/Starfield.png"); - SDL_Rect starfieldRect; - SDL_QueryTexture(starfieldTexture, NULL, NULL, NULL, &starfieldRect.h); - SDL_QueryTexture(starfieldTexture, NULL, NULL, &starfieldRect.w, NULL); - - blackHoleTexture = IMG_LoadTexture(renderer, "./Images/Black-Hole.png"); - SDL_Rect blackHoleRectangle; - blackHoleRectangle.x = 0; - blackHoleRectangle.y = 0; - SDL_QueryTexture(blackHoleTexture, NULL, NULL, NULL, &blackHoleRectangle.h); - SDL_QueryTexture(blackHoleTexture, NULL, NULL, &blackHoleRectangle.w, NULL); - - - // Enable resizing the window: - SDL_SetWindowResizable(window, SDL_TRUE); - ship Temp; - playerController playerOne; - playerOne.number = 1; - bool shipAUpdated, shipBUpdated; - - TTF_Font * font = TTF_OpenFont("./Robtronika.ttf", 12); - SDL_Color white = {255, 255, 255}; - int keyCount = 0; - const uint8_t * keyboardState = SDL_GetKeyboardState(&keyCount); - - // Prep the titlescreen struct: - SpacewarTitlescreen titlescreen = prepareTitleScreen(window, renderer, "./Images/Starfield.png", - "./Images/Title.png", font, - "Press Enter or Button 0 on your joystick to connect!"); - - // Load all joysticks: - int joystickListLength = SDL_NumJoysticks(); - SDL_Joystick ** joysticksList = calloc(joystickListLength, sizeof(SDL_Joystick*)); - - for(int index = 0; index < SDL_NumJoysticks(); index++) - { - joysticksList[index] = SDL_JoystickOpen(index); - } - - // Choose a player joystick: - printf("Please press button zero on the controller you wish to use, or enter to play keyboard only.\n"); - - int joystickIndex = 0; - bool inputSelected = false; - - // Render the title text: - while(!inputSelected) - { - // Draw the title screen according to the titlescreen struct: - drawTitleScreen(&titlescreen); - - SDL_PumpEvents(); - SDL_GetKeyboardState(&keyCount); - if(keyboardState[SDL_SCANCODE_RETURN] == 1) - { - joysticksList[joystickIndex] = NULL; - inputSelected = true; - } - joystickIndex++; - if(joystickIndex >= joystickListLength) - { - joystickIndex = 0; - } - if(SDL_JoystickGetButton(joysticksList[joystickIndex], 0) == 1) - { - inputSelected = true; - } - - // Delay enough so that we run at 30 frames in the menu: - SDL_Delay(1000 / 30); - } - - // Load joystick - playerOne.joystick = joysticksList[joystickIndex]; - if (playerOne.joystick == NULL ) - { - printf( "Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError() ); - } - playerOne.haptic = SDL_HapticOpenFromJoystick(playerOne.joystick); - SDL_HapticRumbleInit(playerOne.haptic); - - while (!quit) - { - while(!(shipAUpdated && shipBUpdated)) - { - // Receive data from the socket: - recvfrom(receiveSocket, &Temp, sizeof(ship), 0, NULL, NULL); - if(Temp.number == 0) - { - shipA = Temp; - shipAUpdated = true; - } - if(Temp.number == 1) - { - shipB = Temp; - shipBUpdated = true; - } - } - - // Check if the user wants to quit: - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - { - quit = true; - break; - } - case SDL_KEYDOWN: - { - switch (event.key.keysym.sym) - { - case SDLK_LEFT: - { - playerOne.turningAnticlockwise = true; - break; - } - case SDLK_RIGHT: - { - playerOne.turningClockwise = true; - break; - } - case SDLK_UP: - { - playerOne.accelerating = true; - break; - } - default: - { - break; - } - } - break; - } - case SDL_KEYUP: - { - switch (event.key.keysym.sym) - { - case SDLK_LEFT: - { - playerOne.turningAnticlockwise = false; - break; - } - case SDLK_RIGHT: - { - playerOne.turningClockwise = false; - break; - } - case SDLK_UP: - { - playerOne.accelerating = false; - break; - } - } - } - default: - { - break; - } - break; - } - } - sendto(sendSocket, &playerOne, sizeof(playerOne), 0, (const struct sockaddr *)&sendAddress, sizeof(sendAddress)); - // Store the window's current width and height: - SDL_GetWindowSize(window, &width, &height); - - // Set the texture to idle: - currentTexture = idleTexture; - - // Calculate the position of the sprites: - shipB.rectangle.x = (width/2) - 16 - (shipB.velocity.xComponent * 15); - shipB.rectangle.y = (height/2) - 16 - (shipB.velocity.yComponent * 15); - - shipA.rectangle.x = (long)((((shipA.position.xComponent - shipB.position.xComponent) - 32) + width/2) - (shipB.velocity.xComponent * 15)); - shipA.rectangle.y = (long)((((shipA.position.yComponent - shipB.position.yComponent) - 32) + height/2) - (shipB.velocity.yComponent * 15)); - - // Set the colour to black: - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - - // Clear the screen, filling it with black: - SDL_RenderClear(renderer); - - // Draw the starfield: - starfieldRect.x = -900 - (long)shipB.position.xComponent % 800 - (shipB.velocity.xComponent * 15); - starfieldRect.y = -900 - (long)shipB.position.yComponent % 800 - (shipB.velocity.yComponent * 15); - while(starfieldRect.x <= (width + 800)) - { - while(starfieldRect.y <= (height + 800)) - { - SDL_RenderCopy(renderer, starfieldTexture, NULL, &starfieldRect); - starfieldRect.y += 800; - } - starfieldRect.y = -900 - (long)shipB.position.yComponent % 800 - (shipB.velocity.yComponent * 15); - starfieldRect.x += 800; - } - - // Draw the ship: - SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipA.rectangle, - angleBetweenVectors(&shipA.engine, &upVector) + 90, NULL, 0); - SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipB.rectangle, - angleBetweenVectors(&shipB.engine, &upVector) + 90, NULL, 0); - - // Calculate the position of the black hole on screen and render it: - blackHoleRectangle.x = ((long)(starPositionX - shipB.position.xComponent - (blackHoleRectangle.w / 2)) + width/2) - - (shipB.velocity.xComponent * 15); - blackHoleRectangle.y = ((long)(starPositionY - shipB.position.yComponent - (blackHoleRectangle.h / 2)) + height/2) - - (shipB.velocity.yComponent * 15); - SDL_RenderCopy(renderer, blackHoleTexture, NULL, &blackHoleRectangle); - - // Present the rendered graphics: - SDL_RenderPresent(renderer); - shipAUpdated = false; - shipBUpdated = false; - } - return 0; -} -// ======================================================================================================== -// Local Variables: -// compile-command: "gcc `sdl2-config --libs --cflags` Spacewar-Client.c spacewarPlayer.c spacewarGraphics.c -lSDL2_image -lSDL2_ttf -lm -o 'Spacewar Client!'" -// End: diff --git a/Spacewar.c b/Spacewar.c deleted file mode 100644 index 3b0b778..0000000 --- a/Spacewar.c +++ /dev/null @@ -1,363 +0,0 @@ -// Spacewar, Barra Ó Catháin. -// =================================== -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xyVector.h" -#include "spacewarPlayer.h" -#include "spacewarGraphics.h" - -int main(int argc, char ** argv) -{ - SDL_Event event; - int width = 0, height = 0; - uint32_t rendererFlags = SDL_RENDERER_ACCELERATED; - uint64_t thisFrameTime = SDL_GetPerformanceCounter(), lastFrameTime = 0; - long starPositionX = 0, starPositionY = 0; - double deltaTime = 0, frameAccumulator = 0; - bool quit = false, rotatingClockwise = false, rotatingAnticlockwise = false, accelerating = false; - xyVector engineVector = {0.85, 0}, upVector = {0, 0.1}, starPosition = {0, 0}; - - // Create the socket: - int sendSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (sendSocket < 0) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - printf("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n"); - - // Create and fill the information needed to bind to the socket: - struct sockaddr_in sendAddress; - sendAddress.sin_family = AF_INET; // IPv4 - sendAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); - sendAddress.sin_port = htons(12000); - - int receiveSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (receiveSocket < 0) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - printf("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n"); - - // Make the socket timeout: - struct timeval readTimeout; - readTimeout.tv_sec = 0; - readTimeout.tv_usec = 800; - setsockopt(receiveSocket, SOL_SOCKET, SO_RCVTIMEO, &readTimeout, sizeof(readTimeout)); - - // Create and fill the information needed to bind to the socket: - struct sockaddr_in receiveAddress; - memset(&receiveAddress, 0, sizeof(receiveAddress)); - receiveAddress.sin_family = AF_INET; // IPv4 - receiveAddress.sin_addr.s_addr = INADDR_ANY; - receiveAddress.sin_port = htons(12001); - - // Bind to the socket: - if (bind(receiveSocket, (const struct sockaddr *)&receiveAddress, sizeof(receiveAddress)) < 0) - { - perror("bind failed"); - exit(EXIT_FAILURE); - } - - ship shipA = createShip(32, 32, 512, 512, 1, 0, 0); - ship shipB = createShip(32, 32, -512, -512, 0, 1, 1); - - // Initialize the SDL library, video, sound, and input: - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) - { - printf("SDL Initialization Error: %s\n", SDL_GetError()); - } - - // Initialize image loading: - IMG_Init(IMG_INIT_PNG); - - // Initialize font support: - TTF_Init(); - - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); - SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - - playerController playerOne = createShipPlayerController(&shipA); - playerController playerTwo = createShipPlayerController(&shipB); - - // Create an SDL window and rendering context in that window: - SDL_Window * window = SDL_CreateWindow("SDL_TEST", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 700, 0); - SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, rendererFlags); - - // Set some properties for the window: - SDL_SetWindowResizable(window, SDL_TRUE); - SDL_SetWindowTitle(window, "Spacewar!"); - - int keyCount = 0; - const uint8_t * keyboardState = SDL_GetKeyboardState(&keyCount); - - SDL_Texture * starfieldTexture = IMG_LoadTexture(renderer, "./Images/Starfield.png"); - SDL_Rect starfieldRect; - SDL_QueryTexture(starfieldTexture, NULL, NULL, NULL, &starfieldRect.h); - SDL_QueryTexture(starfieldTexture, NULL, NULL, &starfieldRect.w, NULL); - - TTF_Font * font = TTF_OpenFont("./Robtronika.ttf", 12); - SDL_Color white = {255, 255, 255}; - - // Check for joysticks: - if (SDL_NumJoysticks() < 1 ) - { - playerOne.joystick = NULL; - bool inputSelected = false; - - // Prep the titlescreen struct: - SpacewarTitlescreen titlescreen = prepareTitleScreen(window, renderer, "./Images/Starfield.png", - "./Images/Title.png", font, "Press Enter to play."); - - // Render the title text: - while(!inputSelected) - { - // Draw the title screen according to the titlescreen struct: - drawTitleScreen(&titlescreen); - - // Wait until they press enter: - SDL_PumpEvents(); - SDL_GetKeyboardState(&keyCount); - if(keyboardState[SDL_SCANCODE_RETURN] == 1) - { - inputSelected = true; - } - - // Delay enough so that we run at 30 frames in the menu: - SDL_Delay(1000 / 30); - } - } - else - { - // Prep the titlescreen struct: - SpacewarTitlescreen titlescreen = prepareTitleScreen(window, renderer, "./Images/Starfield.png", - "./Images/Title.png", font, - "Press Enter or Button 0 on your joystick to play."); - - // Load all joysticks: - int joystickListLength = SDL_NumJoysticks(); - SDL_Joystick ** joysticksList = calloc(joystickListLength, sizeof(SDL_Joystick*)); - - for(int index = 0; index < SDL_NumJoysticks(); index++) - { - joysticksList[index] = SDL_JoystickOpen(index); - } - - // Choose a player joystick: - printf("Please press button zero on the controller you wish to use, or enter to play keyboard only.\n"); - - int joystickIndex = 0; - bool inputSelected = false; - - // Render the title text: - while(!inputSelected) - { - // Draw the title screen according to the titlescreen struct: - drawTitleScreen(&titlescreen); - - SDL_PumpEvents(); - SDL_GetKeyboardState(&keyCount); - if(keyboardState[SDL_SCANCODE_RETURN] == 1) - { - joysticksList[joystickIndex] = NULL; - inputSelected = true; - } - joystickIndex++; - if(joystickIndex >= joystickListLength) - { - joystickIndex = 0; - } - if(SDL_JoystickGetButton(joysticksList[joystickIndex], 0) == 1) - { - inputSelected = true; - } - - // Delay enough so that we run at 30 frames in the menu: - SDL_Delay(1000 / 30); - } - - // Load joystick - playerOne.joystick = joysticksList[joystickIndex]; - if (playerOne.joystick == NULL ) - { - printf( "Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError() ); - } - playerOne.haptic = SDL_HapticOpenFromJoystick(playerOne.joystick); - SDL_HapticRumbleInit(playerOne.haptic); - } - - - // Load in all of our textures: - SDL_Texture * blackHoleTexture, * idleTexture, * acceleratingTexture, * clockwiseTexture, * anticlockwiseTexture, - * currentTexture, * acceleratingTexture2, *blackHolePointerTexture; - - idleTexture = IMG_LoadTexture(renderer, "./Images/Ship-Idle.png"); - blackHoleTexture = IMG_LoadTexture(renderer, "./Images/Black-Hole.png"); - clockwiseTexture = IMG_LoadTexture(renderer, "./Images/Ship-Clockwise.png"); - acceleratingTexture = IMG_LoadTexture(renderer, "./Images/Ship-Accelerating.png"); - anticlockwiseTexture = IMG_LoadTexture(renderer, "./Images/Ship-Anticlockwise.png"); - blackHolePointerTexture = IMG_LoadTexture(renderer, "./Images/Black-Hole-Pointer.png"); - acceleratingTexture2 = IMG_LoadTexture(renderer, "./Images/Ship-Accelerating-Frame-2.png"); - currentTexture = acceleratingTexture; - - SDL_Rect blackHoleRectangle; - blackHoleRectangle.x = 0; - blackHoleRectangle.y = 0; - SDL_QueryTexture(blackHoleTexture, NULL, NULL, NULL, &blackHoleRectangle.h); - SDL_QueryTexture(blackHoleTexture, NULL, NULL, &blackHoleRectangle.w, NULL); - - SDL_Rect blackHolePointerRectangle; - blackHolePointerRectangle.x = 0; - blackHolePointerRectangle.y = 0; - blackHolePointerRectangle.w = 128; - blackHolePointerRectangle.h = 128; - lastFrameTime = SDL_GetPerformanceCounter(); - thisFrameTime = SDL_GetPerformanceCounter(); - - while (!quit) - { - lastFrameTime = thisFrameTime; - thisFrameTime = SDL_GetPerformanceCounter(); - deltaTime = (double)(((thisFrameTime - lastFrameTime) * 1000) / (double)SDL_GetPerformanceFrequency()); - - sendto(sendSocket, &shipA, sizeof(ship), 0, (const struct sockaddr *)&sendAddress, sizeof(sendAddress)); - sendto(sendSocket, &shipB, sizeof(ship), 0, (const struct sockaddr *)&sendAddress, sizeof(sendAddress)); - // Store the window's current width and height: - SDL_GetWindowSize(window, &width, &height); - - // Check input: - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - { - quit = true; - break; - } - } - } - - // Wrap the positions if the ship goes interstellar: - if(shipA.position.xComponent > 4096) - { - shipA.position.xComponent = -2000; - } - else if(shipA.position.xComponent < -4096) - { - shipA.position.xComponent = 2000; - } - if(shipA.position.yComponent > 4096) - { - shipA.position.yComponent = -2000; - } - else if(shipA.position.yComponent < -4096) - { - shipA.position.yComponent = 2000; - } - - if(shipB.position.xComponent > 4096) - { - shipB.position.xComponent = -2000; - shipB.velocity.xComponent *= 0.9; - } - else if(shipB.position.xComponent < -4096) - { - shipB.position.xComponent = 2000; - shipB.velocity.xComponent *= 0.9; - } - if(shipB.position.yComponent > 4096) - { - shipB.position.yComponent = -2000; - shipB.velocity.yComponent *= 0.9; - } - else if(shipB.position.yComponent < -4096) - { - shipB.position.yComponent = 2000; - shipB.velocity.yComponent *= 0.9; - } - - // Get the needed input: - getPlayerInput(&playerOne); - takeNetworkInput(&playerTwo, receiveSocket); - - // Do the needed input: - doShipInput(&playerOne, &shipA, starPosition, deltaTime); - doShipInput(&playerTwo, &shipB, starPosition, deltaTime); - - shipA.rectangle.x = (width/2) - 16 - (shipA.velocity.xComponent * 15); - shipA.rectangle.y = (height/2) - 16 - (shipA.velocity.yComponent * 15); - - shipB.rectangle.x = (long)((((shipB.position.xComponent - shipA.position.xComponent) - 32) + width/2) - (shipA.velocity.xComponent * 15)); - shipB.rectangle.y = (long)((((shipB.position.yComponent - shipA.position.yComponent) - 32) + height/2) - (shipA.velocity.yComponent * 15)); - - - // Set the colour to black: - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - - // Clear the screen, filling it with black: - SDL_RenderClear(renderer); - - starfieldRect.x = -900 - (long)shipA.position.xComponent % 800 - (shipA.velocity.xComponent * 15); - starfieldRect.y = -900 - (long)shipA.position.yComponent % 800 - (shipA.velocity.yComponent * 15); - while(starfieldRect.x <= (width + 800)) - { - while(starfieldRect.y <= (height + 800)) - { - SDL_RenderCopy(renderer, starfieldTexture, NULL, &starfieldRect); - starfieldRect.y += 800; - } - starfieldRect.y = -900 - (long)shipA.position.yComponent % 800 - (shipA.velocity.yComponent * 15); - starfieldRect.x += 800; - } - - // Draw the ship: - SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipA.rectangle, - angleBetweenVectors(&shipA.engine, &upVector) + 90, NULL, 0); - SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipB.rectangle, - angleBetweenVectors(&shipB.engine, &upVector) + 90, NULL, 0); - - // Draw the black hole: - blackHoleRectangle.x = ((long)(starPositionX - shipA.position.xComponent - (blackHoleRectangle.w / 2)) + width/2) - - (shipA.velocity.xComponent * 15); - blackHoleRectangle.y = ((long)(starPositionY - shipA.position.yComponent - (blackHoleRectangle.h / 2)) + height/2) - - (shipA.velocity.yComponent * 15); - SDL_RenderCopy(renderer, blackHoleTexture, NULL, &blackHoleRectangle); - - // Set the colour to yellow: - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - - // Draw a line representing the velocity: - SDL_RenderDrawLine(renderer, width/2 - (shipA.velocity.xComponent * 15), - height/2 - (shipA.velocity.yComponent * 15), - (long)((width/2) + shipA.velocity.xComponent * 15) - (shipA.velocity.xComponent * 15), - (long)((height/2) + shipA.velocity.yComponent * 15) - (shipA.velocity.yComponent * 15)); - - // Set the colour to blue: - SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); - - // Draw a line representing the direction of the star: - blackHolePointerRectangle.x = width/2 - (shipA.velocity.xComponent * 15) - (blackHolePointerRectangle.w / 2); - blackHolePointerRectangle.y = height/2 - (shipA.velocity.yComponent * 15) - (blackHolePointerRectangle.h / 2); - SDL_RenderCopyEx(renderer, blackHolePointerTexture, NULL, &blackHolePointerRectangle, - angleBetweenVectors(&shipA.gravity, &upVector) + 90, NULL, 0); - - // Present the rendered graphics: - SDL_RenderPresent(renderer); - } - return 0; -} -// ======================================================================================================== -// Local Variables: -// compile-command: "gcc `sdl2-config --libs --cflags` Spacewar.c spacewarPlayer.c spacewarGraphics.c -lSDL2_image -lSDL2_ttf -lm -o 'Spacewar!'" -// End: diff --git a/source/Spacewar-Client.c b/source/Spacewar-Client.c new file mode 100644 index 0000000..8abee86 --- /dev/null +++ b/source/Spacewar-Client.c @@ -0,0 +1,362 @@ +// ========================================= +// | Spacewar-Client.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include +#include +#include +#include +#include "Spacewar-Messages.h" +#include "Spacewar-Graphics.h" +#include "Spacewar-Server.h" + +const char * messageStrings[] = {"HELLO", "GOODBYE", "PING", "PONG", "SECRET"}; +typedef struct SpacewarNetworkConfig +{ + SpacewarClientInput input; + SpacewarState * state; +} SpacewarNetworkConfig; + +void * runNetworkThread (void * parameters) +{ + SpacewarNetworkConfig * configuration = (SpacewarNetworkConfig *)parameters; + int udpSocket = 0; + udpSocket = socket(AF_INET, SOCK_DGRAM, 0); + + // Configure a timeout for receiving: + struct timeval receiveTimeout; + receiveTimeout.tv_sec = 0; + receiveTimeout.tv_usec = 1000; + setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout, sizeof(struct timeval)); + + // Point at the server: + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); + serverAddress.sin_port = htons(5200); + + // A structure to store the most recent state from the network: + SpacewarState * updatedState = calloc(1, sizeof(SpacewarState)); + + while (true) + { + sendto(udpSocket, &configuration->input, sizeof(SpacewarClientInput), 0, + (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + recvfrom(udpSocket, updatedState, sizeof(SpacewarState), 0, NULL, NULL); + memcpy(configuration->state, updatedState, sizeof(SpacewarState)); + } +} + +int main(int argc, char ** argv) +{ + xyVector upVector = {0, 0.1}; + + // Initialize the SDL library, video, sound, and input: + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) + { + printf("SDL Initialization Error: %s\n", SDL_GetError()); + } + + // Initialize image loading: + IMG_Init(IMG_INIT_PNG); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + // Initialize font support: + TTF_Init(); + + // Create an SDL window and rendering context in that window: + SDL_Window * window = SDL_CreateWindow("SDL_TEST", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, 640, 360, 0); + uint32_t rendererFlags = SDL_RENDERER_ACCELERATED; + SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, rendererFlags); + SDL_SetWindowTitle(window, "Spacewar!"); + SDL_SetWindowResizable(window, SDL_TRUE); + + // Set up keyboard input: + int keyCount = 0; + const uint8_t * keyboardState = SDL_GetKeyboardState(&keyCount); + + // Prep the titlescreen struct: + bool titlescreenInput = false; + TTF_Font * font = TTF_OpenFont("../Robtronika.ttf", 12); + SpacewarTitlescreen titlescreen = prepareTitleScreen(window, renderer, + "../Images/Starfield.png", + "../Images/Title.png", font, + "Press Enter or Button 0."); + // Set up event handling: + SDL_Event event; + bool keepRunning = true; + bool runServer = false; + + // Display the titlescreen until we get an input: + while (!titlescreenInput && keepRunning) + { + // Update events and input: + SDL_PumpEvents(); + SDL_GetKeyboardState(&keyCount); + + // Check windowing system events: + while (SDL_PollEvent(&event) != 0) + { + switch (event.type) + { + case SDL_QUIT: + { + keepRunning = false; + continue; + } + } + } + + // Check if Enter was pressed: + if(keyboardState[SDL_SCANCODE_RETURN] == 1) + { + titlescreenInput = true; + } + + // Check if Space was pressed: + if(keyboardState[SDL_SCANCODE_SPACE] == 1 && !runServer) + { + runServer = true; + printf("Running server!\n"); + } + + // Draw the title screen: + drawTitleScreen(&titlescreen); + + // Delay enough so that we run at 60 frames in the menu: + SDL_Delay(1000 / 60); + } + + if (runServer) + { + SpacewarServerConfiguration serverConfig = {5200}; + pthread_t serverThread; + pthread_create(&serverThread, NULL, runSpacewarServer, &serverConfig); + sleep(1); + } + + // Give me a socket, and make sure it's working: + int serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == -1) + { + printf("Socket creation failed.\n"); + exit(EXIT_FAILURE); + } + + // Create an address struct to point at the server: + struct sockaddr_in serverAddress; + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); + serverAddress.sin_port = htons(5200); + + // Connect to the server: + if (connect(serverSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)) != 0) + { + fprintf(stderr, "Connecting to the server failed.\n"); + exit(0); + } + + printf("Connected.\n"); + + bool playerNumberSet, secretKeySet; + uint8_t playerNumber; + SpacewarNetworkConfig networkConfiguration; + SpacewarMessage message; + + while (!playerNumberSet || !secretKeySet) + { + recv(serverSocket, &message, sizeof(SpacewarMessage), 0); + switch (message.type) + { + case 0: + { + playerNumberSet = true; + playerNumber = message.content; + networkConfiguration.input.playerNumber = message.content; + break; + } + case 4: + { + secretKeySet = true; + networkConfiguration.input.secret = message.content; + } + } + } + + SpacewarState * state = calloc(1, sizeof(SpacewarState)); + networkConfiguration.state = state; + + // Spawn network thread: + pthread_t networkThread; + pthread_create(&networkThread, NULL, runNetworkThread, &networkConfiguration); + + // Spawn client-side-prediction thread: + if (!runServer) + { + pthread_t clientSidePredictionThread; + } + + // Load in all of our textures: + SDL_Texture * blackHoleTexture, * idleTexture, * acceleratingTexture, * clockwiseTexture, + * anticlockwiseTexture, * currentTexture, * acceleratingTexture2, *blackHolePointerTexture; + + SDL_Rect starfieldRect; + SDL_Texture * starfieldTexture = IMG_LoadTexture(renderer, "../Images/Starfield.png"); + SDL_QueryTexture(starfieldTexture, NULL, NULL, &starfieldRect.w, &starfieldRect.h); + + idleTexture = IMG_LoadTexture(renderer, "../Images/Ship-Idle.png"); + blackHoleTexture = IMG_LoadTexture(renderer, "../Images/Black-Hole.png"); + clockwiseTexture = IMG_LoadTexture(renderer, "../Images/Ship-Clockwise.png"); + acceleratingTexture = IMG_LoadTexture(renderer, "../Images/Ship-Accelerating.png"); + anticlockwiseTexture = IMG_LoadTexture(renderer, "../Images/Ship-Anticlockwise.png"); + blackHolePointerTexture = IMG_LoadTexture(renderer, "../Images/Black-Hole-Pointer.png"); + acceleratingTexture2 = IMG_LoadTexture(renderer, "../Images/Ship-Accelerating-Frame-2.png"); + currentTexture = acceleratingTexture; + + SDL_Rect blackHoleRectangle; + blackHoleRectangle.x = 0; + blackHoleRectangle.y = 0; + SDL_QueryTexture(blackHoleTexture, NULL, NULL, + &blackHoleRectangle.w, &blackHoleRectangle.h); + + + SDL_Rect blackHolePointerRectangle; + blackHolePointerRectangle.x = 0; + blackHolePointerRectangle.y = 0; + blackHolePointerRectangle.w = 128; + blackHolePointerRectangle.h = 128; + + SDL_Rect shipRectangles[32]; + for (int index = 0; index < 32; index++) + { + shipRectangles[index].w = 32; + shipRectangles[index].h = 32; + } + + SDL_Rect rendererSize; + int width, height; + while (true) + { + SDL_PumpEvents(); + SDL_GetKeyboardState(&keyCount); + //SDL_GetWindowSize(window, &width, &height); + + // Do input: + networkConfiguration.input.input.turningClockwise = keyboardState[SDL_SCANCODE_RIGHT]; + networkConfiguration.input.input.turningAnticlockwise = keyboardState[SDL_SCANCODE_LEFT]; + networkConfiguration.input.input.accelerating = keyboardState[SDL_SCANCODE_UP]; + + if (keyboardState[SDL_SCANCODE_PAGEUP] == 1) + { + float scaleX, scaleY; + SDL_RenderGetScale(renderer, &scaleX, &scaleY); + SDL_RenderSetScale(renderer, scaleX + 0.01, scaleY + 0.01); + } + + if (keyboardState[SDL_SCANCODE_PAGEDOWN] == 1) + { + float scaleX, scaleY; + SDL_RenderGetScale(renderer, &scaleX, &scaleY); + SDL_RenderSetScale(renderer, scaleX - 0.01, scaleY - 0.01); + } + + SDL_RenderGetViewport(renderer, &rendererSize); + width = rendererSize.w; + height = rendererSize.h; + + // Clear the screen: + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + // Draw the starfield: + starfieldRect.x = -900 - (long)state->playerStates[playerNumber].position.xComponent % 800; + starfieldRect.y = -900 - (long)state->playerStates[playerNumber].position.yComponent % 800; + + while(starfieldRect.x <= (width + 800)) + { + while(starfieldRect.y <= (height + 800)) + { + SDL_RenderCopy(renderer, starfieldTexture, NULL, &starfieldRect); + starfieldRect.y += 800; + } + starfieldRect.y = -900 - (long)state->playerStates[playerNumber].position.yComponent % 800; + starfieldRect.x += 800; + } + + // Draw the black hole: + blackHoleRectangle.x = ((long)(0 - state->playerStates[playerNumber].position.xComponent + - (blackHoleRectangle.w / 2)) + width/2); + blackHoleRectangle.y = ((long)(0 - state->playerStates[playerNumber].position.yComponent + - (blackHoleRectangle.h / 2)) + height/2); + + SDL_RenderCopy(renderer, blackHoleTexture, NULL, &blackHoleRectangle); + + // Draw the black hole directional indicator: + blackHolePointerRectangle.x = width/2 - (blackHolePointerRectangle.w / 2); + blackHolePointerRectangle.y = height/2 - (blackHolePointerRectangle.h / 2); + SDL_RenderCopyEx(renderer, blackHolePointerTexture, NULL, &blackHolePointerRectangle, + angleBetweenVectors(&state->playerStates[playerNumber].gravity, &upVector) + 90, + NULL, 0); + + // Draw the player's ship: + shipRectangles[playerNumber].x = (width/2) - 16; + shipRectangles[playerNumber].y = (height/2) - 16; + + SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipRectangles[playerNumber], + angleBetweenVectors(&state->playerStates[playerNumber].engine, &upVector) + 90, + NULL, 0); + + // Draw every other ship: + for (int index = 0; index < 32; index++) + { + if (index == playerNumber) + { + continue; + } + if (state->playerStates[playerNumber].inPlay == true) + { + shipRectangles[index].x = ((long)(state->playerStates[index].position.xComponent - + state->playerStates[playerNumber].position.xComponent) - + 32 + (width/2)); + shipRectangles[index].y = ((long)(state->playerStates[index].position.yComponent - + state->playerStates[playerNumber].position.yComponent) - + 32 + (height/2)); + + SDL_RenderCopyEx(renderer, currentTexture, NULL, &shipRectangles[index], + angleBetweenVectors(&state->playerStates[index].engine, &upVector) + 90, + NULL, 0); + } + } + // Present to the screen: + SDL_RenderPresent(renderer); + SDL_Delay(1000 / 144); + } + pthread_join(networkThread, NULL); + + return 0; +} +// ======================================================= +// | End of Spacewar-Client.c, copyright notice follows. | +// ======================================================= + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Local Variables: +// compile-command: "gcc `sdl2-config --libs --cflags` Spacewar-Client.c Spacewar-Graphics.c Spacewar-Server.c Spacewar-Physics.c -lSDL2_image -lSDL2_ttf -lm -o 'Spacewar-Client'" +// End: diff --git a/spacewarGraphics.c b/source/Spacewar-Graphics.c similarity index 75% rename from spacewarGraphics.c rename to source/Spacewar-Graphics.c index f49279d..183cf54 100644 --- a/spacewarGraphics.c +++ b/source/Spacewar-Graphics.c @@ -1,10 +1,11 @@ -#include -#include -#include -#include #include #include -#include "spacewarGraphics.h" +#include +#include +#include +#include + +#include "Spacewar-Graphics.h" SpacewarTitlescreen prepareTitleScreen(SDL_Window * window, SDL_Renderer * renderer, char * starfieldTexturePath, char * logoTexturePath, @@ -120,4 +121,37 @@ void drawTitleScreen(SpacewarTitlescreen * titlescreen) SDL_RenderPresent(titlescreen->renderer); } +/* void drawMenuScreen(SpacewarMenuscreen * menuscreen) */ +/* { */ +/* // Get the current size of the window: */ +/* int width = 0, height = 0; */ +/* SDL_GetWindowSize(menuscreen->window, &width, &height); */ + +/* // Set the renderer colour to black and clear the screen: */ +/* SDL_SetRenderDrawColor(menuscreen->renderer, 0, 0, 0, 255); */ +/* SDL_RenderClear(menuscreen->renderer); */ + +/* // Set the correct position to begin the starfield, and scroll it back for the next frame: */ +/* menuscreen->starfieldRectangle->x = 0 - menuscreen->xScroll++; */ +/* menuscreen->starfieldRectangle->y = 0; */ + +/* // Draw the starfield by tiling the starfield texture: */ +/* while (menuscreen->starfieldRectangle->x <= (width + menuscreen->starfieldRectangle->w)) */ +/* { */ +/* // Go down, covering a column of the screen: */ +/* while(menuscreen->starfieldRectangle->y <= (height + menuscreen->starfieldRectangle->h)) */ +/* { */ +/* SDL_RenderCopy(menuscreen->renderer, menuscreen->starfieldTexture, NULL, */ +/* menuscreen->starfieldRectangle); */ +/* menuscreen->starfieldRectangle->y += menuscreen->starfieldRectangle->h; */ +/* } */ + +/* // Back to the top, move over one texture width: */ +/* menuscreen->starfieldRectangle->y = 0; */ +/* menuscreen->starfieldRectangle->x += menuscreen->starfieldRectangle->w; */ +/* } */ + +/* // Display to the renderer: */ +/* SDL_RenderPresent(menuscreen->renderer); */ +/* } */ diff --git a/source/Spacewar-Graphics.h b/source/Spacewar-Graphics.h new file mode 100644 index 0000000..f0a2a22 --- /dev/null +++ b/source/Spacewar-Graphics.h @@ -0,0 +1,55 @@ +// ========================================= +// | Spacewar-Graphics.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef SPACEWAR_GRAPHICS_H +#define SPACEWAR_GRAPHICS_H + +#include +#include + +typedef struct SpacewarTitlescreen +{ + SDL_Window * window; + SDL_Renderer * renderer; + uint16_t xScroll, titleAlpha, textAlpha; + SDL_Texture * titleTexture, * textTexture, * starfieldTexture; + SDL_Rect * titleRectangle, * textRectangle, * starfieldRectangle; +} SpacewarTitlescreen; + +typedef struct SpacewarMenuscreen +{ + SDL_Window * window; + SDL_Renderer * renderer; + uint16_t xScroll; + SDL_Texture * starfieldTexture; +} SpacewarMenuscreen; + +SpacewarTitlescreen prepareTitleScreen(SDL_Window * window, SDL_Renderer * renderer, + char * starfieldTexturePath, char * logoTexturePath, + TTF_Font * font, char * text); + +SpacewarMenuscreen prepareMenuscreenFromTitle(SpacewarTitlescreen * titlescreen); + +void drawTitleScreen(SpacewarTitlescreen * titlescreen); + +void drawMenuScreen(SpacewarMenuscreen * menuscreen); + +#endif +// ========================================================= +// | End of Spacewar-Graphics.h, copyright notice follows. | +// ========================================================= + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/Spacewar-Messages.h b/source/Spacewar-Messages.h new file mode 100644 index 0000000..ed3d72e --- /dev/null +++ b/source/Spacewar-Messages.h @@ -0,0 +1,40 @@ +// ========================================= +// | Spacewar-Messages.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef SPACEWAR_MESSAGES_H +#define SPACEWAR_MESSAGES_H +#include + +typedef struct SpacewarMessage +{ + uint8_t type; + uint32_t content; +} SpacewarMessage; + +/* Message Types: + 0 - HELLO: Contents sent to client indicate a given player number. + 1 - GOODBYE: No contents, end the connection. + 2 - PING: Contents indicate the missed amount of pongs. + 3 - PONG: No contents. + 4 - SECRET: Contents indicate the secret key that must be sent with UDP packets to the server. +*/ + +#endif +// ========================================================= +// | End of Spacewar-Messages.h, copyright notice follows. | +// ========================================================= + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/Spacewar-Physics.c b/source/Spacewar-Physics.c new file mode 100644 index 0000000..792bcdf --- /dev/null +++ b/source/Spacewar-Physics.c @@ -0,0 +1,100 @@ +// ========================================= +// | Spacewar-Physics.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include "Spacewar-Physics.h" + +void doPhysicsTick(SpacewarState * state) +{ + double gravityMagnitude, gravityAcceleration, velocityMagnitude; + for (int shipIndex = 0; shipIndex < 32; shipIndex++) + { + SpacewarShipState * currentShip = &state->playerStates[shipIndex]; + if (currentShip->inPlay) + { + // Calculate Gravity: + xyVectorBetweenPoints(currentShip->position.xComponent, currentShip->position.yComponent, + 0.0, 0.0, ¤tShip->gravity); + gravityMagnitude = normalizeXYVector(¤tShip->gravity); + gravityAcceleration = 0; + + // Some maths that felt okay: + if (gravityMagnitude >= 116) + { + gravityAcceleration = (45000 / pow(gravityMagnitude, 2)) * 6.67; + } + // We're pactually in the black hole; teleport: + else + { + currentShip->position.xComponent = (double)(random() % 7500); + currentShip->position.yComponent = (double)(random() % 7500); + currentShip->velocity.xComponent = 0; + currentShip->velocity.yComponent = 0; + } + + multiplyXYVector(¤tShip->gravity, gravityAcceleration); + + // Apply Inputs: + + // Rotate the engine vector if needed: + if (state->playerInputs[shipIndex].turningClockwise == 1) + { + rotateXYVector(¤tShip->engine, 2.5); + } + if (state->playerInputs[shipIndex].turningAnticlockwise == 1) + { + rotateXYVector(¤tShip->engine, -2.5); + } + + // Apply Gravity and Velocity to Position: + if (state->playerInputs[shipIndex].accelerating == 1) + { + addXYVector(¤tShip->velocity, ¤tShip->engine); + } + addXYVector(¤tShip->velocity, ¤tShip->gravity); + addXYVector(¤tShip->position, ¤tShip->velocity); + + // Wrap position to game field: + if (currentShip->position.xComponent > 8000.0) + { + state->playerStates[shipIndex].position.xComponent = -7999.0; + state->playerStates[shipIndex].velocity.xComponent *= 0.9; + } + if (currentShip->position.xComponent < -8000.0) + { + state->playerStates[shipIndex].position.xComponent = 7999.0; + state->playerStates[shipIndex].velocity.xComponent *= 0.9; + } + if (currentShip->position.yComponent > 8000.0) + { + state->playerStates[shipIndex].position.yComponent = -7999.0; + state->playerStates[shipIndex].velocity.yComponent *= 0.9; + } + if (currentShip->position.yComponent < -8000.0) + { + state->playerStates[shipIndex].position.yComponent = 7999.0; + state->playerStates[shipIndex].velocity.yComponent *= 0.9; + } + } + } +} +// ======================================================== +// | End of Spacewar-Physics.c, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/Spacewar-Physics.h b/source/Spacewar-Physics.h new file mode 100644 index 0000000..c6f707e --- /dev/null +++ b/source/Spacewar-Physics.h @@ -0,0 +1,62 @@ +// ========================================= +// | Spacewar-Physics.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef SPACEWAR_PHYSICS +#define SPACEWAR_PHYSICS +#include +#include "xyVector.h" +#include "Spacewar-Server.h" + +typedef struct SpacewarShipState +{ + bool inPlay; + xyVector engine; + xyVector gravity; + xyVector position; + xyVector velocity; +} SpacewarShipState; + + +typedef struct SpacewarShipInput +{ + double turningAmount, acceleratingAmount; + uint8_t turningClockwise, turningAnticlockwise, accelerating; +} SpacewarShipInput; + +typedef struct SpacewarClientInput +{ + uint8_t playerNumber; + uint32_t secret; + SpacewarShipInput input; +} SpacewarClientInput; + +typedef struct SpacewarState +{ + uint64_t tickNumber; + struct timeval timestamp; + SpacewarShipState playerStates[32]; + SpacewarShipInput playerInputs[32]; +} SpacewarState; + +// Does a single step of the physics: +void doPhysicsTick(SpacewarState * state); + +#endif +// ======================================================== +// | End of Spacewar-Physics.h, copyright notice follows. | +// ======================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/Spacewar-Server.c b/source/Spacewar-Server.c new file mode 100644 index 0000000..b0bcf55 --- /dev/null +++ b/source/Spacewar-Server.c @@ -0,0 +1,294 @@ +// ========================================= +// | Spacewar Server.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Spacewar-Messages.h" +#include "Spacewar-Physics.h" + +SpacewarConnection * getConnectionBySocket(SpacewarConnection * connections, size_t connectionCount, int socket) +{ + for (size_t connectionIndex = 0; connectionIndex < connectionCount; connectionIndex++) + { + if (connections[connectionIndex].clientSocket == socket) + { + return &connections[connectionIndex]; + } + } + return NULL; +} + +void sendCurrentState(SpacewarState * state, SpacewarConnection * connections, int udpSocket) +{ + for (int connectionIndex = 0; connectionIndex < 32; connectionIndex++) + { + if (connections[connectionIndex].active) + { + sendto(udpSocket, state, sizeof(SpacewarState), 0, + (struct sockaddr *)&connections[connectionIndex].clientAddress, sizeof(struct sockaddr_in)); + } + } +} +void * runServerPhysics(void * parameters) +{ + SpacewarServerSharedState * sharedState = (SpacewarServerSharedState *)parameters; + for(int index = 0; index < 32; index++) + { + sharedState->physicsState->playerStates[index].engine.yComponent = 0.1; + } + + while (true) + { + doPhysicsTick(sharedState->physicsState); + sendCurrentState(sharedState->physicsState, sharedState->connections, sharedState->udpSocket); + usleep(15625); + } + + return NULL; +} + +void * runInputReceiver(void * parameters) +{ + SpacewarServerSharedState * sharedState = (SpacewarServerSharedState *)parameters; + int bytesRead; + socklen_t socketAddressLength; + struct sockaddr_in clientAddress; + + SpacewarClientInput input; + + while (true) + { + bytesRead = recvfrom(sharedState->udpSocket, &input, sizeof(SpacewarClientInput), 0, + (struct sockaddr *)&clientAddress, &socketAddressLength); + if (bytesRead == sizeof(SpacewarClientInput)) + { + if (input.playerNumber < 32) + { + if (input.secret == sharedState->connections[input.playerNumber].playerSecret) + { + sharedState->physicsState->playerStates[input.playerNumber].inPlay = true; + memcpy(&sharedState->connections[input.playerNumber].clientAddress, + &clientAddress, sizeof(struct sockaddr_in)); + memcpy(&sharedState->physicsState->playerInputs[input.playerNumber], &input.input, + sizeof(SpacewarShipInput)); + } + } + } + bzero(&input, sizeof(SpacewarClientInput)); + } + + return NULL; +} + +// Adds a new player to a physics simulation. Returns a randomly generated secret key: +uint32_t addPlayer(SpacewarConnection * connection, int playerNumber, SpacewarState * state) +{ + connection->playerSecret = rand(); + state->playerStates[playerNumber].inPlay = false; + + return connection->playerSecret; +} + +// Creates a Spacewar server, intended to be ran by the standalone server or forked by the game client: +void * runSpacewarServer(void * configuration) +{ + SpacewarServerConfiguration * serverConfig = (SpacewarServerConfiguration *)configuration; + printf("Starting Server.\n"); + + // Create our network listeners: + int masterSocket = socket(AF_INET, SOCK_STREAM, 0); + if (masterSocket < 0) + { + fprintf(stderr, "Failed to create socket.\n"); + exit(EXIT_FAILURE); + } + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); + + // Create a structure to bind the listening socket: + struct sockaddr_in listeningAddress; + memset(&listeningAddress, 0, sizeof(listeningAddress)); + listeningAddress.sin_family = AF_INET; // IPv4 + listeningAddress.sin_addr.s_addr = INADDR_ANY; + listeningAddress.sin_port = htons(serverConfig->port); + + // Bind to the listening socket: + if (bind(masterSocket, (const struct sockaddr *)&listeningAddress, sizeof(listeningAddress)) < 0) + { + fprintf(stderr, "Failed to bind socket.\n"); + exit(EXIT_FAILURE); + } + + // Begin listening on the master socket: + listen(masterSocket, 32); + + // Create an epoll descriptor to keep track of clients: + int recievedEventCount = 0; + struct epoll_event receivedEvents[32]; + int epollDescriptor = epoll_create(1); + + // Add the master socket to the epoll set: + struct epoll_event requestedEvents; + requestedEvents.events = EPOLLIN | EPOLLET; + requestedEvents.data.fd = masterSocket; + epoll_ctl(epollDescriptor, EPOLL_CTL_ADD, masterSocket, &requestedEvents); + + // Create a set of connection structs to store the current connection information: + SpacewarConnection * connectedClients = calloc(32, sizeof(SpacewarConnection)); + for(int index = 0; index < 32; index++) + { + connectedClients[index].active = false; + } + + // Create a UDP socket: + int udpSocket = 0; + if ((udpSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + exit(EXIT_FAILURE); + } + + // Create a struct to bind the UDP socket to a port: + struct sockaddr_in serverAddress; + memset(&serverAddress, 0, sizeof(serverAddress)); + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(5200); + serverAddress.sin_addr.s_addr = INADDR_ANY; + + // Bind it: + bind(udpSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + + // Setup the server threads: + pthread_t physicsThread, inputThread; + SpacewarState * currentState = calloc(1, sizeof(SpacewarState)); + SpacewarServerSharedState threadState; + threadState.udpSocket = udpSocket; + threadState.physicsState = currentState; + threadState.connections = connectedClients; + + // Begin the simulation: + pthread_create(&inputThread, NULL, runInputReceiver, &threadState); + pthread_create(&physicsThread, NULL, runServerPhysics, &threadState); + + // Manage clients and sending packets back and forth: + while (true) + { + int receivedEventCount = epoll_wait(epollDescriptor, receivedEvents, 32, -1); + for (int eventIndex = 0; eventIndex < receivedEventCount; eventIndex++) + { + // If there's activity on the master socket, there's a new connection: + if (receivedEvents[eventIndex].data.fd == masterSocket) + { + struct sockaddr_in clientAddress; + int newClientSocket = accept(masterSocket, NULL, NULL); + + // Check that the socket is functional: + if (newClientSocket < 0) + { + fprintf(stderr, "Failed to accept client connection.\n"); + continue; + } + + // Register the new client in the epoll set: + requestedEvents.events = EPOLLIN | EPOLLET; + requestedEvents.data.fd = newClientSocket; + epoll_ctl(epollDescriptor, EPOLL_CTL_ADD, newClientSocket, &requestedEvents); + + for (int index = 0; index < 32; index++) + { + if (connectedClients[index].active == false) + { + // Configure the new connection: + connectedClients[index].active = true; + connectedClients[index].missedPongs = false; + connectedClients[index].clientSocket = newClientSocket; + + // Send the HELLO packet to the player: + SpacewarMessage helloMessage; + helloMessage.type = 0; + helloMessage.content = index; + send(newClientSocket, &helloMessage, sizeof(SpacewarMessage), 0); + + // Add the player to the simulation: + uint32_t secret = addPlayer(&connectedClients[index], index, currentState); + + // Send the SECRET packet to the player: + helloMessage.type = 4; + helloMessage.content = secret; + send(newClientSocket, &helloMessage, sizeof(SpacewarMessage), 0); + + break; + } + } + } + // Otherwise, we've been sent a packet from one of the connected clients: + else + { + SpacewarConnection * client = getConnectionBySocket(connectedClients, 32, + receivedEvents->data.fd); + + SpacewarMessage receivedMessage; + size_t bytesRead = recv(client->clientSocket, &receivedMessage, + sizeof(SpacewarMessage), 0); + + if (bytesRead == 0) + { + // Send a goodbye message: + SpacewarMessage goodbyeMessage; + goodbyeMessage.type = 1; + goodbyeMessage.content = 0; + send(client->clientSocket, &goodbyeMessage, sizeof(SpacewarMessage), 0); + + // Remove the socket from the epoll interest set: + epoll_ctl(epollDescriptor, EPOLL_CTL_DEL, client->clientSocket, NULL); + + // Remove the player from the simulation: + //removePlayer(&connectedClients[index], currentState); + + // Shutdown the socket: + shutdown(client->clientSocket, SHUT_RDWR); + + // Deactivate the connection: + client->active = false; + } + + else + { + switch (receivedMessage.content) + { + // Handle message contents: + } + } + + } + } + } + return NULL; +} + +// ======================================================= +// | End of Spacewar-Server.c, copyright notice follows. | +// ======================================================= + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/source/Spacewar-Server.h b/source/Spacewar-Server.h new file mode 100644 index 0000000..99429fa --- /dev/null +++ b/source/Spacewar-Server.h @@ -0,0 +1,59 @@ +// ========================================= +// | Spacewar-Server.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef SPACEWAR_SERVER +#define SPACEWAR_SERVER +#include +#include +#include +#include + +#include "Spacewar-Physics.h" + +typedef struct SpacewarState SpacewarState; + +typedef struct SpacewarConnection +{ + bool active; + int clientSocket; + uint8_t missedPongs; + uint32_t playerSecret; + struct sockaddr_in clientAddress; +} SpacewarConnection; + + +typedef struct SpacewarServerConfiguration +{ + uint16_t port; +} SpacewarServerConfiguration; + + +typedef struct SpacewarServerSharedState +{ + int udpSocket; + SpacewarState * physicsState; + SpacewarConnection * connections; +} SpacewarServerSharedState; + +// Creates a spacewar server, intended to be ran by the standalone server or forked by the game client: +void * runSpacewarServer(void * configuration); + +#endif +// ======================================================= +// | End of Spacewar-Server.h, copyright notice follows. | +// ======================================================= + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . diff --git a/xyVector.h b/source/xyVector.h similarity index 99% rename from xyVector.h rename to source/xyVector.h index ad32290..5eee9a8 100644 --- a/xyVector.h +++ b/source/xyVector.h @@ -13,7 +13,6 @@ static inline double angleBetweenVectors(xyVector * vectorA, xyVector * vectorB) { double dotProduct = (vectorA->xComponent * vectorB->xComponent) + (vectorA->yComponent * vectorB->yComponent); double determinant = (vectorA->xComponent * vectorB->yComponent) - (vectorA->yComponent * vectorB->xComponent); - return atan2(dotProduct, determinant) / 0.01745329; } diff --git a/spacewarGraphics.h b/spacewarGraphics.h deleted file mode 100644 index a2125e6..0000000 --- a/spacewarGraphics.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef SPACEWAR_GRAPHICS_H -#define SPACEWAR_GRAPHICS_H -#include - -typedef struct SpacewarTitlescreen -{ - SDL_Window * window; - SDL_Renderer * renderer; - uint16_t xScroll, titleAlpha, textAlpha; - SDL_Texture * titleTexture, * textTexture, * starfieldTexture; - SDL_Rect * titleRectangle, * textRectangle, * starfieldRectangle; -} SpacewarTitlescreen; - -SpacewarTitlescreen prepareTitleScreen(SDL_Window * window, SDL_Renderer * renderer, - char * starfieldTexturePath, char * logoTexturePath, - TTF_Font * font, char * text); - -void drawTitleScreen(SpacewarTitlescreen * titlescreen); - -static inline void DrawCircle(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius) -{ - const int32_t diameter = (radius * 2); - - int32_t x = (radius - 1); - int32_t y = 0; - int32_t tx = 1; - int32_t ty = 1; - int32_t error = (tx - diameter); - - while (x >= y) - { - // Each of the following renders an octant of the circle - SDL_RenderDrawPoint(renderer, centreX + x, centreY - y); - SDL_RenderDrawPoint(renderer, centreX + x, centreY + y); - SDL_RenderDrawPoint(renderer, centreX - x, centreY - y); - SDL_RenderDrawPoint(renderer, centreX - x, centreY + y); - SDL_RenderDrawPoint(renderer, centreX + y, centreY - x); - SDL_RenderDrawPoint(renderer, centreX + y, centreY + x); - SDL_RenderDrawPoint(renderer, centreX - y, centreY - x); - SDL_RenderDrawPoint(renderer, centreX - y, centreY + x); - - if (error <= 0) - { - ++y; - error += ty; - ty += 2; - } - - if (error > 0) - { - --x; - tx += 2; - error += (tx - diameter); - } - } -} - -#endif diff --git a/spacewarPlayer.c b/spacewarPlayer.c deleted file mode 100644 index 715df44..0000000 --- a/spacewarPlayer.c +++ /dev/null @@ -1,102 +0,0 @@ -// spacewarPlayer.c: Contains function definitions for player interaction. -// Barra Ó Catháin, 2023 -// ======================================================================= -#include -#include -#include -#include -#include -#include - -#include "xyVector.h" -#include "spacewarPlayer.h" - -void takeNetworkInput(playerController * controller, int descriptor) -{ - recvfrom(descriptor, controller, sizeof(playerController), 0, NULL, NULL); -} - -void getPlayerInput(playerController * controller) -{ - SDL_PumpEvents(); - const uint8_t * keyboardState = SDL_GetKeyboardState(NULL); - if(keyboardState[SDL_SCANCODE_UP] == 1) - { - controller->accelerating = true; - } - else - { - controller->accelerating = false; - } - if(keyboardState[SDL_SCANCODE_LEFT] == 1) - { - controller->turningAnticlockwise = true; - } - else - { - controller->turningAnticlockwise = false;; - } - if(keyboardState[SDL_SCANCODE_RIGHT] == 1) - { - controller->turningClockwise = true; - } - else - { - controller->turningClockwise = false; - } - if(controller->joystick != NULL) - { - controller->turningAmount = SDL_JoystickGetAxis(controller->joystick, 0); - controller->acceleratingAmount = SDL_JoystickGetAxis(controller->joystick, 5); - } -} - -void doShipInput(playerController * controller, ship * ship, xyVector starPosition, double deltaTime) -{ - if(controller->number == ship->number) - { - // Calculate the gravity for the ships: - calculateGravity(&starPosition, ship); - - // Rotate the engine vector if needed: - if (controller->turningClockwise) - { - rotateXYVector(&ship->engine, 0.25 * deltaTime); - } - else if (controller->turningAmount > 2500) - { - double rotationalSpeed = (controller->turningAmount / 20000); - rotateXYVector(&ship->engine, 0.25 * deltaTime * rotationalSpeed); - } - - if (controller->turningAnticlockwise) - { - rotateXYVector(&ship->engine, -0.25 * deltaTime); - } - else if (controller->turningAmount < -2500) - { - double rotationalSpeed = (controller->turningAmount / 20000); - rotateXYVector(&ship->engine, 0.25 * deltaTime * rotationalSpeed); - } - - // Calculate the new current velocity: - addXYVectorDeltaScaled(&ship->velocity, &ship->gravity, deltaTime); - - if (controller->acceleratingAmount > 2500) - { - xyVector temporary = ship->engine; - multiplyXYVector(&ship->engine, controller->acceleratingAmount/ 32748); - SDL_HapticRumblePlay(controller->haptic, (float)controller->acceleratingAmount / 32768, 20); - addXYVectorDeltaScaled(&ship->velocity, &ship->engine, deltaTime); - ship->engine = temporary; - } - - else if (controller->accelerating) - { - addXYVectorDeltaScaled(&ship->velocity, &ship->engine, deltaTime); - } - - // Calculate the new position: - addXYVectorDeltaScaled(&ship->position, &ship->velocity, deltaTime); - } -} diff --git a/spacewarPlayer.h b/spacewarPlayer.h deleted file mode 100644 index 1c591be..0000000 --- a/spacewarPlayer.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef SPACEWARPLAYER_H -#define SPACEWARPLAYER_H -#include "xyVector.h" - -// A struct storing the needed data to draw a ship: -typedef struct ship -{ - int number; - xyVector engine; - xyVector gravity; - xyVector position; - xyVector velocity; - SDL_Rect rectangle; -} ship; - -// A struct to store the input state for one player: -typedef struct playerController -{ - SDL_Joystick * joystick; - SDL_Haptic * haptic; - int number; - double turningAmount, acceleratingAmount; - bool turningClockwise, turningAnticlockwise, accelerating; -} playerController; - -static inline void calculateGravity(xyVector * starPosition, ship * shipUnderGravity) -{ - // Calculate the vector between the star and ship: - xyVectorBetweenPoints(shipUnderGravity->position.xComponent, shipUnderGravity->position.yComponent, - starPosition->xComponent, starPosition->yComponent, &shipUnderGravity->gravity); - - // Make it into a unit vector: - double gravityMagnitude = normalizeXYVector(&shipUnderGravity->gravity); - double gravityAcceleration = 0; - - // Calculate the gravity between the star and ship: - if(gravityMagnitude != 0) - { - if(gravityMagnitude >= 116) - { - gravityAcceleration = pow(2, (3000 / (pow(gravityMagnitude, 2)))) / 8; - } - else - { - shipUnderGravity->position.xComponent = random() % 4000; - shipUnderGravity->position.yComponent = random() % 4000; - shipUnderGravity->velocity.xComponent = 0; - shipUnderGravity->velocity.yComponent = 0; - } - } - else - { - gravityAcceleration = 1; - } - - if(gravityAcceleration < 0.01) - { - gravityAcceleration = 0.01; - } - - // Scale the vector: - multiplyXYVector(&shipUnderGravity->gravity, gravityAcceleration); -} - -static inline playerController createShipPlayerController(ship * ship) -{ - playerController newController; - newController.number = ship->number; - newController.joystick = NULL; - return newController; -} - -// Create a ship with the given parameters: -static inline ship createShip(int width, int height, double positionX, double positionY, double velocityX, double velocityY, int number) -{ - ship newShip; - - // Player number: - newShip.number = number; - - // Rectangle to show the ship in: - newShip.rectangle.w = width; - newShip.rectangle.h = height; - - // Position: - newShip.position.xComponent = positionX; - newShip.position.yComponent = positionY; - - // Velocity: - newShip.velocity.xComponent = velocityX; - newShip.velocity.yComponent = velocityY; - - // Gravity: - newShip.gravity.xComponent = 0; - newShip.gravity.yComponent = 0; - - // Engine: - newShip.engine.yComponent = 0; - newShip.engine.xComponent = 0.1; - return newShip; -} - -// Function prototypes: -void doShipInput(playerController * controller, ship * ship, xyVector starPosition, double deltaTime); - -void takeNetworkInput(playerController * controller, int descriptor); - -void getPlayerInput(playerController * controller); - -#endif