Compare commits

42 Commits

Author SHA1 Message Date
418579a627 Added C++ standard as a compiler flag, which is apparently needed by compilers on macOS 2024-03-10 01:09:31 -06:00
6acbf90d80 Updated UNIX macro checks, to account for MacOS 2024-03-10 00:51:20 -06:00
2c7d1d0b43 Started working on client-side decoding of IPv6 code 2024-03-09 21:34:47 -05:00
00b83e6de2 Added bash flag to fail if any command in the script fails 2024-03-09 21:34:09 -05:00
9881567009 Created a script to build statically-linked binaries on Linux 2024-03-09 21:33:49 -05:00
f23f307e17 Updated TODO 2024-03-09 21:33:22 -05:00
dd658c9c1d WROTE IN AIRPLANE: Checked edge case where the number is zero 2024-03-09 21:05:38 -05:00
52f8034f4e Rudimentary support for IPv6 in server socket creation
I haven't completely implemented it yet, but I did come up with a basic algorithm
to convert the IPv6 address into a 'code' form. I still have to write the code to
actually create the socket, though.
2024-03-09 19:32:45 -05:00
8758060bfb Updated TODO 2024-03-09 19:32:03 -05:00
10f91fafd4 Updated README 2024-03-09 19:31:51 -05:00
e9405b69e7 Updated README 2024-03-09 12:46:52 -05:00
9a12edcdb1 Started working on better error handling, by throwing exceptions and displaying error messages in the GUI 2024-03-09 11:05:04 -05:00
83a0d5beb4 Updated files to reflect change in easysock (from CPP to C) 2024-03-09 11:04:17 -05:00
eeae444b1d Moved display_text_centered() into a separate file, since I could possibly extend this file with other raygui helper functions 2024-03-09 11:03:27 -05:00
f9d5e8cdeb Converted easysock from CPP to C, because it was mostly just C code anyways 2024-03-09 11:02:50 -05:00
9972e146d5 Updated TODO 2024-03-08 23:45:48 -05:00
8848f0ff8c Removed debug print statements, cleared background before drawing to screen, and added client GUI implementation
I removed a print statement that printed out every position of the ball, because it was no longer necessary.
I also added code to clear the background before drawing to the screen at the start of the game, to remove
any lingering un-erased objects. Finally, and most substantially, I finished the initial implementation of
the client-side GUI. The client should now be able to specify a connect code through the GUI, and connect
to the appropriate server.
2024-03-08 23:35:18 -05:00
07ac3f9166 Filled out implementation of check_client function 2024-03-08 23:34:49 -05:00
9e0990156e Removed unnecessary print message, and added timer.c to source files 2024-03-08 23:33:54 -05:00
1423cc19a0 Split timer into header and implementation file 2024-03-08 23:33:24 -05:00
352d3f26f1 Moved struct definition to separate file, and added check for displaying GUI
I moved the GameType struct (and the Mode enum) to a separate file, as I will need
to use it in the check_server and check_client functions as well. I also added the
signum function (which was previously in sign.hpp) to this file, since it was the only
function in sign.hpp. Finally, I added a check, that will only display the GUI, if the
user didn't provide any command-line arguments.
2024-03-08 14:46:30 -05:00
788b334e7c Removed sign file, since it only contained one function. This function has been moved to main.cpp 2024-03-08 14:46:26 -05:00
9de9353936 Added include guards to timer header file 2024-03-08 14:45:18 -05:00
a3392308c4 Updated TODO 2024-03-08 14:44:53 -05:00
0e9088beb6 Added comments, and added a way to use the bundled raylib, even if we are building a dynamically linked version 2024-03-08 14:44:17 -05:00
7812611fe6 Created an implementation and header file to check the user input, if it is entered through the GUI 2024-03-08 14:43:45 -05:00
bc0d644399 Replaced compound literal initialization of 'Rectangle' and 'Vector2' types (which is only valid in C), with braced-initialization (valid in C++) 2024-03-08 08:20:52 -05:00
d05ba0daa1 Updated TODO 2024-03-07 23:26:01 -05:00
c8f29d1336 Updated TODO 2024-03-07 23:24:47 -05:00
0d1dc049b5 Worked on further implementation of game mode selection.
I added code to display a help text after the user selects a mode. Currently,
this text is only displayed in single player mode. Additionally, I added a rudimentary
'form' to input IP address and port, if the user selects server mode.
2024-03-07 23:19:03 -05:00
f4bbb6ef6a Added a rudimentary timer implementation 2024-03-07 23:18:59 -05:00
1f470e23ee Updated TODO 2024-03-07 18:15:34 -05:00
97e6da3b2b Updated TODO 2024-03-07 18:11:15 -05:00
0286878c70 Rudimentary support for inputting game mode through GUI instead of command-line
The only thing that 'works' right now is the skeleton GUI structure. The buttons don't
actually do anything.
2024-03-07 18:10:14 -05:00
e9da48d9a0 Added raygui header file, and dark mode header file 2024-03-07 18:09:27 -05:00
832dae977a Updated TODO 2024-03-07 18:08:42 -05:00
613b81c542 Added compiler flag to ignore warnings related to narrowing conversions
I know it's bad practice, but I didn't see any other alternative, as this seemed to be an
issue with raygui.
2024-03-07 18:07:18 -05:00
28c4b421d2 Made changes to script
I renamed the file to reflect that it is only for MinGW (I plan to create
similar scripts for other operating systems), and added code to zip the release folder,
inside the shell script.
2024-03-07 07:49:39 -05:00
13ce75067b Updated TODO 2024-03-07 07:48:28 -05:00
635d71f1c5 Slightly lowered speed, to make the game a little easier to play 2024-03-07 07:46:38 -05:00
8c4a515046 Updated TODO 2024-03-06 23:30:28 -05:00
f25e7fae38 Updated TODO 2024-03-06 21:42:04 -05:00
24 changed files with 6660 additions and 67 deletions

View File

@@ -4,16 +4,21 @@ __Netpong__ is a network-enabled Pong game, written in C++. It enables two playe
## How it works
The game has only one runtime dependency: The [raylib](https://www.raylib.com/) graphics system. In order to write idiomatic C++, I chose to use the [raylib-cpp](https://robloach.github.io/raylib-cpp/) wrapper, which provides an object-oriented interface to the Raylib library. However, this wrapper is bundled with the project, and is thus not required to be installed.
The game has only one runtime dependency: The [raylib](https://www.raylib.com/) graphics library. In order to write idiomatic C++, I chose to use the [raylib-cpp](https://robloach.github.io/raylib-cpp/) wrapper, which provides an object-oriented interface to the Raylib library.
## Building
This application uses [Meson](https://mesonbuild.com/) as a build system. To build the application:
1. Install __meson__ from the link above.
2. Install __raylib__ from the link above (THIS IS OPTIONAL, SEE STEP 5)
3. Set up the build directory with the `meson setup build` command.
4. Compile the application, with the existing raylib installation, using `meson compile -C build`.
5. If you don't have raylib installed, you can create a statically linked version of the library on Linux by running the following commands:
1. Install meson from the link above.
2. Set up the build directory.
```
meson setup build
```
3. Compile the application. Meson should use a system installation of raylib, if it exists. If not, it falls back to a bundled version.
```
meson compile -C build
```
4. You can also create a statically-linked version of the game (with no runtime dependencies) on Linux by running the following commands:
```
meson configure -Ddefault_library=static build/
@@ -30,3 +35,7 @@ This application uses [Meson](https://mesonbuild.com/) as a build system. To bui
- One player runs the application in Server mode, specifying their IP address and a port: `build/pong -S <ip_address> <port>`
- The other player connects to the first player by running in Client mode, specifying the first player's IP address and port: `build/pong -C <ip_address> <port>`.
- The server controls the left paddle by default (WIP to allow the user to modify this), and the client controls the right paddle.
## TODO
See todo.txt.

83
check_input.cpp Normal file
View File

@@ -0,0 +1,83 @@
#include <iostream>
#include "includes/easysock.h"
#include "includes/connect_code.hpp"
#include "includes/server.hpp"
#include "includes/client.hpp"
#include "includes/check_input.hpp"
#include "includes/raygui/raygui.h"
#include "includes/exception_consts.hpp"
#include "includes/raygui_helpers.hpp"
#include "includes/timer.h"
GameType check_server(char* ip_text, char* port_text) {
GameType type;
std::string addr;
uint16_t port;
/* Check if IP address and port are in valid forms */
if (check_ip_ver(ip_text) < 0) {
throw std::invalid_argument("Invalid IP address");
}
if (port_to_num(port_text) < 0) {
throw std::invalid_argument("Invalid port");
}
/* From here on, we assume that the IP and port are valid */
addr = std::string(ip_text);
port = std::stoi(std::string(port_text));
std::string code = connect_code::encode(addr, std::to_string(port));
/* Create server socket and wait for client to connect */
Server* server = new Server(check_ip_ver(addr.data()), ES_UDP, addr.data(), port);
server->create_socket();
display_text_centered("Your code is " + code + "\nWaiting for connection...");
std::string response = "";
char* temp_response = NULL;
/* Wait for the client to connect. Since recvAll returns a char*, we need to create a temporary variable to check for NULL.
TODO - Check that the client actually sends 'GG'. */
do {
temp_response = server->recvAll();
} while (temp_response == NULL);
response = std::string(temp_response);
server->sendAll("U2");
display_text_centered("Connection received from " + server->get_peer_addr());
Timer timer = timer_init(3);
while (!timer_done(timer)); // Wait for five seconds
type.mode = M_SERVER;
type.netsock = server;
return type;
}
GameType check_client(char* code_text) {
GameType type;
std::vector<std::string> addr_port;
std::string connect_code = std::string(code_text); /* The connect code is a special string, that contains the server address and port. It is given by the server. */
try {
addr_port = connect_code::decode(connect_code);
Client* client = new Client(4, ES_UDP, addr_port[0].data(), std::stoi(addr_port[1]));
client->create_socket();
/* Send a specific message to the server, and wait for the appropriate response, to know that the server is ready */
client->sendAll("GG");
// display_text_centered("Connecting...");
std::string msg_from_server = client->recvAll();
if (msg_from_server == "U2") {
display_text_centered("Connection made");
Timer timer = timer_init(3);
while (!timer_done(timer));
} else {
throw EXCEPT_WRONGRESPONSE;
}
type.mode = M_CLIENT;
type.netsock = client;
return type;
} catch (int e) {
throw;
} catch (std::exception& e) {
throw;
}
}

View File

@@ -2,7 +2,7 @@
#include "includes/client.hpp"
#include "includes/exception_consts.hpp"
#include "includes/sock.hpp"
#include "includes/easysock.hpp"
#include "includes/easysock.h"
/* Destructor - closes any open sockets */
Client::~Client() {

View File

@@ -1,3 +1,4 @@
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
@@ -5,6 +6,15 @@
#include <cstdint>
#include "includes/connect_code.hpp"
#include "includes/numeric_base.hpp"
#include "includes/easysock.h"
#if defined(_WIN32)
#include <In6addr.h>
#include <Ws2tcpip.h>
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
namespace connect_code {
@@ -44,13 +54,70 @@ namespace connect_code {
uint32_t addr_val = (std::stoul(octets[0]) << 24) + (std::stoul(octets[1]) << 16) + (std::stoul(octets[2]) << 8) + (std::stoul(octets[3]));
return std::to_string(addr_val);
}
/* Expand an IPv6 address (expand '::' into ':0000:', for example).
This is done by first converting the address into a binary representation,
and then printing every character of the binary representation into a string. */
std::string expand_ip6_addr(std::string addr) {
char ip6_string[40]; // 32 characters + 7 colons
struct in6_addr* ip6_s_ptr = (struct in6_addr *)malloc(sizeof(in6_addr)); // Struct pointer, to store the binary representation of the address
inet_pton(AF_INET6, addr.data(), ip6_s_ptr); // Convert the string representation into a binary form
/* This abomination, converts the binary representation into a string.
It uses sprintf to print every byte in the binary representation into a string.
The bytes are formatted as 2-character hexadecimal values. */
sprintf(ip6_string,
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ip6_s_ptr->s6_addr[0], ip6_s_ptr->s6_addr[1],
ip6_s_ptr->s6_addr[2], ip6_s_ptr->s6_addr[3],
ip6_s_ptr->s6_addr[4], ip6_s_ptr->s6_addr[5],
ip6_s_ptr->s6_addr[6], ip6_s_ptr->s6_addr[7],
ip6_s_ptr->s6_addr[8], ip6_s_ptr->s6_addr[9],
ip6_s_ptr->s6_addr[10], ip6_s_ptr->s6_addr[11],
ip6_s_ptr->s6_addr[12], ip6_s_ptr->s6_addr[13],
ip6_s_ptr->s6_addr[14], ip6_s_ptr->s6_addr[15]);
return std::string(ip6_string);
}
std::string encode(std::string address, std::string port) {
std::string addr_coded;
/* Convert the address to decimal, and convert that to hex */
std::string addr_coded = dotted_dec_to_dec(address);
addr_coded = base_convert(addr_coded, 10, 32);
if (check_ip_ver(address.data()) == 4) {
/* I don't really have a reason to use my own function (dotted_dec_to_dec()
and dec_to_dotted_dec()), to convert the IP address from text to binary.
The inet_pton() and inet_ntop() functions can do this just fine, and also
take care of edge cases. Maybe someday, I might change this code. I could probably
repurpose the functions for something else, though. */
/* First, convert the address into a 32-bit integer (the integer is stored as a string).
Then, convert the address into base-32. */
addr_coded = dotted_dec_to_dec(address);
addr_coded = base_convert(addr_coded, 10, 32);
}
if (check_ip_ver(address.data()) == 6) {
/* First, expand the address into the full 39-character format (32 hex values + 7 colons).
Then, tokenize the string, using colons as the delimiters.
Finally, take each token in the string, and convert it from base-16 to base-32, appending a '-' as a delimiter. */
std::string addr_expanded = expand_ip6_addr(address);
std::string addr_coded = "";
std::vector<std::string> addr_tokenized = tokenize_str(addr_expanded, ":");
for (int i = 0; i < addr_tokenized.size()-1; i++ ) {
addr_coded += base_convert(addr_tokenized[i], 16, 32);
addr_coded += "-";
}
addr_coded += base_convert(addr_tokenized[addr_tokenized.size()-1], 16, 32); // I put this outside the loop, because I don't want a hyphen after it
/* TODO - Check if the IP address is actually converted properly, and test if the server socket is created correctly.
Also do the same for client side, and check client-server connection. */
std::cout << addr_coded << std::endl;
abort();
}
/* Convert the port to hex */
std::string port_coded = base_convert(port, 10, 32);
std::string ret_val = addr_coded + "_" + port_coded;
@@ -60,6 +127,15 @@ namespace connect_code {
}
std::vector<std::string> decode(std::string connect_code) {
//<AIRPLANE_CODE>
int ip_ver = 0;
if (connect_code.find("-") == connect_code.npos) {
ip_ver = 6; // If the string contains hyphens, it must be an IPv6 address encoding.
} else {
ip_ver = 4;
}
//</AIRPLANE_CODE>
if (connect_code.find("_") == std::string::npos) {
throw std::invalid_argument("Invalid code entered.");
}

View File

@@ -2,6 +2,7 @@
# This script copies required DLLs, and the application itself into a folder called 'release'. It only runs on MinGW.
set -o errexit # Stop executing when a command fails
BASE_DIR=$(dirname $0)
REL_DIR="$BASE_DIR/release/dist"
@@ -19,3 +20,6 @@ cp "$BASE_DIR/build/pong" "$REL_DIR"
# Remove the temporary file.
rm "$BASE_DIR/tmp_file.txt"
#Zip the $REL_DIR folder
zip -r "$BASE_DIR/release/netpong-win.zip" "$REL_DIR"

26
create_static_linux.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# This script creates and packages a statically-linked build of the game, for Linux.
# It must be placed in the root of the source code.
set -o errexit # Stop executing when a command fails
BASE_DIR=$(dirname 0)
REL_DIR="$BASE_DIR/release/static/pong"
mkdir -p "$REL_DIR"
# Set the default build target to static
meson configure -Ddefault_library=static "$BASE_DIR/build/"
# Build the application
meson compile -C "$BASE_DIR/build/"
# Package the application:
# 1. Copy the executable to REL_DIR
# 2. Create a tarball
cp "$BASE_DIR/build/pong" "$REL_DIR"
tar -czf "$BASE_DIR/release/pong.tar.gz" "$REL_DIR"
# Reset default build target to shared
meson configure -Ddefault_library=shared "$BASE_DIR/build/"

View File

@@ -1,8 +1,9 @@
#include "includes/easysock.hpp"
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#include "includes/easysock.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#ifndef _WIN32
@@ -187,6 +188,29 @@ int check_ip_ver(const char* address) {
}
}
int port_to_num(const char* port_str) {
/* The largest possible port value is 65535: a 5 character string */
if (strlen(port_str) > 5) {
return -1;
}
for (int i = 0; i < strlen(port_str); i++) {
if (isdigit(port_str[i]) == 0) { // Ensure that every character in port_str is a digit (isidigit() returns 0 if the parameter is not a digit)
return -1;
}
}
/* Convert the string to a base-10 integer */
int port_num = (int)strtol(port_str, NULL, 10);
if (port_num > 65535) {
return -1;
}
if (port_num < 1024) {
return -2;
}
return port_num;
}
int int_to_inet(int network) {
if (network == 4) {
return AF_INET;

28
includes/check_input.hpp Normal file
View File

@@ -0,0 +1,28 @@
#ifndef _CHECK_INPUT_H
#define _CHECK_INPUT_H
#include "includes/sock.hpp"
typedef enum {M_SINGLE, M_CLIENT, M_SERVER} Mode;
/* This struct contains a Mode enum, which indicates the type of game we are
playing (Single player, client mode or server mode). The netsock parameter is
a 'Sock' object - Client and Server classes inherit from this object, so this
parameter can be instantiated to either a client or server, depending on the
game type. */
typedef struct {
Mode mode;
Sock* netsock;
} GameType;
/* This function checks the IP address and port passed to it, and returns a struct,
that contains information about the game mode, and contains the server socket.
It assumes that both ip_text and port_text are non-null
TODO - Add better error checking. */
GameType check_server(char* ip_text, char* port_text);
/* NOT IMPLEMENTED YET - This function checks the code given to it, and returns a struct
that contains information about the game mode, and contains the client socket. */
GameType check_client(char* code);
#endif

View File

@@ -1,12 +1,15 @@
#ifndef EASYSOCK_HPP_
#define EASYSOCK_HPP_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef EASYSOCK_H_
#define EASYSOCK_H_
#ifdef _WIN32
#include <winsock2.h>
#include <winsock.h>
#include <ws2tcpip.h>
#endif
#ifdef __unix__
#if defined(__unix) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@@ -70,6 +73,12 @@ IPv6 address (returns 6) or neither (returns -1). */
int check_ip_ver(const char* address);
/* port_to_num - Converts a string representing a port, into a numeric value.
Returns -1 if the string is not numeric, or exceeds the maximum port length.
Returns -2 if the string is lower than 1024, This serves as a warning, as ports less
than 1023 are reserved. */
int port_to_num(const char* port_str);
/* int_to_inet - Takes an int value (4 for IPv4, 6 for IPv6) and returns AF_INET or
AF_INET6 respectively. */
@@ -92,3 +101,6 @@ int sock_quit(void);
int sock_close(SOCKET);
#endif
#ifdef __cplusplus
}
#endif

5535
includes/raygui/raygui.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,589 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// StyleAsCode exporter v2.0 - Style data exported as a values array //
// //
// USAGE: On init call: GuiLoadStyleDark(); //
// //
// more info and bugs-report: github.com/raysan5/raygui //
// feedback and support: ray[at]raylibtech.com //
// //
// Copyright (c) 2020-2024 raylib technologies (@raylibtech) //
// //
//////////////////////////////////////////////////////////////////////////////////
#define DARK_STYLE_PROPS_COUNT 23
// Custom style name: Dark
static const GuiStyleProp darkStyleProps[DARK_STYLE_PROPS_COUNT] = {
{ 0, 0, 0x878787ff }, // DEFAULT_BORDER_COLOR_NORMAL
{ 0, 1, 0x2c2c2cff }, // DEFAULT_BASE_COLOR_NORMAL
{ 0, 2, 0xc3c3c3ff }, // DEFAULT_TEXT_COLOR_NORMAL
{ 0, 3, 0xe1e1e1ff }, // DEFAULT_BORDER_COLOR_FOCUSED
{ 0, 4, 0x848484ff }, // DEFAULT_BASE_COLOR_FOCUSED
{ 0, 5, 0x181818ff }, // DEFAULT_TEXT_COLOR_FOCUSED
{ 0, 6, 0x000000ff }, // DEFAULT_BORDER_COLOR_PRESSED
{ 0, 7, 0xefefefff }, // DEFAULT_BASE_COLOR_PRESSED
{ 0, 8, 0x202020ff }, // DEFAULT_TEXT_COLOR_PRESSED
{ 0, 9, 0x6a6a6aff }, // DEFAULT_BORDER_COLOR_DISABLED
{ 0, 10, 0x818181ff }, // DEFAULT_BASE_COLOR_DISABLED
{ 0, 11, 0x606060ff }, // DEFAULT_TEXT_COLOR_DISABLED
{ 0, 16, 0x00000010 }, // DEFAULT_TEXT_SIZE
{ 0, 17, 0x00000000 }, // DEFAULT_TEXT_SPACING
{ 0, 18, 0x9d9d9dff }, // DEFAULT_LINE_COLOR
{ 0, 19, 0x3c3c3cff }, // DEFAULT_BACKGROUND_COLOR
{ 0, 20, 0x00000018 }, // DEFAULT_TEXT_LINE_SPACING
{ 1, 5, 0xf7f7f7ff }, // LABEL_TEXT_COLOR_FOCUSED
{ 1, 8, 0x898989ff }, // LABEL_TEXT_COLOR_PRESSED
{ 4, 5, 0xb0b0b0ff }, // SLIDER_TEXT_COLOR_FOCUSED
{ 5, 5, 0x848484ff }, // PROGRESSBAR_TEXT_COLOR_FOCUSED
{ 9, 5, 0xf5f5f5ff }, // TEXTBOX_TEXT_COLOR_FOCUSED
{ 10, 5, 0xf6f6f6ff }, // VALUEBOX_TEXT_COLOR_FOCUSED
};
// WARNING: This style uses a custom font: "PixelOperator.ttf" (size: 16, spacing: 0)
#define DARK_STYLE_FONT_ATLAS_COMP_SIZE 2126
// Font atlas image pixels data: DEFLATE compressed
static unsigned char darkFontData[DARK_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
0xdd, 0xdb, 0x72, 0xa4, 0x3a, 0x12, 0x05, 0x50, 0xfd, 0xff, 0x4f, 0xe7, 0x3c, 0x4c, 0x4c, 0x4c, 0x74, 0x9c, 0xd3, 0x20,
0xa5, 0x52, 0x17, 0xec, 0xd5, 0xeb, 0xcd, 0xe5, 0x76, 0x51, 0xa0, 0x94, 0x84, 0x28, 0x36, 0xd1, 0x00, 0x00, 0x00, 0x80,
0x5f, 0x2f, 0xfe, 0xf5, 0x27, 0xf1, 0xd7, 0xdf, 0x8c, 0xee, 0xbf, 0xf3, 0xfc, 0xf3, 0xff, 0xbd, 0x1a, 0x0f, 0xef, 0xd5,
0xb7, 0xad, 0xa3, 0xef, 0x1b, 0x03, 0x7b, 0xe2, 0xdf, 0xb7, 0x2f, 0xba, 0xff, 0xee, 0xdf, 0x3e, 0xdf, 0xf8, 0xef, 0x3f,
0xfd, 0xa5, 0xe7, 0xad, 0x8f, 0xa1, 0xfd, 0x3e, 0xfe, 0x7f, 0xc6, 0x8e, 0x62, 0x2c, 0xd9, 0xf7, 0xef, 0x5b, 0x37, 0xbe,
0xed, 0x95, 0xff, 0x27, 0x1e, 0x3e, 0x4f, 0xe6, 0x28, 0xf5, 0xd4, 0xd6, 0x0d, 0xf5, 0x1f, 0x5d, 0x95, 0x18, 0xa5, 0x2d,
0xe7, 0xff, 0xef, 0x1a, 0x85, 0x3d, 0x55, 0x76, 0x4f, 0x3e, 0x55, 0xfa, 0xf8, 0xfe, 0x78, 0x6a, 0xfd, 0xe3, 0x3d, 0x60,
0x94, 0x7c, 0x96, 0xf9, 0xd6, 0x18, 0xd3, 0xbf, 0xdd, 0xd7, 0x92, 0x6a, 0x3e, 0xd5, 0xcc, 0x2b, 0xad, 0xe4, 0x28, 0x9d,
0xa8, 0xff, 0x48, 0xf7, 0x54, 0x31, 0x58, 0xd1, 0x15, 0x7b, 0x30, 0xd3, 0x0f, 0xb7, 0x8d, 0xf5, 0x1f, 0xa5, 0x73, 0x97,
0x28, 0x6b, 0x73, 0x27, 0xea, 0xff, 0x79, 0xd4, 0x6e, 0x25, 0xed, 0x26, 0x5e, 0xf6, 0x51, 0x6d, 0x2d, 0xaf, 0x9d, 0xa7,
0xae, 0xae, 0xff, 0xe7, 0xdf, 0x1c, 0x1d, 0x6f, 0x63, 0x70, 0xec, 0xae, 0xd8, 0x4f, 0xb9, 0xf1, 0xbf, 0x7e, 0x3f, 0xc6,
0x5f, 0xc7, 0xe6, 0xec, 0x3c, 0x64, 0x64, 0x0f, 0xc7, 0xe0, 0xd9, 0xd5, 0xec, 0x08, 0x7b, 0x72, 0xfc, 0xef, 0x9b, 0x0b,
0xaa, 0xff, 0xb5, 0xf5, 0x9f, 0xf9, 0x24, 0xd1, 0xb9, 0x05, 0x99, 0x31, 0xb1, 0x5d, 0x50, 0xff, 0xa3, 0xfd, 0xd0, 0xf3,
0xbe, 0xaa, 0x9a, 0xcb, 0x8f, 0xae, 0x4b, 0xf4, 0x9d, 0x61, 0xef, 0x6b, 0x85, 0xe3, 0x73, 0xa3, 0xf1, 0xfa, 0x1f, 0xef,
0x3d, 0xdf, 0xfe, 0xda, 0xe8, 0x5e, 0xfd, 0xfb, 0x99, 0xdd, 0xfc, 0x1e, 0xdf, 0x57, 0xff, 0x91, 0x18, 0x53, 0xce, 0xd7,
0x73, 0x4d, 0xfd, 0x47, 0x62, 0xe5, 0xa2, 0x95, 0xad, 0xf2, 0xe5, 0xd6, 0x25, 0xea, 0xd6, 0x0c, 0xce, 0x1f, 0x81, 0xf7,
0xfa, 0x7f, 0x3b, 0x1f, 0x3c, 0x3f, 0xfe, 0xc7, 0x15, 0xe3, 0x7f, 0xa4, 0xd7, 0x86, 0x5b, 0xa2, 0xf7, 0xfd, 0x5a, 0xfd,
0x47, 0xa2, 0x66, 0xa2, 0xe4, 0xbc, 0xbc, 0x2d, 0x59, 0xcb, 0xab, 0x5d, 0x33, 0xdc, 0x73, 0xb4, 0xde, 0x46, 0xd1, 0xaa,
0xb3, 0x9a, 0x9f, 0x55, 0xff, 0xad, 0x60, 0xfc, 0xef, 0x9b, 0x03, 0xcc, 0x7e, 0x8e, 0xf8, 0xd8, 0xe8, 0x5f, 0x59, 0xff,
0xbb, 0x56, 0xec, 0xd7, 0xb7, 0xc6, 0xb8, 0xe2, 0xc8, 0x58, 0xff, 0xaf, 0x9c, 0xff, 0xe7, 0xd7, 0x00, 0xc6, 0xce, 0x63,
0xe2, 0xd2, 0xea, 0xcf, 0x9c, 0x33, 0x57, 0x5d, 0xb1, 0xaf, 0xfd, 0x36, 0x41, 0x24, 0xbe, 0xab, 0x51, 0xb3, 0x8a, 0xb3,
0xfa, 0x18, 0x8c, 0x8f, 0xff, 0xbb, 0xae, 0xff, 0xbf, 0xcf, 0xb9, 0xef, 0x18, 0xff, 0xdb, 0xeb, 0xe8, 0x7e, 0xcb, 0xf6,
0x00, 0x7b, 0x7b, 0x80, 0x50, 0xfd, 0xe0, 0x7b, 0xc4, 0x80, 0xfa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0a, 0xbf, 0x9f, 0x5f, 0x9f, 0x63, 0x30, 0x9e, 0x84, 0xbd, 0x3e, 0x81, 0x7d, 0x26, 0x13, 0x38, 0xbb,
0x87, 0xaa, 0xfe, 0xe2, 0x73, 0x76, 0xfc, 0x48, 0xd2, 0x42, 0x7f, 0xcb, 0x88, 0xe1, 0xa7, 0x1f, 0x54, 0x26, 0x0a, 0x64,
0x32, 0x0d, 0xda, 0x60, 0x9b, 0xa8, 0xcd, 0x52, 0x1a, 0xdb, 0xfa, 0x3d, 0x2d, 0xfe, 0x0b, 0xf5, 0x7f, 0xfe, 0x95, 0x96,
0xc8, 0xef, 0xd9, 0x59, 0xff, 0xa3, 0x6d, 0x2f, 0x97, 0x8f, 0x3b, 0x37, 0x96, 0xec, 0x4e, 0x14, 0x8b, 0xc2, 0x63, 0xb8,
0x6f, 0x5c, 0xfd, 0x33, 0x43, 0x67, 0x47, 0xbb, 0x9e, 0xa9, 0xff, 0xd1, 0xec, 0xd3, 0x78, 0xe8, 0x85, 0xef, 0xa9, 0xff,
0xb6, 0xa5, 0xfe, 0xe3, 0x23, 0xf5, 0x5f, 0x9d, 0x24, 0x53, 0x95, 0x42, 0x9e, 0x49, 0xbe, 0x19, 0x3f, 0x86, 0xf9, 0x84,
0xdf, 0x6c, 0x8b, 0xfb, 0x4e, 0xfd, 0x67, 0xda, 0xd6, 0x73, 0xce, 0xaa, 0xfa, 0x7f, 0xdf, 0xb7, 0x99, 0xd1, 0x64, 0xfe,
0xe7, 0x35, 0x95, 0xbf, 0x27, 0x47, 0xf7, 0xed, 0x39, 0x5a, 0x6b, 0xdb, 0xca, 0xec, 0x58, 0xf9, 0x95, 0xfa, 0x8f, 0xe4,
0x5f, 0x89, 0x97, 0x1e, 0xa5, 0x36, 0x81, 0xbd, 0x26, 0xb5, 0x6d, 0x6f, 0xfd, 0x57, 0x3f, 0xf7, 0xe7, 0x8e, 0xfa, 0x3f,
0x9f, 0xa3, 0xbb, 0xea, 0x1c, 0xae, 0x76, 0xac, 0xfc, 0xd9, 0xe3, 0xff, 0x7b, 0x2f, 0xb9, 0xa3, 0x7d, 0xcc, 0x8c, 0xf4,
0x27, 0xc7, 0xff, 0x73, 0x3f, 0x3f, 0x5b, 0xff, 0x55, 0xad, 0x3d, 0x36, 0xce, 0x4c, 0xb2, 0x63, 0xe5, 0xce, 0x55, 0xb2,
0xdd, 0xe7, 0xff, 0xab, 0x3e, 0x59, 0x1b, 0x9e, 0x4d, 0xc4, 0xb5, 0xf5, 0x3f, 0xb3, 0x0a, 0xb9, 0x6e, 0xbe, 0x70, 0x66,
0xfe, 0x9f, 0x7b, 0xbe, 0x6d, 0xe6, 0xa9, 0x80, 0xb7, 0x8d, 0xff, 0xbb, 0xae, 0x92, 0xb5, 0xcd, 0x35, 0xb9, 0xf7, 0xbd,
0x2a, 0x9f, 0x52, 0x37, 0x9e, 0xdf, 0x1f, 0xc5, 0x33, 0xbc, 0xaf, 0xd7, 0x7f, 0x7e, 0x95, 0xbf, 0x15, 0xad, 0x4a, 0x9e,
0x9b, 0xff, 0xef, 0x7e, 0xd2, 0x49, 0xe6, 0x5d, 0x6a, 0xfa, 0xdf, 0xbe, 0xab, 0xc2, 0xb5, 0xa3, 0xc0, 0xdf, 0xaf, 0x36,
0x44, 0xd1, 0xc8, 0x51, 0x95, 0xdf, 0xff, 0xe7, 0xb6, 0x8d, 0x3f, 0xf1, 0x6c, 0xfc, 0x4a, 0x7c, 0x0c, 0x3e, 0x4f, 0xff,
0x44, 0xfd, 0x67, 0xde, 0x39, 0xf3, 0xbf, 0x46, 0x8f, 0x61, 0x65, 0xfd, 0x9f, 0xeb, 0x01, 0xe4, 0x6e, 0xc2, 0x8d, 0xb3,
0x18, 0xe0, 0xe7, 0xf4, 0x00, 0x9e, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xec, 0xbe, 0x13, 0xfb, 0xef, 0xd9, 0x5c, 0x99, 0xd4, 0xf8, 0xf1, 0xfb, 0xa4, 0x77, 0x26, 0xe6, 0xb7, 0x97, 0x14, 0xb2,
0xe7, 0xdf, 0xfa, 0xf3, 0x95, 0x8a, 0x3c, 0xde, 0x28, 0xbc, 0x6b, 0xfd, 0x29, 0x93, 0xe4, 0x39, 0x4b, 0x71, 0x34, 0x77,
0xe0, 0x7d, 0x0f, 0x8f, 0x1f, 0x97, 0x99, 0xd7, 0x5a, 0x61, 0x9a, 0x42, 0x24, 0xd2, 0x5e, 0xde, 0xf6, 0x7a, 0x7f, 0x36,
0x70, 0x94, 0x6d, 0xf1, 0x58, 0x1d, 0x55, 0x65, 0x27, 0xdf, 0x9e, 0x98, 0x3f, 0x9f, 0xbc, 0x13, 0x2f, 0xad, 0xae, 0xbf,
0xfe, 0x7b, 0x92, 0x23, 0xd6, 0xe7, 0x18, 0xf5, 0xfc, 0x34, 0x93, 0x7a, 0x31, 0x96, 0xc1, 0x36, 0xd7, 0x06, 0x56, 0x8f,
0x50, 0x6d, 0xb2, 0x42, 0x62, 0xaa, 0xa5, 0xcc, 0x67, 0xff, 0xbd, 0x6f, 0x69, 0x1b, 0x1c, 0xb9, 0x6e, 0x48, 0xcc, 0x8c,
0xce, 0xfa, 0xaa, 0xfe, 0x69, 0x1b, 0xec, 0x6f, 0x32, 0x79, 0x75, 0x51, 0x9a, 0x44, 0x91, 0xff, 0x3f, 0x91, 0x48, 0x6d,
0x6e, 0xc9, 0x2c, 0xbe, 0x7c, 0x46, 0x7c, 0x26, 0xa7, 0x2a, 0x16, 0xef, 0xd9, 0x4c, 0xfd, 0xb7, 0x43, 0xf5, 0x9f, 0x49,
0x3d, 0x38, 0x9f, 0x98, 0x1d, 0xa9, 0xda, 0xdd, 0x5b, 0xff, 0xd1, 0xd1, 0x7f, 0x65, 0x66, 0xd7, 0x51, 0x78, 0xce, 0xf0,
0x3e, 0xe3, 0xad, 0x1e, 0xff, 0xdb, 0xe3, 0x7e, 0x1a, 0x4f, 0x75, 0x5b, 0x3f, 0x42, 0x65, 0x7a, 0x93, 0xb1, 0xfa, 0x5f,
0xdd, 0x63, 0xcd, 0x3c, 0x07, 0x29, 0x3e, 0x37, 0xfe, 0x9f, 0x49, 0xcc, 0x8e, 0xc4, 0xd9, 0x75, 0xe6, 0x7d, 0xeb, 0xd7,
0x5e, 0xa2, 0xb0, 0xfe, 0x33, 0x3d, 0x40, 0xbe, 0xfe, 0x77, 0xce, 0xa6, 0xb3, 0x79, 0x93, 0x31, 0xdd, 0x86, 0xce, 0xd4,
0x7f, 0xe5, 0xd3, 0x96, 0x56, 0x24, 0xe6, 0x8e, 0x3f, 0xd9, 0xf1, 0xd4, 0xf8, 0xdf, 0x52, 0xe3, 0x7f, 0x6e, 0x9c, 0xa8,
0x3b, 0xf6, 0xb5, 0x73, 0xe5, 0xf7, 0xfa, 0x8f, 0x0d, 0xef, 0xb6, 0xaf, 0xfe, 0x33, 0x3d, 0x61, 0x2c, 0x9b, 0xff, 0x67,
0xab, 0x25, 0x86, 0xe7, 0xd7, 0x3b, 0x13, 0x73, 0x33, 0xeb, 0xcc, 0xf7, 0xd7, 0x7f, 0xe5, 0x33, 0x4c, 0xda, 0x54, 0xfa,
0x72, 0x94, 0x8e, 0xc8, 0xfb, 0xc6, 0xff, 0x15, 0xb3, 0xe9, 0x4c, 0xfa, 0xfe, 0x3d, 0xeb, 0x7f, 0xad, 0xf8, 0x88, 0xb5,
0xe3, 0x79, 0x88, 0x2b, 0x66, 0x85, 0x27, 0xce, 0xff, 0xab, 0x12, 0xd8, 0x73, 0xe7, 0xff, 0xd5, 0x2b, 0xf2, 0x73, 0xc7,
0xf2, 0xde, 0xf1, 0x3f, 0xd7, 0x42, 0x62, 0x68, 0x7c, 0x6e, 0x9f, 0xaa, 0xff, 0x5b, 0xbe, 0xd1, 0x50, 0xd1, 0x6f, 0x45,
0x49, 0xef, 0x96, 0x79, 0x6e, 0x4b, 0xe5, 0x3c, 0xaa, 0x72, 0xfd, 0x6a, 0xe6, 0xfa, 0xff, 0xcc, 0xb7, 0x53, 0x56, 0xbf,
0x5b, 0xfe, 0xfa, 0xff, 0xda, 0xfa, 0x8f, 0xe2, 0x2d, 0xe6, 0x9e, 0x5e, 0xea, 0xe6, 0xf7, 0xd3, 0x7e, 0xee, 0x38, 0x82,
0x6a, 0x59, 0xeb, 0x39, 0x71, 0x3e, 0xc4, 0xef, 0x9a, 0x33, 0xf3, 0xd3, 0xbf, 0xe1, 0x6d, 0x3f, 0x18, 0x9b, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xef, 0xde, 0xc3, 0x14, 0xaf, 0x77, 0x38, 0xbc, 0x65, 0x4b,
0x64, 0x72, 0x29, 0x5b, 0x69, 0xce, 0x5d, 0x4b, 0x24, 0xf3, 0x67, 0xb7, 0xae, 0x77, 0xaf, 0xac, 0xff, 0x64, 0xb3, 0x69,
0xe6, 0x99, 0xc4, 0xf9, 0xcc, 0x3e, 0x3e, 0x7b, 0x34, 0xdf, 0x32, 0x3c, 0x22, 0x91, 0xc6, 0x5a, 0x79, 0xbc, 0x5a, 0x6a,
0xdf, 0x65, 0x5b, 0x59, 0x6f, 0x0f, 0xd0, 0x9f, 0x0f, 0x53, 0x7f, 0x8f, 0x69, 0xa4, 0xf6, 0x79, 0x4d, 0x32, 0xff, 0x4c,
0x5b, 0x6a, 0xe9, 0x04, 0xe2, 0x6c, 0xab, 0xe9, 0xfd, 0x5b, 0x31, 0xfd, 0x97, 0xdf, 0xb3, 0x5a, 0xe3, 0x73, 0x47, 0x33,
0x9b, 0x17, 0x5f, 0x7d, 0xbc, 0x6a, 0xd3, 0xdb, 0x6a, 0x7a, 0x80, 0x8a, 0xfa, 0xaf, 0xdb, 0xca, 0xfd, 0xf7, 0xc6, 0x46,
0xf2, 0x7e, 0xfa, 0xf8, 0xfc, 0xfd, 0xa7, 0xf1, 0xd1, 0x24, 0x81, 0x5c, 0xca, 0xe4, 0xae, 0x4f, 0x95, 0xcb, 0xe5, 0xca,
0xa7, 0xb7, 0xed, 0x1c, 0xff, 0x33, 0x49, 0x52, 0x99, 0xb4, 0xd0, 0xea, 0x57, 0xb2, 0xbd, 0x65, 0xae, 0xfe, 0x33, 0x99,
0x4d, 0x51, 0x94, 0x67, 0x5c, 0xf7, 0xdc, 0x93, 0x48, 0xce, 0xaf, 0x4f, 0x1e, 0xcd, 0x78, 0x4d, 0x25, 0xaf, 0xca, 0xd8,
0xdd, 0xfb, 0x4a, 0x4c, 0x26, 0x3e, 0x44, 0xd7, 0xf1, 0x8e, 0xe9, 0xb1, 0x70, 0xd7, 0xd3, 0x02, 0x33, 0xcf, 0xa4, 0x68,
0xc9, 0xd4, 0xcc, 0x28, 0x9b, 0xe1, 0x57, 0xbf, 0xb2, 0xa2, 0xfe, 0xe3, 0xe1, 0x3c, 0xb9, 0xfa, 0x99, 0x65, 0xd5, 0x47,
0xb3, 0xa7, 0x67, 0xf8, 0x62, 0xfd, 0xaf, 0x9d, 0xa7, 0xd5, 0x9c, 0xff, 0x47, 0xfa, 0x59, 0x3c, 0x77, 0xb6, 0x98, 0xe8,
0x4c, 0x77, 0xfe, 0x69, 0xf5, 0xbf, 0x77, 0x1f, 0x57, 0xf7, 0xe6, 0xcf, 0x33, 0xec, 0xf6, 0xd1, 0xfa, 0x9f, 0x49, 0x8b,
0xaf, 0x39, 0x5f, 0x79, 0x5f, 0xff, 0xaf, 0x5c, 0xd9, 0xb8, 0xa3, 0xfe, 0x6f, 0xa9, 0xf2, 0xb1, 0x7d, 0xbe, 0xb6, 0xfe,
0xcf, 0xd7, 0xd0, 0x6c, 0xf2, 0xfb, 0xf9, 0x6d, 0x8f, 0xb2, 0xf3, 0xff, 0x9a, 0x75, 0xa1, 0xea, 0x79, 0xc4, 0x6d, 0x3d,
0xe8, 0xaa, 0xa7, 0xab, 0xbd, 0xcd, 0x1d, 0xaa, 0xcf, 0x77, 0xef, 0x18, 0xff, 0x43, 0xfd, 0x2f, 0x58, 0x0b, 0xaf, 0x1d,
0x25, 0xf3, 0x2b, 0xcc, 0x33, 0xfd, 0xc9, 0xf8, 0xb3, 0xec, 0x6b, 0x57, 0xc9, 0x32, 0x7d, 0x68, 0xcf, 0x2b, 0xf9, 0x27,
0x6e, 0x7c, 0x75, 0xfe, 0xdf, 0xf3, 0x54, 0xa0, 0x6f, 0xd5, 0x7f, 0x24, 0xd7, 0xb7, 0xee, 0xa8, 0xff, 0xb5, 0x57, 0x06,
0xd6, 0x5f, 0xcb, 0xbf, 0x79, 0xfe, 0xbf, 0xa2, 0xcf, 0xfb, 0x76, 0xfd, 0x7f, 0x61, 0x0e, 0xbd, 0xe6, 0x5b, 0x28, 0xe7,
0xea, 0x7f, 0x6e, 0xdc, 0x56, 0xff, 0x2b, 0x56, 0x8c, 0xf3, 0x9f, 0x7a, 0xc5, 0xf9, 0x7f, 0xef, 0x4c, 0xe3, 0xe7, 0xd7,
0x7f, 0x7d, 0xd2, 0xf9, 0x9a, 0xf3, 0xcd, 0x76, 0xe9, 0x77, 0x61, 0xee, 0xfa, 0x4e, 0xf2, 0x9e, 0x16, 0xb3, 0x66, 0xed,
0x23, 0xf3, 0x94, 0xde, 0xda, 0xa7, 0x39, 0xc5, 0xa2, 0x4a, 0xcf, 0x5d, 0xe5, 0xdd, 0x73, 0xfd, 0x7f, 0xef, 0x37, 0x8a,
0xaa, 0xb7, 0x50, 0xfd, 0x7f, 0xa3, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xdb,
0xee, 0xd0, 0xdb, 0x9b, 0xa8, 0x5f, 0x9f, 0x82, 0x71, 0x7b, 0xa2, 0xfe, 0xdb, 0x5d, 0x33, 0xed, 0xda, 0x44, 0xfd, 0xd6,
0x95, 0x4c, 0xf4, 0xbd, 0x44, 0xfd, 0xdf, 0x78, 0xc7, 0xc7, 0x6d, 0x89, 0xfa, 0x75, 0x77, 0x4e, 0xdf, 0x9e, 0xa8, 0xdf,
0x5e, 0xf2, 0x87, 0x6e, 0x4e, 0xd4, 0xcf, 0xdf, 0x3b, 0x75, 0x6b, 0xa2, 0x3e, 0x6b, 0xef, 0x98, 0xec, 0xed, 0xfb, 0xe3,
0xe0, 0x31, 0x5b, 0x75, 0x77, 0x69, 0x7c, 0xf4, 0xce, 0xcf, 0xf1, 0x8c, 0xa6, 0x15, 0x3d, 0x4a, 0xe5, 0x5d, 0x9a, 0xeb,
0x13, 0xf5, 0x7f, 0x4a, 0xbd, 0xee, 0x4a, 0xd4, 0x7f, 0xcf, 0x19, 0x8b, 0x4d, 0xdb, 0x1f, 0x43, 0xf3, 0xc6, 0xb5, 0x89,
0x5a, 0xcf, 0xfd, 0xe8, 0xbd, 0xcf, 0x47, 0xf8, 0x76, 0xa2, 0xf6, 0x6f, 0xab, 0xff, 0x55, 0xfb, 0x29, 0x8a, 0xaa, 0xae,
0xf7, 0x95, 0x7d, 0x59, 0x41, 0x75, 0xf5, 0x1f, 0xe5, 0xaf, 0xed, 0x4d, 0xd4, 0xca, 0xae, 0x27, 0xa8, 0xff, 0xef, 0xac,
0x01, 0xe4, 0xd6, 0xad, 0xe6, 0x12, 0x75, 0x4f, 0xb6, 0x80, 0x9d, 0x89, 0x5a, 0x5f, 0xaf, 0xff, 0xcc, 0xac, 0x5c, 0xfd,
0xdf, 0x39, 0xff, 0x8f, 0xb2, 0x57, 0x32, 0xbd, 0x46, 0x7e, 0xfd, 0xff, 0xcb, 0x4f, 0xd4, 0xfa, 0x99, 0xf5, 0x7f, 0x47,
0xa2, 0xae, 0xfa, 0x3f, 0x33, 0xff, 0x5f, 0xf7, 0x54, 0x82, 0x9d, 0x89, 0xba, 0xc6, 0xff, 0x55, 0x15, 0x71, 0x77, 0x95,
0xdf, 0x95, 0x9b, 0xfb, 0xcd, 0xf3, 0xff, 0xdc, 0x93, 0x40, 0xb3, 0x3d, 0xc0, 0xbe, 0x84, 0xd6, 0x13, 0x89, 0xfa, 0x12,
0x75, 0x67, 0x8e, 0xd7, 0xf3, 0xec, 0xef, 0x9b, 0xd7, 0x63, 0x6e, 0x9f, 0xff, 0xd7, 0x5e, 0xab, 0xf9, 0xe7, 0x9a, 0xd2,
0xea, 0x15, 0xef, 0x55, 0xf3, 0xff, 0xda, 0x2b, 0x5b, 0x12, 0x75, 0xab, 0x8f, 0xda, 0x6f, 0x5f, 0xff, 0x3b, 0xdb, 0xd3,
0xc0, 0xee, 0x79, 0xed, 0xf9, 0x9e, 0x10, 0xf8, 0xda, 0x37, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xf9, 0xef, 0x3f, 0xfb, 0x01, 0xd4, 0x3f,
0xf0, 0xeb, 0xea, 0xff, 0x3f };
// Font glyphs rectangles data (on atlas)
static const Rectangle darkFontRecs[189] = {
{ 4, 4, 4 , 16 },
{ 16, 4, 1 , 9 },
{ 25, 4, 3 , 3 },
{ 36, 4, 6 , 9 },
{ 50, 4, 5 , 13 },
{ 63, 4, 7 , 9 },
{ 78, 4, 5 , 9 },
{ 91, 4, 1 , 3 },
{ 100, 4, 3 , 9 },
{ 111, 4, 3 , 9 },
{ 122, 4, 5 , 5 },
{ 135, 4, 5 , 5 },
{ 148, 4, 2 , 3 },
{ 158, 4, 4 , 1 },
{ 170, 4, 1 , 1 },
{ 179, 4, 3 , 9 },
{ 190, 4, 5 , 9 },
{ 203, 4, 3 , 9 },
{ 214, 4, 5 , 9 },
{ 227, 4, 5 , 9 },
{ 240, 4, 5 , 9 },
{ 253, 4, 5 , 9 },
{ 266, 4, 5 , 9 },
{ 279, 4, 5 , 9 },
{ 292, 4, 5 , 9 },
{ 305, 4, 5 , 9 },
{ 318, 4, 1 , 7 },
{ 327, 4, 2 , 9 },
{ 337, 4, 3 , 5 },
{ 348, 4, 4 , 3 },
{ 360, 4, 3 , 5 },
{ 371, 4, 5 , 9 },
{ 384, 4, 7 , 9 },
{ 399, 4, 5 , 9 },
{ 412, 4, 5 , 9 },
{ 425, 4, 5 , 9 },
{ 438, 4, 5 , 9 },
{ 451, 4, 5 , 9 },
{ 464, 4, 5 , 9 },
{ 477, 4, 5 , 9 },
{ 490, 4, 5 , 9 },
{ 4, 28, 1 , 9 },
{ 13, 28, 5 , 9 },
{ 26, 28, 5 , 9 },
{ 39, 28, 5 , 9 },
{ 52, 28, 7 , 9 },
{ 67, 28, 5 , 9 },
{ 80, 28, 5 , 9 },
{ 93, 28, 5 , 9 },
{ 106, 28, 5 , 9 },
{ 119, 28, 5 , 9 },
{ 132, 28, 5 , 9 },
{ 145, 28, 5 , 9 },
{ 158, 28, 5 , 9 },
{ 171, 28, 5 , 9 },
{ 184, 28, 7 , 9 },
{ 199, 28, 5 , 9 },
{ 212, 28, 5 , 9 },
{ 225, 28, 5 , 9 },
{ 238, 28, 3 , 9 },
{ 249, 28, 3 , 9 },
{ 260, 28, 3 , 9 },
{ 271, 28, 5 , 3 },
{ 284, 28, 5 , 1 },
{ 297, 28, 2 , 2 },
{ 307, 28, 5 , 7 },
{ 320, 28, 5 , 9 },
{ 333, 28, 5 , 7 },
{ 346, 28, 5 , 9 },
{ 359, 28, 5 , 7 },
{ 372, 28, 4 , 9 },
{ 384, 28, 5 , 9 },
{ 397, 28, 5 , 9 },
{ 410, 28, 1 , 9 },
{ 419, 28, 5 , 11 },
{ 432, 28, 5 , 9 },
{ 445, 28, 2 , 9 },
{ 455, 28, 7 , 7 },
{ 470, 28, 5 , 7 },
{ 483, 28, 5 , 7 },
{ 496, 28, 5 , 9 },
{ 4, 52, 5 , 9 },
{ 17, 52, 5 , 7 },
{ 30, 52, 5 , 7 },
{ 43, 52, 4 , 8 },
{ 55, 52, 5 , 7 },
{ 68, 52, 5 , 7 },
{ 81, 52, 7 , 7 },
{ 96, 52, 5 , 7 },
{ 109, 52, 5 , 9 },
{ 122, 52, 5 , 7 },
{ 135, 52, 4 , 9 },
{ 147, 52, 1 , 9 },
{ 156, 52, 4 , 9 },
{ 168, 52, 6 , 2 },
{ 182, 52, 1 , 9 },
{ 191, 52, 5 , 11 },
{ 204, 52, 6 , 9 },
{ 218, 52, 6 , 9 },
{ 232, 52, 5 , 9 },
{ 245, 52, 5 , 12 },
{ 258, 52, 5 , 9 },
{ 271, 52, 5 , 10 },
{ 284, 52, 7 , 9 },
{ 299, 52, 5 , 9 },
{ 312, 52, 6 , 5 },
{ 326, 52, 5 , 3 },
{ 339, 52, 7 , 9 },
{ 354, 52, 5 , 9 },
{ 367, 52, 4 , 4 },
{ 379, 52, 5 , 7 },
{ 392, 52, 5 , 9 },
{ 405, 52, 5 , 9 },
{ 418, 52, 5 , 12 },
{ 431, 52, 5 , 9 },
{ 444, 52, 7 , 9 },
{ 459, 52, 1 , 1 },
{ 468, 52, 5 , 10 },
{ 481, 52, 5 , 9 },
{ 494, 52, 5 , 9 },
{ 4, 76, 6 , 5 },
{ 18, 76, 9 , 9 },
{ 35, 76, 9 , 7 },
{ 52, 76, 5 , 11 },
{ 65, 76, 5 , 9 },
{ 78, 76, 5 , 12 },
{ 91, 76, 5 , 12 },
{ 104, 76, 5 , 12 },
{ 117, 76, 6 , 12 },
{ 131, 76, 5 , 11 },
{ 144, 76, 5 , 13 },
{ 157, 76, 9 , 9 },
{ 174, 76, 5 , 12 },
{ 187, 76, 5 , 12 },
{ 200, 76, 5 , 12 },
{ 213, 76, 5 , 12 },
{ 226, 76, 5 , 11 },
{ 239, 76, 2 , 12 },
{ 249, 76, 2 , 12 },
{ 259, 76, 3 , 12 },
{ 270, 76, 3 , 11 },
{ 281, 76, 6 , 9 },
{ 295, 76, 6 , 12 },
{ 309, 76, 5 , 12 },
{ 322, 76, 5 , 12 },
{ 335, 76, 5 , 12 },
{ 348, 76, 6 , 12 },
{ 362, 76, 5 , 11 },
{ 375, 76, 5 , 5 },
{ 388, 76, 7 , 9 },
{ 403, 76, 5 , 12 },
{ 416, 76, 5 , 12 },
{ 429, 76, 5 , 12 },
{ 442, 76, 5 , 11 },
{ 455, 76, 5 , 12 },
{ 468, 76, 5 , 9 },
{ 481, 76, 5 , 9 },
{ 494, 76, 5 , 10 },
{ 4, 100, 5 , 10 },
{ 17, 100, 5 , 10 },
{ 30, 100, 6 , 10 },
{ 44, 100, 5 , 9 },
{ 57, 100, 5 , 11 },
{ 70, 100, 9 , 7 },
{ 87, 100, 5 , 10 },
{ 100, 100, 5 , 10 },
{ 113, 100, 5 , 10 },
{ 126, 100, 5 , 10 },
{ 139, 100, 5 , 9 },
{ 152, 100, 2 , 10 },
{ 162, 100, 2 , 10 },
{ 172, 100, 3 , 10 },
{ 183, 100, 3 , 9 },
{ 194, 100, 6 , 9 },
{ 208, 100, 6 , 10 },
{ 222, 100, 5 , 10 },
{ 235, 100, 5 , 10 },
{ 248, 100, 5 , 10 },
{ 261, 100, 6 , 10 },
{ 275, 100, 5 , 9 },
{ 288, 100, 5 , 5 },
{ 301, 100, 7 , 7 },
{ 316, 100, 5 , 10 },
{ 329, 100, 5 , 10 },
{ 342, 100, 5 , 10 },
{ 355, 100, 5 , 9 },
{ 368, 100, 5 , 12 },
{ 381, 100, 5 , 11 },
{ 394, 100, 5 , 11 },
};
// Font glyphs info data
// NOTE: No glyphs.image data provided
static const GlyphInfo darkFontGlyphs[189] = {
{ 32, 0, 13, 4, { 0 }},
{ 33, 2, 4, 5, { 0 }},
{ 34, 2, 4, 7, { 0 }},
{ 35, 1, 4, 8, { 0 }},
{ 36, 1, 2, 7, { 0 }},
{ 37, 1, 4, 9, { 0 }},
{ 38, 1, 4, 7, { 0 }},
{ 39, 2, 4, 5, { 0 }},
{ 40, 3, 4, 7, { 0 }},
{ 41, 1, 4, 7, { 0 }},
{ 42, 1, 4, 7, { 0 }},
{ 43, 1, 6, 7, { 0 }},
{ 44, 1, 12, 5, { 0 }},
{ 45, 1, 8, 6, { 0 }},
{ 46, 2, 12, 5, { 0 }},
{ 47, 1, 4, 5, { 0 }},
{ 48, 1, 4, 7, { 0 }},
{ 49, 2, 4, 7, { 0 }},
{ 50, 1, 4, 7, { 0 }},
{ 51, 1, 4, 7, { 0 }},
{ 52, 1, 4, 7, { 0 }},
{ 53, 1, 4, 7, { 0 }},
{ 54, 1, 4, 7, { 0 }},
{ 55, 1, 4, 7, { 0 }},
{ 56, 1, 4, 7, { 0 }},
{ 57, 1, 4, 7, { 0 }},
{ 58, 2, 6, 5, { 0 }},
{ 59, 1, 6, 5, { 0 }},
{ 60, 1, 6, 5, { 0 }},
{ 61, 1, 7, 6, { 0 }},
{ 62, 1, 6, 5, { 0 }},
{ 63, 1, 4, 7, { 0 }},
{ 64, 1, 4, 9, { 0 }},
{ 65, 1, 4, 7, { 0 }},
{ 66, 1, 4, 7, { 0 }},
{ 67, 1, 4, 7, { 0 }},
{ 68, 1, 4, 7, { 0 }},
{ 69, 1, 4, 7, { 0 }},
{ 70, 1, 4, 7, { 0 }},
{ 71, 1, 4, 7, { 0 }},
{ 72, 1, 4, 7, { 0 }},
{ 73, 2, 4, 5, { 0 }},
{ 74, 1, 4, 7, { 0 }},
{ 75, 1, 4, 7, { 0 }},
{ 76, 1, 4, 7, { 0 }},
{ 77, 1, 4, 9, { 0 }},
{ 78, 1, 4, 7, { 0 }},
{ 79, 1, 4, 7, { 0 }},
{ 80, 1, 4, 7, { 0 }},
{ 81, 1, 4, 7, { 0 }},
{ 82, 1, 4, 7, { 0 }},
{ 83, 1, 4, 7, { 0 }},
{ 84, 1, 4, 7, { 0 }},
{ 85, 1, 4, 7, { 0 }},
{ 86, 1, 4, 7, { 0 }},
{ 87, 1, 4, 9, { 0 }},
{ 88, 1, 4, 7, { 0 }},
{ 89, 1, 4, 7, { 0 }},
{ 90, 1, 4, 7, { 0 }},
{ 91, 3, 4, 7, { 0 }},
{ 92, 1, 4, 5, { 0 }},
{ 93, 1, 4, 7, { 0 }},
{ 94, 1, 4, 7, { 0 }},
{ 95, 0, 14, 5, { 0 }},
{ 96, 1, 4, 5, { 0 }},
{ 97, 1, 6, 7, { 0 }},
{ 98, 1, 4, 7, { 0 }},
{ 99, 1, 6, 7, { 0 }},
{ 100, 1, 4, 7, { 0 }},
{ 101, 1, 6, 7, { 0 }},
{ 102, 1, 4, 6, { 0 }},
{ 103, 1, 6, 7, { 0 }},
{ 104, 1, 4, 7, { 0 }},
{ 105, 2, 4, 5, { 0 }},
{ 106, 1, 4, 7, { 0 }},
{ 107, 1, 4, 7, { 0 }},
{ 108, 2, 4, 5, { 0 }},
{ 109, 1, 6, 9, { 0 }},
{ 110, 1, 6, 7, { 0 }},
{ 111, 1, 6, 7, { 0 }},
{ 112, 1, 6, 7, { 0 }},
{ 113, 1, 6, 7, { 0 }},
{ 114, 1, 6, 7, { 0 }},
{ 115, 1, 6, 7, { 0 }},
{ 116, 1, 5, 6, { 0 }},
{ 117, 1, 6, 7, { 0 }},
{ 118, 1, 6, 7, { 0 }},
{ 119, 1, 6, 9, { 0 }},
{ 120, 1, 6, 7, { 0 }},
{ 121, 1, 6, 7, { 0 }},
{ 122, 1, 6, 7, { 0 }},
{ 123, 2, 4, 7, { 0 }},
{ 124, 2, 4, 5, { 0 }},
{ 125, 1, 4, 7, { 0 }},
{ 126, 1, 4, 8, { 0 }},
{ 161, 2, 6, 5, { 0 }},
{ 162, 1, 4, 7, { 0 }},
{ 163, 1, 4, 8, { 0 }},
{ 8364, 1, 4, 8, { 0 }},
{ 165, 1, 4, 7, { 0 }},
{ 352, 1, 1, 7, { 0 }},
{ 167, 2, 4, 9, { 0 }},
{ 353, 1, 3, 7, { 0 }},
{ 169, 1, 4, 9, { 0 }},
{ 170, 2, 4, 9, { 0 }},
{ 171, 1, 6, 8, { 0 }},
{ 172, 1, 8, 7, { 0 }},
{ 174, 1, 4, 9, { 0 }},
{ 175, 2, 4, 9, { 0 }},
{ 176, 1, 4, 6, { 0 }},
{ 177, 1, 6, 7, { 0 }},
{ 178, 2, 4, 9, { 0 }},
{ 179, 2, 4, 9, { 0 }},
{ 381, 1, 1, 7, { 0 }},
{ 181, 1, 6, 7, { 0 }},
{ 182, 1, 4, 9, { 0 }},
{ 183, 2, 8, 5, { 0 }},
{ 382, 1, 3, 7, { 0 }},
{ 185, 2, 4, 9, { 0 }},
{ 186, 2, 4, 9, { 0 }},
{ 187, 1, 6, 8, { 0 }},
{ 338, 1, 4, 11, { 0 }},
{ 339, 1, 6, 11, { 0 }},
{ 376, 1, 2, 7, { 0 }},
{ 191, 1, 6, 7, { 0 }},
{ 192, 1, 1, 7, { 0 }},
{ 193, 1, 1, 7, { 0 }},
{ 194, 1, 1, 7, { 0 }},
{ 195, 1, 1, 7, { 0 }},
{ 196, 1, 2, 7, { 0 }},
{ 197, 1, 0, 7, { 0 }},
{ 198, 1, 4, 11, { 0 }},
{ 199, 1, 4, 7, { 0 }},
{ 200, 1, 1, 7, { 0 }},
{ 201, 1, 1, 7, { 0 }},
{ 202, 1, 1, 7, { 0 }},
{ 203, 1, 2, 7, { 0 }},
{ 204, 1, 1, 5, { 0 }},
{ 205, 2, 1, 5, { 0 }},
{ 206, 1, 1, 5, { 0 }},
{ 207, 1, 2, 5, { 0 }},
{ 208, 0, 4, 7, { 0 }},
{ 209, 1, 1, 7, { 0 }},
{ 210, 1, 1, 7, { 0 }},
{ 211, 1, 1, 7, { 0 }},
{ 212, 1, 1, 7, { 0 }},
{ 213, 1, 1, 7, { 0 }},
{ 214, 1, 2, 7, { 0 }},
{ 215, 1, 6, 7, { 0 }},
{ 216, 0, 4, 7, { 0 }},
{ 217, 1, 1, 7, { 0 }},
{ 218, 1, 1, 7, { 0 }},
{ 219, 1, 1, 7, { 0 }},
{ 220, 1, 2, 7, { 0 }},
{ 221, 1, 1, 7, { 0 }},
{ 222, 1, 4, 7, { 0 }},
{ 223, 1, 4, 7, { 0 }},
{ 224, 1, 3, 7, { 0 }},
{ 225, 1, 3, 7, { 0 }},
{ 226, 1, 3, 7, { 0 }},
{ 227, 1, 3, 7, { 0 }},
{ 228, 1, 4, 7, { 0 }},
{ 229, 1, 2, 7, { 0 }},
{ 230, 1, 6, 11, { 0 }},
{ 231, 1, 6, 7, { 0 }},
{ 232, 1, 3, 7, { 0 }},
{ 233, 1, 3, 7, { 0 }},
{ 234, 1, 3, 7, { 0 }},
{ 235, 1, 4, 7, { 0 }},
{ 236, 1, 3, 5, { 0 }},
{ 237, 2, 3, 5, { 0 }},
{ 238, 1, 3, 5, { 0 }},
{ 239, 1, 4, 5, { 0 }},
{ 240, 1, 4, 7, { 0 }},
{ 241, 1, 3, 7, { 0 }},
{ 242, 1, 3, 7, { 0 }},
{ 243, 1, 3, 7, { 0 }},
{ 244, 1, 3, 7, { 0 }},
{ 245, 1, 3, 7, { 0 }},
{ 246, 1, 4, 7, { 0 }},
{ 247, 1, 6, 7, { 0 }},
{ 248, 0, 6, 7, { 0 }},
{ 249, 1, 3, 7, { 0 }},
{ 250, 1, 3, 7, { 0 }},
{ 251, 1, 3, 7, { 0 }},
{ 252, 1, 4, 7, { 0 }},
{ 253, 1, 3, 7, { 0 }},
{ 254, 1, 4, 7, { 0 }},
{ 255, 1, 4, 7, { 0 }},
};
// Style loading function: Dark
static void GuiLoadStyleDark(void)
{
// Load style properties provided
// NOTE: Default properties are propagated
for (int i = 0; i < DARK_STYLE_PROPS_COUNT; i++)
{
GuiSetStyle(darkStyleProps[i].controlId, darkStyleProps[i].propertyId, darkStyleProps[i].propertyValue);
}
// Custom font loading
// NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
int darkFontDataSize = 0;
unsigned char *data = DecompressData(darkFontData, DARK_STYLE_FONT_ATLAS_COMP_SIZE, &darkFontDataSize);
Image imFont = { data, 512, 256, 1, 2 };
Font font = { 0 };
font.baseSize = 16;
font.glyphCount = 189;
// Load texture from image
font.texture = LoadTextureFromImage(imFont);
UnloadImage(imFont); // Uncompressed image data can be unloaded from memory
// Copy char recs data from global fontRecs
// NOTE: Required to avoid issues if trying to free font
font.recs = (Rectangle *)RAYGUI_MALLOC(font.glyphCount*sizeof(Rectangle));
memcpy(font.recs, darkFontRecs, font.glyphCount*sizeof(Rectangle));
// Copy font char info data from global fontChars
// NOTE: Required to avoid issues if trying to free font
font.glyphs = (GlyphInfo *)RAYGUI_MALLOC(font.glyphCount*sizeof(GlyphInfo));
memcpy(font.glyphs, darkFontGlyphs, font.glyphCount*sizeof(GlyphInfo));
GuiSetFont(font);
// Setup a white rectangle on the font to be used on shapes drawing,
// it makes possible to draw shapes and text (full UI) in a single draw call
Rectangle fontWhiteRec = { 510, 254, 1, 1 };
SetShapesTexture(font.texture, fontWhiteRec);
//-----------------------------------------------------------------
// TODO: Custom user style setup: Set specific properties here (if required)
// i.e. Controls specific BORDER_WIDTH, TEXT_PADDING, TEXT_ALIGNMENT
}

View File

@@ -0,0 +1,9 @@
#ifndef _RAYGUI_HELPERS_HPP
#define _RAYGUI_HELPERS_HPP
#include <string>
/* Display the given text, centered on the screen, as a label.
NEEDS RAYGUI LIBRARY. */
void display_text_centered(std::string to_disp);
#endif

View File

@@ -1,8 +0,0 @@
#ifndef _MATH_HELP
#define _MATH_HELP
int signum(int num) {
int retval = 0;
(num > 0) ? retval = 1 : retval = -1;
return retval;
}
#endif

View File

@@ -2,7 +2,7 @@
#define _SOCK_CLASS
#include <string>
#ifdef __unix__
#if defined(__unix__) || defined(__unix) ||(defined(__APPLE__) && defined(__MACH__))
#include <sys/socket.h>
#endif
#ifdef _WIN32

30
includes/timer.h Normal file
View File

@@ -0,0 +1,30 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _TIMER_H
#define _TIMER_H
#include <stdbool.h>
/* This file defines a simple timer Types, and declares functions to initialize it,
and keep track of time elapsed. The actual definition of the Timer struct is in timer.c
It was copied from https://github.com/raysan5/raylib/wiki/Frequently-Asked-Questions#how-do-i-make-a-timer */
typedef struct Timer_s {
double start_time; // Start time (seconds)
double lifetime; // Lifetime (seconds)
} Timer;
/* Starts a timer for given number of seconds */
Timer timer_init(double lifetime_secs);
/* Returns true when timer finishes, false if not */
bool timer_done(Timer timer);
/* Returns amount of time elapsed since start of timer */
double timer_get_elapsed(Timer timer);
#endif
#ifdef __cplusplus
}
#endif

158
main.cpp
View File

@@ -19,15 +19,21 @@
#include <ctime>
#include <sstream>
#include "includes/raylib-cpp/raylib-cpp.hpp"
#define RAYGUI_IMPLEMENTATION
#include "includes/raygui/raygui.h"
#include "includes/raygui/style_dark.h"
#include "includes/paddle.hpp"
#include "includes/ball.hpp"
#include "includes/easysock.hpp"
#include "includes/sign.hpp"
#include "includes/connect_code.hpp"
#include "includes/client.hpp"
#include "includes/server.hpp"
#include "includes/exception_consts.hpp"
#include "includes/check_input.hpp"
#include "includes/raygui_helpers.hpp"
#include "includes/easysock.h"
#include "includes/serialization.h"
#include "includes/timer.h"
/* Global variables used to instantiate structs */
const int WIDTH = 1500;
@@ -38,21 +44,15 @@ const int PADDLE_SPEED = 8;
const int CIRC_RAD = 10;
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_COMPONENTS = 15;
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
playing (Single player, client mode or server mode). The netsock parameter is
a 'Sock' object - Client and Server classes inherit from this object, so this
parameter can be instantiated to either a client or server, depending on the
game type. */
typedef struct {
Mode mode;
Sock* netsock;
} GameType;
/* Simple function to return 1 if a value is positive, and -1 if it is negative */
int signum(int num) {
int retval = 0;
(num > 0) ? retval = 1 : retval = -1;
return retval;
}
raylib::Vector2 changeVelocityAfterCollision(Paddle paddle, Ball ball) {
float paddle_mid_y = (paddle.getRect().y + paddle.getRect().GetHeight()) / 2.0; /* Middle y value of rectangle */
@@ -153,7 +153,8 @@ GameType check_server_client(int argc, char** argv) {
std::cout << "Waiting for connection..." << std::endl;
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. */
/* Wait for the right client to connect. Since recvAll returns a char*, we need to create a temporary variable to check for NULL.
TODO - Check that the client actually sends 'GG'. */
do {
temp_response = server->recvAll();
} while (temp_response == NULL);
@@ -217,6 +218,129 @@ int main(int argc, char** argv) {
bool game_started = false;
srand(std::time(NULL));
/* If there were no command-line arguments, the user is prompted in the GUI */
if (argc == 1) {
/* Display a drop-down menu, to allow user to pick between Single player, server and client. This section of the code uses the raygui library, and is written in C. */
GuiLoadStyleDark(); // Load the dark theme style
/* Modify the default style, by changing font size and spacing */
int font_size = 25;
int font_spacing = 2;
GuiSetStyle(DEFAULT, TEXT_SIZE, font_size);
GuiSetStyle(DEFAULT, TEXT_SPACING, font_spacing);
/* Set variables to position objects on screen */
int selected_item = 0; // variable to hold the index of the selected item
const char* text_to_display = "Select Game Mode"; // Text to display
/* Size of the label, drop down box and button */
Vector2 label_size = MeasureTextEx(GetFontDefault(), text_to_display, font_size, font_spacing); // Set the size based on the width of the string to print, the font size and the text spacing. I added 1 to font_size and font_spacing, to account for any possible rounding errors, since the function expects floats.
Vector2 box_size = Vector2{label_size.x, HEIGHT / 20};
bool is_being_edited = false; // Indicates whether the drop-down menu is being 'edited' i.e. whether an option is being selected
bool button_pressed = false; // Indicates whether the submit button has been pressed
while (button_pressed == false) {
BeginDrawing();
ClearBackground(BLACK);
GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/8), label_size.x, label_size.y}, text_to_display); // Label to display text on top
if (is_being_edited) {
GuiLock(); // If the drop-down menu is being 'edited', we need to prevent the user from modifying any other aspect of the UI
}
/* Button that allows user to proceed */
button_pressed = GuiButton(Rectangle{(WIDTH/2)-(box_size.x/2), (HEIGHT/2) + (HEIGHT/8), box_size.x, box_size.y}, "Continue");
/* Drop-down menu, that allows user to select game mode */
if (GuiDropdownBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/8), box_size.x, box_size.y}, "SINGLE;CLIENT;SERVER", &selected_item, is_being_edited)) { // This function returns != 0 if there was a mouse click inside the dropdown area
is_being_edited = !is_being_edited; // If the dropdown menu was selected, then it is being edited (or not being edited, if it previously was).
}
GuiUnlock();
EndDrawing();
}
/* Single player mode */
if (selected_item == M_SINGLE) {
GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // Enable text wrapping so that the long text, displayed below, will be wrapped
BeginDrawing();
ClearBackground(BLACK);
GuiLabel(Rectangle{(WIDTH/2)-(WIDTH/8), (HEIGHT/2)-(HEIGHT/8), WIDTH/4, HEIGHT/4}, "W and S control left paddle, Up and Down arrow keys control right paddle. Good luck!");
EndDrawing();
Timer timer = timer_init(5);
while (!timer_done(timer));
}
/* Server mode, ask user to input IP address and port */
if (selected_item == M_SERVER) {
button_pressed = false; // Whether submit button is pressed
char* ip_text = (char *)calloc(150, sizeof(char)); // Holds input of IP text box
char* port_text = (char *)calloc(20, sizeof(char)); // Holds input of port text box
const char* ip_label = "Local IP address";
const char* port_label = "Port number (1024 - 65535)";
int port_label_x_size = MeasureTextEx(GetFontDefault(), port_label, font_size, font_spacing).x; // Custom size for port label, because it's long
bool editing_ip = false; // Indicates whether the IP address text box is being edited
bool editing_port = false; // Indicates whether the port text box is being edited
while (button_pressed == false || ((strlen(ip_text) == 0) || (strlen(port_text) == 0))) {
BeginDrawing();
ClearBackground(BLACK);
/* Label and text box for IP address */
GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - (HEIGHT/6) - label_size.y - 10, label_size.x, label_size.y}, ip_label); // Label to display text on top
/* The reason this if statement exists, is largely the same as the reasoning for the drop-down menu. We want to make the text box editable
if it has been clicked. If it is already editable, we want to make it read-only if the user clicks outside the box. This functionality
is mostly handled in the GuiTextBox function. If the text box is in edit mode, this function returns nonzero if the user clicks INSIDE
the box. If the text box is in editable mode, this function returns nonzero if the user clicks OUTSIDE the box. */
if (GuiTextBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/6), box_size.x, box_size.y}, ip_text, 100, editing_ip)) {
editing_ip = !editing_ip;
}
/* Label and text box for port. See above for explanation of if statement. */
GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - label_size.y, port_label_x_size }, port_label); // Label to display text on top
if (GuiTextBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2), box_size.x, box_size.y}, port_text, 100, editing_port)) {
editing_port = !editing_port;
}
button_pressed = GuiButton(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) + (HEIGHT/6), box_size.x, box_size.y}, "Start Server");
EndDrawing();
}
try {
type = check_server(ip_text, port_text);
} catch (std::invalid_argument& inv) {
display_text_centered(std::string(inv.what()) + "\nClosing game...");
Timer timer = timer_init(2); // Wait for two seconds
while (!timer_done(timer));
CloseWindow(); // Close and exit
return -1;
}
free(ip_text);
free(port_text);
}
if (selected_item == M_CLIENT) {
button_pressed = false; // Whether submit button is pressed
char* code_text = (char *)calloc(150, sizeof(char)); // Holds the connect code
const char* code_label = "Enter code:";
bool editing_code = false; // Indicates whether the port text box is being edited
while (button_pressed == false || ((strlen(code_text) == 0))) {
BeginDrawing();
ClearBackground(BLACK);
/* Label and text box for IP address */
GuiLabel(Rectangle{(WIDTH/2)-(label_size.x/2), (HEIGHT/2) - (HEIGHT/6) - label_size.y - 10, label_size.x, label_size.y}, code_label);
if (GuiTextBox(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) - (HEIGHT/6), box_size.x, box_size.y}, code_text, 100, editing_code)) {
editing_code = !editing_code;
}
button_pressed = GuiButton(Rectangle{(WIDTH/2) - (box_size.x/2), (HEIGHT/2) + (HEIGHT/6), box_size.x, box_size.y}, "Connect");
EndDrawing();
}
type = check_client(code_text);
free(code_text);
}
}
/* Variable to store the response given by the other player */
std::string response;
Serial_Data response_data;
@@ -233,6 +357,7 @@ int main(int argc, char** argv) {
Ball ball = Ball(window.GetWidth()/2, window.GetHeight()/2, CIRC_RAD, BASE_SPEED, 0);
window.BeginDrawing();
window.ClearBackground(BLACK);
pad1.draw();
pad2.draw();
ball.draw();
@@ -281,7 +406,6 @@ int main(int argc, char** argv) {
uint8_t* response_array = (uint8_t *)(type.netsock->recvAll());
if (response_array != NULL) {
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 {
/* If the response is NULL, that means it timed-out. In this case, there's no value to print */
std::cout << "NOTHING RECEIVED" << std::endl;

View File

@@ -1,21 +1,25 @@
project('Pong', ['cpp', 'c'], version: '0.1')
add_global_arguments('-g', '-Wall', '-pedantic', '-Wno-unused-function', language : ['cpp', 'c'])
add_global_arguments('-g', '-Wall', '-pedantic', '-Wno-unused-function', '-Wno-narrowing', language : ['cpp', 'c'])
add_global_arguments('-std=c++11', language: ['cpp'])
compiler = meson.get_compiler('cpp')
cmake = import('cmake')
# if we are building a shared library
if get_option('default_library') == 'shared'
raylib = dependency('raylib', required: false) # Try to find dependency with pkg-config
if not raylib.found()
raylib = compiler.find_library('raylib', has_headers: ['raylib.h', 'raymath.h'], required: true) # Try to manually search for the dependency
raylib = compiler.find_library('raylib', has_headers: ['raylib.h', 'raymath.h'], required: false) # Try to manually search for the dependency
endif
if not raylib.found()
opt_var = cmake.subproject_options()
opt_var.add_cmake_defines({'BUILD_SHARED_LIBS' : true})
opt_var.add_cmake_defines({'CMAKE_SKIP_RPATH' : true})
raylib_proj = cmake.subproject('raylib', options: opt_var)
raylib = raylib_proj.dependency('raylib')
endif
# if not raylib.found()
# opt_var = cmake.subproject_options()
# opt_var.add_cmake_defines({'BUILD_SHARED_LIBS' : true})
# opt_var.add_cmake_defines({'CMAKE_SKIP_RPATH' : true})
# raylib_proj = cmake.subproject('raylib', options: opt_var)
# raylib = raylib_proj.dependency('raylib')
# endif
endif
# I we are building a static library
if get_option('default_library') == 'static'
raylib_proj = cmake.subproject('raylib')
raylib = raylib_proj.dependency('raylib')
@@ -29,8 +33,8 @@ if build_machine.system() == 'windows'
endif
executable('pong',
'main.cpp', 'easysock.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp',
'serialization.c',
executable('pong',
'main.cpp', 'sock.cpp','paddle.cpp', 'ball.cpp', 'numeric_base.cpp', 'connect_code.cpp', 'server.cpp', 'client.cpp', 'check_input.cpp', 'raygui_helpers.cpp',
'serialization.c', 'timer.c', 'easysock.c',
dependencies: [raylib, ws2_dep, winmm]
)

View File

@@ -26,12 +26,17 @@ unsigned int to_decimal(std::string num, int from_base) {
std::string from_decimal(unsigned int num, int to_base) {
std::string return_val;
int val = 0;
while (num > 0) {
val = num % to_base;
return_val.push_back(possible_chars[val]);
num /= to_base;
/* Handle the special case of num being zero: In this case, the result is also zero */
if (num == 0) {
return_val = "0";
} else {
while (num > 0) {
val = num % to_base;
return_val.push_back(possible_chars[val]);
num /= to_base;
}
}
/* Reverse the string, since we started from the right */
std::reverse(return_val.begin(), return_val.end());

13
raygui_helpers.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include "includes/raygui_helpers.hpp"
#include "includes/raygui/raygui.h"
void display_text_centered(std::string to_disp) {
const char* to_disp_cstr = to_disp.c_str();
Vector2 label_size = MeasureTextEx(GetFontDefault(), to_disp_cstr, GuiGetStyle(DEFAULT, TEXT_SIZE)+1, GuiGetStyle(DEFAULT, TEXT_SPACING)+1); // The '+1' is there to account for any rounding errors
BeginDrawing();
ClearBackground(BLACK);
GuiLabel(Rectangle{(GetScreenWidth()/2) - (label_size.x/2), (GetScreenHeight()/2) - (label_size.y/2), label_size.x, label_size.y}, to_disp_cstr);
EndDrawing();
return;
}

View File

@@ -2,7 +2,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#ifdef __unix__
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#include <arpa/inet.h>
#endif
#ifdef _WIN32

View File

@@ -1,4 +1,4 @@
#ifdef __unix__
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/socket.h>
#endif
#ifdef _WIN32
@@ -8,8 +8,8 @@
#include "includes/sock.hpp"
#include "includes/server.hpp"
#include "includes/exception_consts.hpp"
#include "includes/easysock.hpp"
#include "includes/connect_code.hpp"
#include "includes/easysock.h"
/* Destructor - closes any open sockets */
Server::~Server() {

View File

@@ -2,7 +2,7 @@
#include <stdexcept>
#include "includes/sock.hpp"
#include "includes/exception_consts.hpp"
#include "includes/easysock.hpp"
#include "includes/easysock.h"
/* Function to create socket. This function doesn't actually create a socket
(and isn't meant to be called directly). Instead, the client and server classes

19
timer.c Normal file
View File

@@ -0,0 +1,19 @@
#include <stdbool.h>
#include "includes/timer.h"
#include "includes/raygui/raygui.h"
Timer timer_init(double lifetime_secs) {
Timer timer;
timer.start_time = GetTime();
timer.lifetime = lifetime_secs;
return timer;
}
bool timer_done(Timer timer) {
return GetTime() - timer.start_time >= timer.lifetime;
}
double timer_get_elapsed(Timer timer) {
return GetTime() - timer.start_time;
}

View File

@@ -1,2 +1,13 @@
3. Try to make the ball go between screens.
8. Have both client and server send a message if the game is quit.
1. Try to make the ball go between screens.
2. ----SHOULD BE DONE---- Add code to zip the dist/ folder inside the release_build script.
3. Sign Windows executable, to remove 'Unknown Publisher' warnings.
4. Create and publish statically-linked Linux binary, and create a build script for packaging it.
5. Figure out how to build statically-linked Mac binary, and create a build script for packaging it.
6. ----IN PROGRESS---- Figure out how to input game mode and (if applicable) IP address and port through the GUI, instead of the command-line.
7. Clean up / refactor the raygui code in main.cpp, that asks user for game mode. Instead of just having a giant blob of code in main.cpp, maybe split it into a function, or move it to another file. It should be easy to split it into a different function, since none of the functions take any specific parameters. The text box function, for example, only takes in the rectangle coordinates, and the text to display. I can move the code to a function, and then pass in any parameters that I need to pass in (I don't think I need to pass many parameters, though).
8. Allow the user to quit before the game actually starts i.e. while they are inputting the game mode.
9. Add better error checking in check_server and check_client functions in check_input.cpp.
10. Add 'install' target to Meson, to allow the user to install the game. This should also copy the .so files to the right locations.
11. Allow the user to specify which paddle they want to control, in multi-player mode.
12. Add IPv6 support for the server and client sockets (and everything that goes along with it, such as error handling for IP addresses).
13. Figure out how to make 'tar' not include the entire directory structure, when creating the archive in create_static_linux.sh.