diff --git a/GameFiles/Coin.cpp b/GameFiles/Coin.cpp new file mode 100644 index 00000000..bdf63e61 --- /dev/null +++ b/GameFiles/Coin.cpp @@ -0,0 +1,22 @@ +#include "RoboCatPCH.h" +#include "Coin.h" + +Coin::Coin() : GameObject("coinGold.png", 70, 70) +{ + +} + +Coin::Coin(int posX, int posY) : GameObject("coinGold.png", posX, posY, 70, 70) +{ + +} + +Coin::~Coin() +{ +} + +void Coin::Update(WorldState* gameWorld, int delta) +{ + //if (mGraphicsData.mX > 1500) mGraphicsData.mX = 0; + //mGraphicsData.mX++; +} \ No newline at end of file diff --git a/GameFiles/Coin.h b/GameFiles/Coin.h new file mode 100644 index 00000000..0fe471cc --- /dev/null +++ b/GameFiles/Coin.h @@ -0,0 +1,16 @@ +#pragma once +#include "GameObject.h" +class Coin : + public GameObject +{ +private: + Coin(); + Coin(int posX, int posY); + ~Coin(); +protected: +public: + virtual void Update(WorldState* gameWorld, int delta); + + CLASS_IDENTIFICATION('COIN', Coin) +}; + diff --git a/GameFiles/GameObject.cpp b/GameFiles/GameObject.cpp new file mode 100644 index 00000000..6bac294d --- /dev/null +++ b/GameFiles/GameObject.cpp @@ -0,0 +1,60 @@ +#include "RoboCatPCH.h" +#include "..\GameFiles\GameObject.h" +#include "allegro_wrapper_functions-main/GraphicsLibrary.h" + +GameObject::GameObject(std::string graphicsID, int posX, int posY, int width, int height) +{ + mpGraphicsID = new std::string(graphicsID); + mGraphicsData.mX = posX; + mGraphicsData.mY = posY; + mGraphicsData.mZ = width; + mGraphicsData.mW = height; +} + +GameObject::GameObject(std::string graphicsID, int width, int height) +{ + mpGraphicsID = new std::string(graphicsID); + mGraphicsData.mX = 0; + mGraphicsData.mY = 0; + mGraphicsData.mZ = width; + mGraphicsData.mW = height; +} + +void GameObject::Write(OutputMemoryBitStream& stream) +{ + stream.Write(mGraphicsData.mX); + stream.Write(mGraphicsData.mY); + stream.Write(mGraphicsData.mZ); + stream.Write(mGraphicsData.mW); + stream.Write(*mpGraphicsID); +} + +void GameObject::Read(InputMemoryBitStream& stream) +{ + stream.Read(mGraphicsData.mX); + stream.Read(mGraphicsData.mY); + stream.Read(mGraphicsData.mZ); + stream.Read(mGraphicsData.mW); + stream.Read(*mpGraphicsID); +} + +void GameObject::Render(GraphicsLibrary* gl) +{ + gl->drawImage(*mpGraphicsID, mGraphicsData.mX - (mGraphicsData.mZ / 2.0f), mGraphicsData.mY - (mGraphicsData.mW / 2.0f)); +} + +void GameObject::SetPostion(int posX, int posY) +{ + mGraphicsData.mX = posX; + mGraphicsData.mY = posY; +} + +Vector3 GameObject::GetPosition() +{ + return Vector3(mGraphicsData.mX,mGraphicsData.mY,0); +} + +GameObject::~GameObject() +{ + delete mpGraphicsID; +} diff --git a/GameFiles/GameObject.h b/GameFiles/GameObject.h new file mode 100644 index 00000000..02ab8f52 --- /dev/null +++ b/GameFiles/GameObject.h @@ -0,0 +1,35 @@ +#pragma once +#include + +class GraphicsLibrary; +class WorldState; + +class GameObject +{ + private: + protected: + GameObject(std::string graphicsID, int posX, int posY, int width, int height); + GameObject(std::string graphicsID, int width, int height); + Quaternion mGraphicsData; //X,Y Position Z,W Sizing + std::string* mpGraphicsID; + public: + enum { mClassId = 'GOBJ' }; + virtual uint32_t GetClassId() const { return mClassId; } + virtual void Update(WorldState* gameWorld, int delta) = 0; + virtual void Write(OutputMemoryBitStream &stream); + virtual void Read(InputMemoryBitStream &stream); + virtual void Render(GraphicsLibrary* gl); + void SetPostion(int posX, int posY); + Vector3 GetPosition(); + ~GameObject(); +}; +struct Location +{ + float x; + float y; +}; +#define CLASS_IDENTIFICATION( inCode, inClass ) \ +enum{ mClassId = inCode }; \ +virtual uint32_t GetClassId() const { return mClassId; } \ +static GameObject* CreateInstance() { return new inClass(); }\ +friend WorldState; \ No newline at end of file diff --git a/GameFiles/JoinerInput.cpp b/GameFiles/JoinerInput.cpp new file mode 100644 index 00000000..f4881597 --- /dev/null +++ b/GameFiles/JoinerInput.cpp @@ -0,0 +1,55 @@ + +#include "RoboCatPCH.h" +#include "JoinerInput.h" + +unsigned int JoinerInput::inputIdIterator = 0; + +JoinerInput::JoinerInput(InputActionIDs initalType, Location loc) +{ + timeOfCreation = time(0); + inputIDType = initalType; + location = loc; + + if (inputIDType == InputActionIDs::JOINER_KEY_SPAWN) + inputID = ++JoinerInput::inputIdIterator; + else + inputID = 00; //these don't need an id + +} + +void JoinerInput::Write(OutputMemoryBitStream& stream) +{ + stream.Write(inputIDType); + stream.Write(timeOfCreation); + stream.Write(location.x); + stream.Write(location.y); + stream.Write(inputID); +} + +void JoinerInput::Write(OutputMemoryBitStream& stream, vector& joinerInputs) +{ + GameObject* tempObj; + stream.Write(joinerInputs.size()); + for (int i = 0; i < joinerInputs.size(); i++) + { + joinerInputs[i].Write(stream); + } +} + +void JoinerInput::Read(InputMemoryBitStream& stream, vector& joinerInputs) +{ + int count; + stream.Read(count); + for (int i = 0; i < count; i++) + { + JoinerInput current; + + stream.Read(current.inputIDType); + stream.Read(current.timeOfCreation); + stream.Read(current.location.x); + stream.Read(current.location.y); + stream.Read(current.inputID); + + joinerInputs.push_back(current); + } +} \ No newline at end of file diff --git a/GameFiles/JoinerInput.h b/GameFiles/JoinerInput.h new file mode 100644 index 00000000..68afd3b3 --- /dev/null +++ b/GameFiles/JoinerInput.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include "GameObject.h" +#include "GameFiles/WorldState.h" + + +class JoinerInput +{ + public: + + + enum InputActionIDs + { + JOINER_KEY_SPAWN, + CONNECTION_CONFIRMATION_MESSAGE + }; + + time_t timeOfCreation; + InputActionIDs inputIDType; + Location location; + + int inputID; + static unsigned int inputIdIterator; + + JoinerInput(InputActionIDs initalType, Location loc); + + static void Write(OutputMemoryBitStream& stream, vector& joinerInputs); + + static void Read(InputMemoryBitStream& stream, vector& joinerInputs); + + private: + + + void Write(OutputMemoryBitStream& stream); + JoinerInput() + { + ; + } +}; \ No newline at end of file diff --git a/GameFiles/Key.cpp b/GameFiles/Key.cpp new file mode 100644 index 00000000..9278ecb5 --- /dev/null +++ b/GameFiles/Key.cpp @@ -0,0 +1,40 @@ +#include "RoboCatPCH.h" +#include "Key.h" +#include "Lock.h" +#include "Coin.h" +#include "WorldState.h" + +Key::Key() : GameObject("keyBlue.png", 70, 70) +{ + +} + +Key::Key(int posX, int posY) : GameObject("keyBlue.png", posX, posY, 70, 70) +{ + +} + +Key::~Key() +{ +} + +void Key::Update(WorldState* gameWorld, int delta) +{ + if (mGraphicsData.mX < 0) mGraphicsData.mX = 1500; + + mGraphicsData.mX -= MOVE_SPEED * delta; + + for (int i = 0; i < gameWorld->mGameObjects.size(); i++) + { + if (gameWorld->mGameObjects[i]->GetClassId() == 'LOCK' + && (gameWorld->mGameObjects[i]->GetPosition() - GetPosition()).Length2D() < mGraphicsData.mZ) + { + + gameWorld->Create(mGraphicsData.mX,mGraphicsData.mY); + gameWorld->SetForDestroy(gameWorld->mGameObjects[i]); + gameWorld->SetForDestroy(this); + break; + } + } + +} \ No newline at end of file diff --git a/GameFiles/Key.h b/GameFiles/Key.h new file mode 100644 index 00000000..2a597579 --- /dev/null +++ b/GameFiles/Key.h @@ -0,0 +1,18 @@ +#pragma once +#include "GameObject.h" +class Key : + public GameObject +{ +private: + const float MOVE_SPEED = 0.1f; + + Key(); + Key(int posX, int posY); + ~Key(); +protected: +public: + virtual void Update(WorldState* gameWorld, int delta); + + CLASS_IDENTIFICATION('KEYS', Key) +}; + diff --git a/GameFiles/Lock.cpp b/GameFiles/Lock.cpp new file mode 100644 index 00000000..5ed8a9a6 --- /dev/null +++ b/GameFiles/Lock.cpp @@ -0,0 +1,25 @@ + +#include "RoboCatPCH.h" +#include "Lock.h" + + +Lock::Lock() : GameObject("lock_blue.png", 70, 70) +{ + +} + +Lock::Lock(int posX, int posY) : GameObject("lock_blue.png", posX, posY, 70, 70) +{ + +} + +Lock::~Lock() +{ +} + +void Lock::Update(WorldState* gameWorld, int delta) +{ + if (mGraphicsData.mX > 1500) mGraphicsData.mX = 0; + + mGraphicsData.mX += MOVE_SPEED * delta; +} diff --git a/GameFiles/Lock.h b/GameFiles/Lock.h new file mode 100644 index 00000000..9623568a --- /dev/null +++ b/GameFiles/Lock.h @@ -0,0 +1,17 @@ +#pragma once +#include "GameObject.h" + +class Lock : public GameObject +{ + const float MOVE_SPEED = 0.25; + + private: + Lock(); + Lock(int posX, int posY); + ~Lock(); + protected: + public: + virtual void Update(WorldState* gameWorld, int delta); + + CLASS_IDENTIFICATION('LOCK', Lock) +}; \ No newline at end of file diff --git a/GameFiles/NetworkManager.cpp b/GameFiles/NetworkManager.cpp new file mode 100644 index 00000000..4c2dd9b4 --- /dev/null +++ b/GameFiles/NetworkManager.cpp @@ -0,0 +1,278 @@ +#include "RoboCatPCH.h" +#include "NetworkManager.h" + + +// inits +void NetworkManager::SetUpInitialListening(int& port, UDPSocketPtr& listeningSocket, SocketAddressPtr& listeningAddress) +{ + // here we can select which IPV we're using, and IPV6 is a bit wild for these purposes so, we go with IPV4 + listeningSocket = SocketUtil::CreateUDPSocket(SocketAddressFamily::INET); + if (listeningSocket == nullptr) + { + SocketUtil::ReportError("Error Creating Listen Socket"); + ExitProcess(1); + } + //LOG("%s", "created listening socket"); + + if(listeningSocket->SetNonBlockingMode(true) != NO_ERROR) + SocketUtil::ReportError("Error Setting To Non-blocking mode"); + + listeningAddress = SocketAddressFactory::CreateIPv4FromString((ACCEPT_ALL_ADDRESS + std::to_string(port++))); + if (listeningAddress == nullptr) + { + SocketUtil::ReportError("Error creating listening address"); + ExitProcess(1); + } + + //LOG("%s", "binding the socket"); + while (listeningSocket->Bind(*listeningAddress) != NO_ERROR) + { + //LOG("%s", "port: 0.0.0.0:" + std::to_string(nextAvailablePort) + " taken, trying to use port: 0.0.0.0:" + std::to_string(nextAvailablePort + 1)); + listeningAddress = SocketAddressFactory::CreateIPv4FromString(ACCEPT_ALL_ADDRESS + std::to_string(port)); + } + + //LOG("%s", "bound the socket"); + //if (listeningSocket->ReceiveFrom() != NO_ERROR) + //{ + // SocketUtil::ReportError("Error listening with the socket"); + // ExitProcess(1); + //} + + LOG("Your port is %i\n", static_cast(port)); + //LOG("%s", "socket is now listening"); +} +void NetworkManager::HandleListening(std::atomic* connectionsOpen, UDPSocketPtr& listeningSocket, SocketAddressPtr& addressRecievedFrom, priority_queue>& unprocessedData) +{ + std::cout << "Listening Now!"; + + while (connectionsOpen->load()) + { + //LOG("%s", "In handleListening before Recieve\n"); + + // TODO, look into what length this buffer should be, make a better one + char buffer[BUFFER_SIZE]; + int byteCount = listeningSocket->ReceiveFrom(buffer, BUFFER_SIZE, *(addressRecievedFrom)); + + //LOG("%s", "In handleListening after Recieve\n"); + + + if (buffer != nullptr && byteCount > 0) + { + string msgRecieved(static_cast(buffer), BUFFER_SIZE); + //LOG("Recieved message: %s", msgRecieved.c_str()); + unprocessedData.push(std::pair(time(0), buffer)); + } + } +} + +void NetworkManager::SetUpSending(int portToSendTo, int portUsedForSending, UDPSocketPtr& sendingSocket, SocketAddressPtr& sendingAddress) +{ + sendingSocket = SocketUtil::CreateUDPSocket(SocketAddressFamily::INET); + if (sendingSocket == nullptr) + { + SocketUtil::ReportError("Error Creating Sending Socket"); + ExitProcess(1); + } + //LOG("%s", "created connection socket"); + + + // make sure you send to THIS address, not the folloing one + SocketAddressPtr a = SocketAddressFactory::CreateIPv4FromString((ACCEPT_ALL_ADDRESS + std::to_string(portUsedForSending))); + if (a == nullptr) + { + SocketUtil::ReportError("Error creating sending address"); + ExitProcess(1); + } + //LOG("%s", "created connection socket address"); + + //LOG("%s", "binding the connection socket"); + while (sendingSocket->Bind(*a) != NO_ERROR) + { + a = SocketAddressFactory::CreateIPv4FromString((HOME_ADDRESS + std::to_string(++portUsedForSending))); + } + //LOG("%s", "finished binding the connection socket"); + + + //LOG("%s%i", "Sending message to 127.0.0.1:", portToSendTo); + sendingAddress = SocketAddressFactory::CreateIPv4FromString((HOME_ADDRESS + std::to_string(portToSendTo))); // this has to match the server's address, and it MUST NOT match client's own + if (sendingAddress == nullptr) + { + SocketUtil::ReportError("Creating foreign listener address"); + ExitProcess(1); + } + + string msg("Let's start a game?"); + if ((sendingSocket->SendTo(msg.c_str(), BUFFER_SIZE, *sendingAddress)) < 0) + { + SocketUtil::ReportError("Sending First Message"); + } + + LOG("%s\n", "Starting message sent."); +} + + +// updates +bool NetworkManager::HandleIncomingInputPackets(priority_queue>& unprocessedData, vector& joinerInputs, system_clock::time_point& lastConnectionTime) +{ + while(unprocessedData.size() > 0) + { + std::cout << "currently have " << unprocessedData.size() << " inputs left to process.\n"; + lastConnectionTime = system_clock::now(); + char* buffer = (char*)(unprocessedData.top().second); + InputMemoryBitStream inStream = InputMemoryBitStream(buffer, BUFFER_SIZE); + JoinerInput::Read(inStream, joinerInputs); + unprocessedData.pop(); + } + + if (duration_cast(system_clock::now() - lastConnectionTime).count() > CONNECTION_TIMOUT) + { + std::cout << "\n\nERROR: IT HAS BEEN " << CONNECTION_TIMOUT / 1000 << " SECONDS SINCE YOU HAVE RECIEVED DATA FROM JOINER. CLOSING GAME NOW.\n\n"; + return false; + } + + return true; +} + +bool NetworkManager::HandleOutgoingWorldStatePackets(WorldState& gameWorld, UDPSocketPtr &sendingSocket, SocketAddressPtr &sendingAddress, int delta) +{ + bool allGood = true; + if(isDelayingAPacket) + { + currentDelayBetweenPacketSend -= delta; + if (currentDelayBetweenPacketSend < 1) + { + isDelayingAPacket = false; + std::cout << "delayed packet sent!\n"; + + if (!sendingSocket->SendTo(delayedStream.GetBufferPtr(), delayedStream.GetByteLength(), *sendingAddress)) + { + SocketUtil::ReportError("Sending outgoing delayed Packet"); + allGood = false; + } + delayedStream = OutputMemoryBitStream(); + } + } + + if (((rand() % 100) + 1) > PACKET_DROP_CHANCE_PERCENT) // if we shouldn't drop a packet + { + if (!isDelayingAPacket && (rand() % 101) < PACKET_DELAY_CHANCE_PERCENT) // if we should delay a packet + { + isDelayingAPacket = true; + currentDelayBetweenPacketSend = PACKET_DELAY_TIME; + std::cout << "current packet deliberately delayed by " << PACKET_DELAY_TIME / 1000 << " seconds\n"; + gameWorld.Write(delayedStream); + } + else + { + gameWorld.Write(stream); + if(!sendingSocket->SendTo(stream.GetBufferPtr(), stream.GetByteLength(), *sendingAddress)) + { + SocketUtil::ReportError("Sending outgoing Packet"); + allGood = false; + } + stream = OutputMemoryBitStream(); + } + } + else + std::cout << "deliberately dropped a packet\n"; + + + return allGood; +} + + +bool NetworkManager::HandleIncomingWorldStatePackets(WorldState& gameWorld, priority_queue>& unprocessedDataPackets, system_clock::time_point& lastConnectionTime) +{ + if (unprocessedDataPackets.size() > 0) + { + lastConnectionTime = system_clock::now(); + if (unprocessedDataPackets.top().first > timeOfLastWorldState)// only world states newer than our current one + { + timeOfLastWorldState = unprocessedDataPackets.top().first; + + char* buffer = (char*)unprocessedDataPackets.top().second; + InputMemoryBitStream inStream = InputMemoryBitStream(buffer, BUFFER_SIZE); + gameWorld.Read(inStream); + } + + // clearing the queue + unprocessedDataPackets = priority_queue>(); + } + else if (duration_cast(system_clock::now() - lastConnectionTime).count() > CONNECTION_TIMOUT) + { + std::cout << "\n\nERROR: IT HAS BEEN " << CONNECTION_TIMOUT/1000 << " SECONDS SINCE YOU HAVE RECIEVED DATA FROM CREATOR. CLOSING GAME NOW.\n\n"; + return false; + } + + return true; +} + +bool NetworkManager::HandleOutgoingInputPackets(vector& joinerInputs, UDPSocketPtr& sendingSocket, SocketAddressPtr& sendingAddress, system_clock::time_point& lastTimeOfSendingConnection, int delta) +{ + bool allGood = true; + bool goodToClearInputs = true; + + // connection_timeout / 5 to give breathing room for the packet to send before dc, as well as if a few get dropped + if (duration_cast(system_clock::now() - lastTimeOfSendingConnection).count() > CONNECTION_TIMOUT / 5) + { + //std::cout << "\n\nIT HAS BEEN " << CONNECTION_TIMOUT / 1000 / 5 << " SECONDS SINCE INPUT, SENDING A CONNECTION CONFIRMATION NOW.\n\n"; + joinerInputs.push_back(JoinerInput(JoinerInput::CONNECTION_CONFIRMATION_MESSAGE, Location{ 0,0 })); + } + + + if(isDelayingAPacket) + { + currentDelayBetweenPacketSend -= delta; + if (currentDelayBetweenPacketSend < 1) + { + isDelayingAPacket = false; + std::cout << "delayed packet sent!\n"; + + if ((sendingSocket->SendTo(delayedStream.GetBufferPtr(), delayedStream.GetByteLength(), *sendingAddress)) < 0) + { + SocketUtil::ReportError("Sending outgoing delayed Packet"); + allGood = false; + } + delayedStream = OutputMemoryBitStream(); + + } + } + + if (joinerInputs.size() > 0) + { + lastTimeOfSendingConnection = system_clock::now(); + + if (((rand() % 100) + 1) > PACKET_DROP_CHANCE_PERCENT) // if we shouldn't drop a packet + { + if (!isDelayingAPacket && ((rand() % 101) < PACKET_DELAY_CHANCE_PERCENT)) // if we should delay a packet + { + isDelayingAPacket = true; + currentDelayBetweenPacketSend = PACKET_DELAY_TIME; + + JoinerInput::Write(delayedStream, std::ref(joinerInputs)); + + std::cout << "current packet deliberately delayed by " << PACKET_DELAY_TIME / 1000 << " seconds\n"; + } + else + { + + JoinerInput::Write(stream, std::ref(joinerInputs)); + + if ((sendingSocket->SendTo(stream.GetBufferPtr(), stream.GetByteLength(), *sendingAddress)) < 0) + { + SocketUtil::ReportError("Sending outgoingPacket"); + allGood = false; + } + stream = OutputMemoryBitStream(); + } + } + else + std::cout << "deliberately dropped a packet\n"; + } + + + + joinerInputs.clear(); + + return allGood; +} \ No newline at end of file diff --git a/GameFiles/NetworkManager.h b/GameFiles/NetworkManager.h new file mode 100644 index 00000000..e1bf9449 --- /dev/null +++ b/GameFiles/NetworkManager.h @@ -0,0 +1,44 @@ +#pragma once + +#include "GameFiles/JoinerInput.h" +#include "WorldState.h" + + + +namespace NetworkManager +{ + // can probably be bigger since we're using UDP + const int BUFFER_SIZE = 4096; + const int CONNECTION_TIMOUT = 7.0 * 1000; // * 1000 converts to milliseconds + const int PACKET_DROP_CHANCE_PERCENT = 10; // goes from 0 to 100 + const int PACKET_DELAY_CHANCE_PERCENT = 5; // goes from 0 to 100 + const int PACKET_DELAY_TIME = 1.0 * 1000; // + + const string HOME_ADDRESS = "127.0.0.1:"; + const string ACCEPT_ALL_ADDRESS = "0.0.0.0:"; + + static time_t timeOfLastWorldState = time(0); + static int currentDelayBetweenPacketSend = 0; + + static OutputMemoryBitStream stream; + static OutputMemoryBitStream delayedStream; + + static bool isDelayingAPacket = false; + + + // "Primitive" Connection Functions + void SetUpInitialListening(int& port, UDPSocketPtr& listeningSocket, SocketAddressPtr& listeningAddress); + void HandleListening(std::atomic* connectionsOpen, UDPSocketPtr& listeningSocket, SocketAddressPtr& addressRecievedFrom, priority_queue>& unprocessedData); + + void SetUpSending(int portToSendTo, int portUsedForSending, UDPSocketPtr& sendingSocket, SocketAddressPtr& sendingAddress); + + // Derived Connection Functions for in-game use + // creator + bool HandleIncomingInputPackets(priority_queue>& unprocessedData, vector& joinerInputs, system_clock::time_point& lastConnectionTime); + bool HandleOutgoingWorldStatePackets(WorldState& gameWorld, UDPSocketPtr& sendingSocket, SocketAddressPtr& sendingAddress, int delta); + // joiner + bool HandleIncomingWorldStatePackets(WorldState& gameWorld, priority_queue>& unprocessedDataPackets, system_clock::time_point& connectionTimer); + bool HandleOutgoingInputPackets(vector& joinerInputs, UDPSocketPtr& sendingSocket, SocketAddressPtr& sendingAddress, system_clock::time_point& lastTimeOfSendingConnection, int delta); +} + + diff --git a/GameFiles/WorldState.cpp b/GameFiles/WorldState.cpp new file mode 100644 index 00000000..96b3467e --- /dev/null +++ b/GameFiles/WorldState.cpp @@ -0,0 +1,225 @@ +#include "RoboCatPCH.h" +#include "WorldState.h" +#include "Lock.h" +#include "Key.h" +#include "Coin.h" +#include "allegro_wrapper_functions-main/GraphicsLibrary.h" + +unsigned int WorldState::currentInputNum = 0; + +WorldState::WorldState(GraphicsLibrary* gl) +{ + mpGraphicsLibrary = gl; + mpGameObjectLinker = new LinkingContext(); + mGameObjects = std::vector(); +} + +WorldState::~WorldState() +{ + ClearGameObjectsOut(); + + + //mpGraphicsLibrary = nullptr; +} + +void WorldState::Update(bool isCreator, vector& joinerInputs, int delta) +{ + if (isCreator) + { + for (int i = 0; i < joinerInputs.size(); i++) + { + if (joinerInputs[i].inputIDType == JoinerInput::JOINER_KEY_SPAWN) + { + if (joinerInputs[i].inputID > WorldState::currentInputNum) + { + int count = joinerInputs[i].inputID - WorldState::currentInputNum; + std::cout << "for count = " << count << ".\n"; + + for (int i = 0; i < (count); i++) + { + std::cout << "2\n"; + CreateKey(1500, 375); // x = best guess for starting position, y = middle of screen + std::cout << "3\n"; + } + std::cout << "4\n"; + WorldState::currentInputNum = joinerInputs[i].inputID; + } + + } + } + joinerInputs.clear(); + } + + for(int i = 0; i < mGameObjects.size(); i++) + { + mGameObjects[i]->Update(this, delta); + } + + RemoveUnneededGameObjects(); +} + +void WorldState::Render(string background) +{ + //std::cout << 5.1 << '\n'; + mpGraphicsLibrary->drawImage(background, 0, 0); + + //std::cout << 5.2 << '\n'; + for each (GameObject * var in mGameObjects) + { + //std::cout << 5.3 << '\n'; + var->Render(mpGraphicsLibrary); + //std::cout << 5.4 << '\n'; + + } + + //std::cout << 5.6 << '\n'; + if (mGameObjects.size() > 1) + { + std::cout << "Allegro's render crashed"; + mpGraphicsLibrary->render(); + std::cout << "\r"; + } + //std::cout << 5.9 << '\n'; + + +} + +void WorldState::CreateLock() +{ + CreateLock(0, 0); +} + +void WorldState::CreateLock(int posX, int posY) +{ + GameObject* createdGameObject = Lock::CreateInstance(); + createdGameObject->SetPostion(posX, 375); // quick fix so it's in middle + mpGameObjectLinker->GetNetworkId(createdGameObject,true); + mGameObjects.push_back(createdGameObject); +} + +void WorldState::CreateKey(int posX, int posY) +{ + GameObject* createdGameObject = Key::CreateInstance(); + createdGameObject->SetPostion(posX, 375); // quick fix so it's in the middle + mpGameObjectLinker->GetNetworkId(createdGameObject,true); + mGameObjects.push_back(createdGameObject); +} + +void WorldState::SetForDestroy(GameObject* obj) +{ + mToDestroy.push_back(obj); +} + +void WorldState::Write(OutputMemoryBitStream& stream) const +{ + int count = mGameObjects.size(); + GameObject* tempObj; + stream.Write(count); + for (int i = 0; i < count; i++) + { + tempObj = mGameObjects[i]; + stream.Write(mpGameObjectLinker->GetNetworkId(tempObj,true)); + stream.Write(tempObj->GetClassId()); + tempObj->Write(stream); + } +} + +void WorldState::Read(InputMemoryBitStream& stream) +{ + ClearGameObjectsOut(); + mpGameObjectLinker = new LinkingContext(); + + int count; + uint32_t networkID; + uint32_t classID; + GameObject* tempObj; + stream.Read(count); + for (int i = 0; i < count; i++) + { + stream.Read(networkID); + stream.Read(classID); + + tempObj = mpGameObjectLinker->GetGameObject(networkID); + + if (tempObj == nullptr) + { + switch (classID) + { + case 'LOCK': + tempObj = Lock::CreateInstance(); + break; + case 'KEYS': + tempObj = Key::CreateInstance(); + break; + case 'COIN': + tempObj = Coin::CreateInstance(); + break; + default: + break; + } + mpGameObjectLinker->AddGameObject(tempObj, networkID); + mGameObjects.push_back(tempObj); + } + + tempObj->Read(stream); + } +} + +void WorldState::ClearGameObjectsOut() +{ + while (!mGameObjects.empty()) + { + GameObject* obj = mGameObjects.back(); + switch (obj->GetClassId()) + { + case 'LOCK': + delete (Lock*)obj; + break; + case 'KEYS': + delete (Key*)obj; + break; + case 'COIN': + delete (Coin*)obj; + break; + default: + break; + } + mGameObjects.pop_back(); + } + + delete mpGameObjectLinker; +} + +void WorldState::RemoveUnneededGameObjects() +{ + // backwards so we don't loop out + for (int i = mToDestroy.size() - 1; i > -1; i--) + { + mpGameObjectLinker->RemoveGameObject(mToDestroy[i]); + for (int j = mGameObjects.size() - 1; j > -1; j--) + { + if (mGameObjects[j] == mToDestroy[i]) + { + mGameObjects.erase(mGameObjects.begin() + j); + break; + } + } + + switch (mToDestroy[i]->GetClassId()) + { + case 'LOCK': + delete (Lock*)mToDestroy[i]; + break; + case 'KEYS': + delete (Key*)mToDestroy[i]; + break; + case 'COIN': + delete (Coin*)mToDestroy[i]; + break; + default: + break; + } + } + + mToDestroy.clear(); +} diff --git a/GameFiles/WorldState.h b/GameFiles/WorldState.h new file mode 100644 index 00000000..e4dde446 --- /dev/null +++ b/GameFiles/WorldState.h @@ -0,0 +1,63 @@ +#pragma once +#include "GameFiles/JoinerInput.h" + +class GameObject; +class LinkingContext; +class GraphicsLibrary; +class JoinerInput; +class Key; + +struct Location; + +class WorldState +{ + const float SCREEN_X = 1000; + const float SCREEN_Y = 500; + const string FILE_PATH = "\\..\\..\\images\\"; + const string CREATOR_BACKGROUND = "background.jpg"; + + static unsigned int currentInputNum; + + public: + WorldState(GraphicsLibrary* gl); + ~WorldState(); + void Update(bool isCreator, vector& joinerInput, int delta); + void Render(string background); + + void CreateLock(); + void CreateLock(int posX, int posY); + void CreateKey(int posX, int posY); + + template + void Create() + { + Create(0, 0); + } + + template + void Create(int posX, int posY) + { + //Yes, the registry would be safer, I'm aware + GameObject* createdGameObject = T::CreateInstance(); + createdGameObject->SetPostion(posX, posY); + mpGameObjectLinker->GetNetworkId(createdGameObject, true); + mGameObjects.push_back(createdGameObject); + } + + void SetForDestroy(GameObject* obj); + + void Write(OutputMemoryBitStream& stream) const; + virtual void Read(InputMemoryBitStream& stream); + protected: + private: + void ClearGameObjectsOut(); + void RemoveUnneededGameObjects(); + + vector mGameObjects; + vector mToDestroy; + LinkingContext* mpGameObjectLinker; + GraphicsLibrary* mpGraphicsLibrary; + + friend Key; +}; + diff --git a/RoboCat/Chapter3.vcxproj b/RoboCat/Chapter3.vcxproj index 8c859a81..f658c8e5 100644 --- a/RoboCat/Chapter3.vcxproj +++ b/RoboCat/Chapter3.vcxproj @@ -92,6 +92,9 @@ true true Bin\$(Configuration)\ + true + true + true true @@ -370,10 +373,17 @@ - + + + + + + + + + + - - @@ -385,12 +395,17 @@ - - - - - + + + + + + + + + + @@ -405,7 +420,6 @@ Create RoboCatPCH.h - diff --git a/RoboCat/Inc/MemoryBitStream.h b/RoboCat/Inc/MemoryBitStream.h index c2138925..6e83423d 100644 --- a/RoboCat/Inc/MemoryBitStream.h +++ b/RoboCat/Inc/MemoryBitStream.h @@ -1,3 +1,4 @@ +#pragma once #include #include diff --git a/RoboCat/Inc/RoboCatShared.h b/RoboCat/Inc/RoboCatShared.h index d7eac1bc..649ee70b 100644 --- a/RoboCat/Inc/RoboCatShared.h +++ b/RoboCat/Inc/RoboCatShared.h @@ -35,7 +35,13 @@ #include "deque" #include "unordered_set" #include "cassert" +#include +#include +#include +#include +using std::stack; +using std::thread; using std::shared_ptr; using std::unique_ptr; using std::vector; @@ -45,12 +51,21 @@ using std::deque; using std::unordered_map; using std::string; using std::unordered_set; +using std::pair; +using std::priority_queue; +using std::chrono::system_clock; +using std::chrono::duration_cast; +using std::chrono::milliseconds; +using std::ref; class RoboCat; class GameObject; + + #include "RoboMath.h" + #include "TransmissionData.h" #include "InFlightPacket.h" @@ -60,6 +75,12 @@ class GameObject; #include "AckRange.h" #include "Timing.h" + +#include "LinkingContext.h" +#include "ByteSwap.h" +#include "MemoryBitStream.h" + + #include "StringUtils.h" #include "SocketAddress.h" #include "SocketAddressFactory.h" diff --git a/RoboCat/Src/Main.cpp b/RoboCat/Src/Main.cpp index 3b0ac9de..a175246f 100644 --- a/RoboCat/Src/Main.cpp +++ b/RoboCat/Src/Main.cpp @@ -1,9 +1,74 @@ +/// +/// This code is based on the code provided by the instructor Scott Barrett, as well +/// as wrapper functions made by Adel Talhouk (the latter having header comments indicating such) +/// unless otherwise stated, the rest of the work was done by Brandon Boras (bb) and Nicholas Perell +/// +// standard libraries + + +// libraries provided to us #include "RoboCatPCH.h" +#include "allegro_wrapper_functions-main/GraphicsLibrary.h" +#include "allegro_wrapper_functions-main/InputSystem.h" +#include "GameFiles/Lock.h" +#include "GameFiles/Key.h" +#include "GameFiles/WorldState.h" +#include "GameFiles/NetworkManager.h" +#include + +/// TODO +/// O - Handle one player disconnecting on the other player's end +/// O - Joiner doesn't crash, should DC when worldstate takes too long to update +/// O - Creator doesn't crash, joiner should send a "all-ok" message if the Joiner hasn't pressed an input in a while +/// O - Update gameobjects via a frame delta time (to prevent desync due to game speed) +/// X - Unreliability Simulation - +/// O - Packets are occasionally "dropped," by random chance or by some algorithm. +/// X - the input packets need to be guaranteed once dropped, they can likely just send a static id num and recieving handles unexpectedly high id num +/// O - Dropped packets are registered as having been sent by the sender, but do not actually call the socket's send() function. +/// O - Random delay is introduced to some packet's send time, (technically in, but a bit of a reach) +/// O - and packets can be delivered out of order. +/// X - Reliability - +/// X - Game state is sent through a reliability layer. Important data is sent through guaranteed mechanisms, +/// while data that does not require guaranteed delivery is sent through simpler mechanisms. +/// X - Important data is resilient to jitter, out - of - order delivery, and high latency. +/// X - Game Complexity - +/// X - There are at least 3 distinct game object types with different data requirements. +/// X - Some game objects require complex types, such as collections or pointers, which are properly replicated. +/// X - Code Cleanliness - +/// Spacing is consistent, variable naming is consistent. +/// Often - repeated code is organized into functions or classes, as appropriate. +/// Variables are appropriately scoped(i.e., they don't outlive their usefulness). +/// Variable scope is kept manageable by migrating code to functions when appropriate. +/// Related code is kept in the same place. Unused or commented code is not present. +/// X - Code Sophistication - +/// Code is robust in dealing with errors and malformed input. Preprocessor macros, gotos, global variables, +/// and similar "code smell" constructs are not used without strong justification. No resources are leaked, +/// including but not limited to memory, files, sockets, and mutexes. Member functions and their parameters +/// are declared const whenever appropriate. No warnings are generated by the compiler. +/// +/// Last touches +/// x - switch srand around line 81 of main from beinbg seeded by 0 to being seeded by current time (it was 0 for replicatability) +/// X - maybe user inputs should also be sent over a priority queue? +/// X - update the control prompt at the beginning of the game +/// X - when user wants to quit, set closingConnections = true; (to clean things up) +/// X - make sure there is no git trash left over like .orig +/// X - clean up every cout in the program for final +/// X - all compiler errors -#if _WIN32 +const float SCREEN_X = 1500; +const float SCREEN_Y = 750; +const string FILE_PATH = "\\..\\..\\images\\"; +const string CREATOR_BACKGROUND = "background.jpg"; +const string JOINER_BACKGROUND = "background3.jpg"; +const int CREATOR_PORT = 8080; +const int JOINER_PORT = 8100; + +std::atomic connectionsOpen(true); + +#if _WIN32 int main(int argc, const char** argv) { UNREFERENCED_PARAMETER(argc); @@ -16,10 +81,209 @@ int main(int argc, const char** argv) __argc = argc; __argv = argv; #endif + + srand(0); + rand(); // discarding the first rand() call, which is usually not very random (even for rand()'s standards) + + string userAnswer; + + // connection-related + + bool userIsCreator; + int listeningPort; + std::thread listeningThread; + UDPSocketPtr listeningSocket; + SocketAddressPtr listeningAddress; + + UDPSocketPtr sendingSocket; + + SocketAddressPtr addressToSendTo; + SocketAddressPtr addressRecievedFrom; + + priority_queue> unprocessedData = priority_queue>(); + + string currentBackground; + + //--------------------- intialization window ------------------------------- SocketUtil::StaticInit(); + // instructions + + std::cout << + "Hello, welcome to this game demonstration, here are the controls\n" << + "The creator(c) of the game and the joiner(j) have different moves available\n" << + "Left Click (c) - Creates a lock that moves from the left to the right.\n" << + "Left Click (j) - Creates a key that moves from the right to the left.\n\n" << + "If a lock and key touch then they spawn a coin.\n\n" << + + "Another thing to note is that any errors in connection will be displayed in the command line.\n\n"; + + // pre-game menu + std::cout << "\n\nWould you like to create (type c) or join (type j) a match of this game?\n"; + do + { + std::cin >> userAnswer; + std::cin.clear(); + } while (!(userAnswer == "c" || userAnswer == "C" || userAnswer == "j" || userAnswer == "J")); + + userIsCreator = (userAnswer == "c" || userAnswer == "C"); + + + // handling the initial networking set up + if (userIsCreator) + { + std::cout << "Thank you for your answer, we will now wait for someone to join the game.\n"; + + + listeningPort = CREATOR_PORT; + NetworkManager::SetUpInitialListening(listeningPort, listeningSocket, listeningAddress); + + addressRecievedFrom = SocketAddressFactory::CreateIPv4FromString((NetworkManager::ACCEPT_ALL_ADDRESS + std::to_string(CREATOR_PORT+5))); + //HandleListening(closingConnections, listeningSocket, addressToSendTo, unprocessedData, waitingForConnection); + //something about this thread is cursed, throws errors in the actual implementation of thread + listeningThread = std::thread(NetworkManager::HandleListening, &(connectionsOpen), listeningSocket, addressRecievedFrom, ref(unprocessedData)); + + do + { + Sleep(500); // wait 0.5 second + std::cout << '.'; + } while (unprocessedData.size() < 1); + + std::cout << "\n Recieved an offer to start the game, now sending the reply to start their game.\n"; + NetworkManager::SetUpSending(JOINER_PORT, CREATOR_PORT + 10, sendingSocket, addressToSendTo); + } + else // try to connect to a host + { + + NetworkManager::SetUpSending(CREATOR_PORT, JOINER_PORT + 10, sendingSocket, addressToSendTo); + std::cout << "\nSent the request to join, waiting for a reply.\n"; + + bool waitingForConnection = true; + + listeningPort = JOINER_PORT; + NetworkManager::SetUpInitialListening(listeningPort, listeningSocket, listeningAddress); + + addressRecievedFrom = SocketAddressFactory::CreateIPv4FromString((NetworkManager::ACCEPT_ALL_ADDRESS + std::to_string( JOINER_PORT + 5))); + listeningThread = std::thread(NetworkManager::HandleListening, &(connectionsOpen), listeningSocket, addressRecievedFrom, ref(unprocessedData)); + do + { + Sleep(500); // wait 0.5 second + std::cout << '.'; + } while (unprocessedData.size() < 1); + } + + std::cout << "\n\nStarting the game!\n"; + + // clearing the queue + unprocessedData = priority_queue>(); + + // Graphics init + GraphicsLibrary gl(SCREEN_X, SCREEN_Y); + if ( !gl.init()) + std::cout << "ERROR INTIALIZING THE GRAPHICS LIBRARY\n"; + + // Input Init + InputSystem inputSys; + if (!inputSys.init(&(gl))) + std::cout << "ERROR INITIALIZING THE INPUT SYSTEM\n"; + + // load background + + currentBackground = userIsCreator ? CREATOR_BACKGROUND : JOINER_BACKGROUND; + gl.loadImage(FILE_PATH, currentBackground); + gl.drawImage(currentBackground, 0, 0); + + + gl.render(); + + gl.loadImage(FILE_PATH, "lock_blue.png"); + gl.loadImage(FILE_PATH, "keyBlue.png"); + gl.loadImage(FILE_PATH, "coinGold.png"); + + WorldState gameWorld = WorldState(&gl); + + if (userIsCreator) + { + //gameWorld.CreateLock(); + //gameWorld.Create(SCREEN_X,0); + } + + vector joinerInputs; + + + system_clock::time_point lastTime = system_clock::now(); + system_clock::time_point startTime = system_clock::now(); + + system_clock::time_point lastTimeOfRecievingConnection = system_clock::now(); + system_clock::time_point lastTimeOfSendingConnection = system_clock::now(); + + + int deltaTime; + int timestep = 16; + bool isGameRunning = true; + + // ````````````````````````` main game loop ``````````````````````````` + while (isGameRunning) + { + deltaTime = duration_cast(system_clock::now() - lastTime).count(); + startTime = lastTime; + lastTime = system_clock::now(); + + //std::cout << 0 << '\n'; + + inputSys.Update(userIsCreator, ref(gameWorld), ref(joinerInputs)); + //std::cout << 1 << '\n'; + if (userIsCreator) + { + //std::cout << 2 << '\n'; + // processing Joiner's inputs before update means that their spawned pieces get exported in the world state + if(!NetworkManager::HandleIncomingInputPackets(ref(unprocessedData), ref(joinerInputs), ref(lastTimeOfRecievingConnection))) + { + isGameRunning = false; + } + //std::cout << 3 << '\n'; + gameWorld.Update(userIsCreator, ref(joinerInputs), deltaTime); + + //std::cout << 4 << '\n'; + NetworkManager::HandleOutgoingWorldStatePackets(ref(gameWorld), sendingSocket, addressToSendTo, deltaTime); + } + else + { + //std::cout << 2 << '\n'; + gameWorld.Update(userIsCreator, ref(joinerInputs), deltaTime); + //ProcessWorldState(); + //std::cout << 3 << '\n'; + if (!NetworkManager::HandleIncomingWorldStatePackets(ref(gameWorld), ref(unprocessedData), ref(lastTimeOfRecievingConnection))) + { + isGameRunning = false; + } + NetworkManager::HandleOutgoingInputPackets(ref(joinerInputs), sendingSocket, addressToSendTo, ref(lastTimeOfSendingConnection), deltaTime); + //std::cout << 4 << '\n'; + } + + //std::cout << 5 << '\n'; + gameWorld.Render(currentBackground); + //std::cout << 6 << '\n'; + + + while (duration_cast(system_clock::now() - startTime).count() < 16) + { + ; + // makes the program maintain a maximum framerate + //std::cout << duration_cast(system_clock::now() - startTime).count() << std::endl; + } + } + + + // ______________________________ Clean Up ______________________________ + + gl.CleanUp(); + connectionsOpen = false; + + listeningThread.join(); SocketUtil::CleanUp(); return 0; } + diff --git a/RoboCat/Src/UDPSocket.cpp b/RoboCat/Src/UDPSocket.cpp index adf859f7..b4fcbdf7 100644 --- a/RoboCat/Src/UDPSocket.cpp +++ b/RoboCat/Src/UDPSocket.cpp @@ -1,6 +1,7 @@ #include "RoboCatPCH.h" + int UDPSocket::Bind( const SocketAddress& inBindAddress ) { int error = bind( mSocket, &inBindAddress.mSockAddr, inBindAddress.GetSize() ); diff --git a/allegro_wrapper_functions-main/Colour.cpp b/allegro_wrapper_functions-main/Colour.cpp new file mode 100644 index 00000000..07703f1a --- /dev/null +++ b/allegro_wrapper_functions-main/Colour.cpp @@ -0,0 +1,32 @@ +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +Colour.cpp +*/ +#include "RoboCatPCH.h" + +#include "Colour.h" + +//Constructor - without alpha +Colour::Colour(unsigned __int8 r, unsigned __int8 g, unsigned __int8 b) +{ + mR = r; + mG = g; + mB = b; + mA = 255; +} + +//Constructor - with alpha +Colour::Colour(unsigned __int8 r, unsigned __int8 g, unsigned __int8 b, unsigned __int8 a) +{ + mR = r; + mG = g; + mB = b; + mA = a; +} + +//Destructor +Colour::~Colour() +{ + +} \ No newline at end of file diff --git a/allegro_wrapper_functions-main/Colour.h b/allegro_wrapper_functions-main/Colour.h new file mode 100644 index 00000000..216b028c --- /dev/null +++ b/allegro_wrapper_functions-main/Colour.h @@ -0,0 +1,43 @@ +#pragma once + +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +Colour.h + + File information: + This file contains data used for colours. +*/ + +class Colour +{ + //-------------------------Private data------------------------- + + //Red channel + unsigned __int8 mR; + + //Green channel + unsigned __int8 mG; + + //Blue channel + unsigned __int8 mB; + + //Alpha channel + unsigned __int8 mA; + + //-------------------------Public data------------------------- +public: + + //Constructor(s) + Colour(unsigned __int8 r, unsigned __int8 g, unsigned __int8 b); + Colour(unsigned __int8 r, unsigned __int8 g, unsigned __int8 b, unsigned __int8 a); + + //Destructor + ~Colour(); + + //Accessor(s) + unsigned __int8 getR() { return mR; }; + unsigned __int8 getG() { return mG; }; + unsigned __int8 getB() { return mB; }; + unsigned __int8 getA() { return mA; }; +}; diff --git a/allegro_wrapper_functions-main/GraphicsLibrary.cpp b/allegro_wrapper_functions-main/GraphicsLibrary.cpp new file mode 100644 index 00000000..a2730c5b --- /dev/null +++ b/allegro_wrapper_functions-main/GraphicsLibrary.cpp @@ -0,0 +1,141 @@ +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +GraphicsLibrary.cpp +*/ + +#include "RoboCatPCH.h" + +#include "GraphicsLibrary.h" + +#include +#include + +//Constructor +GraphicsLibrary::GraphicsLibrary(float screenSizeX, float screenSizeY) +{ + //Setup data - screen size + mScreenSizeX = screenSizeX; + mScreenSizeY = screenSizeY; + + //Allegro display + mpDisplay = nullptr; +} + +//Destructor +GraphicsLibrary::~GraphicsLibrary() +{ + CleanUp(); +} + +bool GraphicsLibrary::init() +{ + //Init allegro + if (!al_init()) + { + std::cout << "error initting Allegro\n"; + system("pause"); + return false; + } + + //Init image addon + if (!al_init_image_addon()) + { + std::cout << "error initting image add-on\n"; + system("pause"); + return false; + } + + //Init font add on + if (!al_init_font_addon()) + { + std::cout << "error initting font add-on\n"; + system("pause"); + return false; + } + + //Init ttf add on + if (!al_init_ttf_addon()) + { + std::cout << "error initting ttf add-on\n"; + system("pause"); + return false; + } + + //Setup display + mpDisplay = al_create_display(mScreenSizeX, mScreenSizeY); + + if (mpDisplay == nullptr) + { + return false; + } + + return true; +} + +void GraphicsLibrary::render() +{ + //Flip display buffers + al_flip_display(); +} + +void GraphicsLibrary::loadImage(std::string imageFolderFilePath, std::string imageIdentifier) +{ + //Add the name of the image and the loaded bitmap to the vector of pairs + + ALLEGRO_BITMAP *bmp = al_load_bitmap((__FILE__ + imageFolderFilePath + imageIdentifier).c_str()); + if (bmp == NULL) + { + std::cout << ("\nbitmap didn't load @ " + (__FILE__ + imageFolderFilePath + imageIdentifier)); + std::getchar(); + } + + mBitmapPointersVector.push_back(std::make_pair(imageIdentifier, bmp)); +} + +void GraphicsLibrary::drawImage(std::string imageIdentifier, float posX, float posY) +{ + //Find the image and draw if it exists + std::vector>::iterator iterator; + + for (iterator = mBitmapPointersVector.begin(); iterator != mBitmapPointersVector.end(); ++iterator) + { + if (iterator->first == imageIdentifier) + { + al_draw_bitmap(iterator->second, posX, posY, 0); + } + } +} + +void GraphicsLibrary::drawTintedImage(std::string imageIdentifier, float posX, float posY, Colour col) +{ + //Find the image and draw if it exists + std::vector>::iterator iterator; + + //Set colour + ALLEGRO_COLOR colour = al_map_rgba(col.getR(), col.getG(), col.getB(), col.getA()); + + for (iterator = mBitmapPointersVector.begin(); iterator != mBitmapPointersVector.end(); ++iterator) + { + if (iterator->first == imageIdentifier) + { + al_draw_tinted_bitmap(iterator->second, colour, posX, posY, 0); + } + } +} + +// bb +void GraphicsLibrary::CleanUp() +{ + //Delete bitmaps + std::vector>::iterator iterator; + for (iterator = mBitmapPointersVector.begin(); iterator != mBitmapPointersVector.end(); ++iterator) + { + al_destroy_bitmap(iterator->second); + } + mBitmapPointersVector.clear(); + + //Clean up display + al_destroy_display(mpDisplay); + mpDisplay = nullptr; +} \ No newline at end of file diff --git a/allegro_wrapper_functions-main/GraphicsLibrary.h b/allegro_wrapper_functions-main/GraphicsLibrary.h new file mode 100644 index 00000000..8ec95e85 --- /dev/null +++ b/allegro_wrapper_functions-main/GraphicsLibrary.h @@ -0,0 +1,71 @@ +#pragma once + +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +GraphicsLibrary.h + + File information: + This file contains function abstractions from Allegro 5, wrapped up in my Graphics Library. This will + be used to render images and text to the screen. + + Source I am consulting: Allegro 5.0.10 Manual - http://cdn.allegro.cc/file/library/allegro/5.0.10/allegro-5.0.10-manual.pdf +*/ + +#include +#include + +#include "Colour.h" + +//https://github.com/liballeg/allegro_wiki/wiki/Allegro-in-Visual-Studio#using-nuget-within-visual-studio +#include +#include +#include +#include +#include +#include + +class GraphicsLibrary +{ + //-------------------------Private data------------------------- + + //Screen data + float mScreenSizeX; + float mScreenSizeY; + + //Allegro display + ALLEGRO_DISPLAY* mpDisplay; + + //Other images to draw + std::vector> mBitmapPointersVector; + + friend class InputSystem; + + //-------------------------Public data------------------------- +public: + + //Constructor(s) + GraphicsLibrary(float screenSizeX, float screenSizeY); + + //Destructor + ~GraphicsLibrary(); + + //Accessor(s) + float getScreenSizeX() { return mScreenSizeX; }; + float getScreenSizeY() { return mScreenSizeY; }; + + //Mutator(s) + + //Functions + bool init(); + void render(); + void loadImage(std::string imageFilePath, std::string imageIdentifier); + + //Drawing functions + void drawImage(std::string imageIdentifier, float posX, float posY); + void drawTintedImage(std::string imageIdentifier, float posX, float posY, Colour col); + + //bb and Nick added these! + bool HandleDrawImages(); + void CleanUp(); +}; \ No newline at end of file diff --git a/allegro_wrapper_functions-main/InputSystem.cpp b/allegro_wrapper_functions-main/InputSystem.cpp new file mode 100644 index 00000000..4d694cb1 --- /dev/null +++ b/allegro_wrapper_functions-main/InputSystem.cpp @@ -0,0 +1,175 @@ +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +InputSystem.cpp +*/ +#include "RoboCatPCH.h" + +#include + +#include "InputSystem.h" + +//Constructor +InputSystem::InputSystem() +{ + //Create an event queue + mpEventQueue = al_create_event_queue(); +} + +//Destructor +InputSystem::~InputSystem() +{ + //Cleanup event queue + al_destroy_event_queue(mpEventQueue); + mpEventQueue = nullptr; +} + +float InputSystem::getMouseX() +{ + //Update mouse state + ALLEGRO_MOUSE_STATE mouseState; + al_get_mouse_state(&mouseState); + + return mouseState.x; +} + +float InputSystem::getMouseY() +{ + //Update mouse state + ALLEGRO_MOUSE_STATE mouseState; + al_get_mouse_state(&mouseState); + + return mouseState.y; +} + +Location InputSystem::GetMousePosition() +{ + //Update mouse state + ALLEGRO_MOUSE_STATE mouseState; + al_get_mouse_state(&mouseState); + Location mouseLocation{ mouseState.x, mouseState.y }; + return mouseLocation; +} + +//Init +bool InputSystem::init(GraphicsLibrary* pGraphicsLib) +{ + //Init keyboard + if (!al_install_keyboard()) + { + std::cout << "error installing Allegro keyboard plugin\n"; + system("pause"); + return false; + } + + //Init mouse + if (!al_install_mouse()) + { + std::cout << "error installing Allegro mouse plugin\n"; + system("pause"); + return false; + } + + //Register screen event source + al_register_event_source(mpEventQueue, al_get_display_event_source(pGraphicsLib->mpDisplay)); + + //Register keyboard event source + al_register_event_source(mpEventQueue, al_get_keyboard_event_source()); + + //Register mouse event source + al_register_event_source(mpEventQueue, al_get_mouse_event_source()); + + return true; +} + +MouseButton InputSystem::GetMouseInput() +{ + ALLEGRO_MOUSE_STATE state; + + al_get_mouse_state(&state); + if (state.buttons & 1) + { + return MouseButton::LeftMouse; + } + if (state.buttons & 4) + { + return MouseButton::MiddleMouse; + } + if (state.buttons & 2) + { + return MouseButton::RightMouse; + } + + return MouseButton::None; +} + +KeyCode InputSystem::getKeyboardInput() +{ + //If there is an event + al_wait_for_event(mpEventQueue, &mEvent); + + if (mEvent.type == InputMode::KeyPressed) + { + //Check the type + switch (mEvent.keyboard.keycode) + { + case KeyCode::ESCAPE_KEY: + return KeyCode::ESCAPE_KEY; + break; + + case KeyCode::R: + return KeyCode::R; + break; + + default: + /*return KeyCode::NONE*/; + } + } + + //return KeyCode::NONE; +} + +void InputSystem::Update(bool isCreator, WorldState& gameWorld, vector& joinerInputs) +{ + switch (GetMouseInput()) + { + case MouseButton::LeftMouse: + if (!wasHoldingLeftMouseLastFrame) + { + wasHoldingLeftMouseLastFrame = true; + //std::cout << "\nLeft Clicked\n"; + + if (isCreator) + { + // create lock + gameWorld.CreateLock(); + + } + else + { + // create key + joinerInputs.push_back(JoinerInput(JoinerInput::JOINER_KEY_SPAWN, GetMousePosition())); + } + } + break; + case MouseButton::MiddleMouse: + if (!wasHoldingMiddleMouseLastFrame) + { + wasHoldingMiddleMouseLastFrame = true; + //std::cout << "\nMiddle Clicked\n"; + } + break; + case MouseButton::RightMouse: + if (!wasHoldingRightMouseLastFrame) + { + wasHoldingRightMouseLastFrame = true; + //std::cout << "\nRight Clicked\n"; + } + break; + case MouseButton::None: + wasHoldingLeftMouseLastFrame = false; + wasHoldingMiddleMouseLastFrame = false; + wasHoldingRightMouseLastFrame = false; + break; + } +} \ No newline at end of file diff --git a/allegro_wrapper_functions-main/InputSystem.h b/allegro_wrapper_functions-main/InputSystem.h new file mode 100644 index 00000000..dd6e66fe --- /dev/null +++ b/allegro_wrapper_functions-main/InputSystem.h @@ -0,0 +1,80 @@ +#pragma once + +/* +Allegro Wrapper Functions +Written by Adel Talhouk in FA21 +InputSystem.h + + File information: + This file contains the keycodes for input, which can be used in any way desired by other classes + and files. +*/ + +#include "GraphicsLibrary.h" + +//Include allegro libraries for input +#include + +#include "GameFiles/WorldState.h" +#include "GameFiles/JoinerInput.h" + + +enum KeyCode +{ + ESCAPE_KEY = ALLEGRO_KEY_ESCAPE, + R = ALLEGRO_KEY_R +}; + +enum MouseButton +{ + None = -1, + LeftMouse = 0, + RightMouse = 1, + MiddleMouse = 2 +}; + +enum InputMode +{ + NONE = -1, + KeyPressed = ALLEGRO_EVENT_KEY_DOWN, + KeyReleased = ALLEGRO_EVENT_KEY_UP, + MouseDown = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, + MouseUp = ALLEGRO_EVENT_MOUSE_BUTTON_UP +}; + +class InputSystem +{ + //-------------------------Private data------------------------- + + //Event queue + ALLEGRO_EVENT_QUEUE* mpEventQueue; + + //Event + ALLEGRO_EVENT mEvent; + + //-------------------------Public data------------------------- +public: + + //Constructor(s) + InputSystem(); + + //Destructor + ~InputSystem(); + + //Accessor(s) + float getMouseX(); + float getMouseY(); + Location GetMousePosition(); + + //Functions + bool init(GraphicsLibrary* pGraphicsLib); + MouseButton GetMouseInput(); + KeyCode getKeyboardInput(); + + bool wasHoldingLeftMouseLastFrame = false; + bool wasHoldingMiddleMouseLastFrame = false; + bool wasHoldingRightMouseLastFrame = false; + + // custom function to handle the game's input - bb + void Update(bool isCreator, WorldState& gameWorld, vector& joinerInputs); +}; \ No newline at end of file diff --git a/allegro_wrapper_functions-main/LICENSE b/allegro_wrapper_functions-main/LICENSE new file mode 100644 index 00000000..52e8a5fb --- /dev/null +++ b/allegro_wrapper_functions-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Adel Talhouk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/allegro_wrapper_functions-main/README.md b/allegro_wrapper_functions-main/README.md new file mode 100644 index 00000000..e8462238 --- /dev/null +++ b/allegro_wrapper_functions-main/README.md @@ -0,0 +1,43 @@ +# allegro_wrapper_functions +Some Allegro 5 wrapper functions for C++ + +I wrote some code for my Computer Architecture final project in FA21 that allowed me to use abstracted Allegro 5 functions. Feel free to use this code, but please keep the block comment in each file :) + +EXAMPLE INIT FUNCTION IN MAIN.CPP: + +```cpp +//-------------------------Graphics Data------------------------- +GraphicsLibrary* pGraphics; +float screenSizeX = 1600.0; +float screenSizeY = 900.0; + +//-------------------------Input Data------------------------- +InputSystem* pInput; + +//-------------------------Assets------------------------- +const std::string ASSET_PATH = "..\\Assets\\"; +const std::string BACKGROUND_IMAGE_FILE = "Background_Image.jpg"; + +//-------------------------Asset Identifiers------------------------- +const std::string backgroundImageSprite = "background_image_image"; + +bool init() +{ + bool bSuccessfulInit = false; + + //Setup the graphical window + pGraphics = new GraphicsLibrary(screenSizeX, screenSizeY); + bSuccessfulInit = pGraphics->init(ASSET_PATH + BACKGROUND_IMAGE_FILE); + + //Add images to the graphcis library + pGraphics->loadImage(ASSET_PATH + BACKGROUND_IMAGE_FILE, backgroundImageSprite); + + //Setup the input system + pInput = new InputSystem(); + if (bSuccessfulInit) + bSuccessfulInit = pInput->init(pGraphics); + + //Init and return if it succeeded or not + return bSuccessfulInit; +} +``` diff --git a/images/background.jpg b/images/background.jpg new file mode 100644 index 00000000..ce0f969d Binary files /dev/null and b/images/background.jpg differ diff --git a/images/background2.jpg b/images/background2.jpg new file mode 100644 index 00000000..128e653d Binary files /dev/null and b/images/background2.jpg differ diff --git a/images/background3.jpg b/images/background3.jpg new file mode 100644 index 00000000..4078df21 Binary files /dev/null and b/images/background3.jpg differ diff --git a/images/background4.jpg b/images/background4.jpg new file mode 100644 index 00000000..7eff98a6 Binary files /dev/null and b/images/background4.jpg differ diff --git a/images/background5.jpg b/images/background5.jpg new file mode 100644 index 00000000..ce4e103d Binary files /dev/null and b/images/background5.jpg differ diff --git a/images/boxCoin.png b/images/boxCoin.png new file mode 100644 index 00000000..fbba4d38 Binary files /dev/null and b/images/boxCoin.png differ diff --git a/images/boxCoinAlt.png b/images/boxCoinAlt.png new file mode 100644 index 00000000..8ba823eb Binary files /dev/null and b/images/boxCoinAlt.png differ diff --git a/images/boxCoinAlt_disabled.png b/images/boxCoinAlt_disabled.png new file mode 100644 index 00000000..9c2f177c Binary files /dev/null and b/images/boxCoinAlt_disabled.png differ diff --git a/images/boxCoin_disabled.png b/images/boxCoin_disabled.png new file mode 100644 index 00000000..9d114d0b Binary files /dev/null and b/images/boxCoin_disabled.png differ diff --git a/images/boxExplosive.png b/images/boxExplosive.png new file mode 100644 index 00000000..d721258e Binary files /dev/null and b/images/boxExplosive.png differ diff --git a/images/boxExplosiveAlt.png b/images/boxExplosiveAlt.png new file mode 100644 index 00000000..6a45a150 Binary files /dev/null and b/images/boxExplosiveAlt.png differ diff --git a/images/boxExplosive_disabled.png b/images/boxExplosive_disabled.png new file mode 100644 index 00000000..4d911707 Binary files /dev/null and b/images/boxExplosive_disabled.png differ diff --git a/images/boxItem.png b/images/boxItem.png new file mode 100644 index 00000000..9698ae6d Binary files /dev/null and b/images/boxItem.png differ diff --git a/images/boxItemAlt.png b/images/boxItemAlt.png new file mode 100644 index 00000000..389bf5b4 Binary files /dev/null and b/images/boxItemAlt.png differ diff --git a/images/boxItemAlt_disabled.png b/images/boxItemAlt_disabled.png new file mode 100644 index 00000000..dc59a02c Binary files /dev/null and b/images/boxItemAlt_disabled.png differ diff --git a/images/boxItem_disabled.png b/images/boxItem_disabled.png new file mode 100644 index 00000000..e504b99c Binary files /dev/null and b/images/boxItem_disabled.png differ diff --git a/images/coinBronze.png b/images/coinBronze.png new file mode 100644 index 00000000..fe70f412 Binary files /dev/null and b/images/coinBronze.png differ diff --git a/images/coinGold.png b/images/coinGold.png new file mode 100644 index 00000000..e63c689a Binary files /dev/null and b/images/coinGold.png differ diff --git a/images/coinSilver.png b/images/coinSilver.png new file mode 100644 index 00000000..4f7965b6 Binary files /dev/null and b/images/coinSilver.png differ diff --git a/images/fireball.png b/images/fireball.png new file mode 100644 index 00000000..6436ef63 Binary files /dev/null and b/images/fireball.png differ diff --git a/images/flagBlue.png b/images/flagBlue.png new file mode 100644 index 00000000..1cc0f429 Binary files /dev/null and b/images/flagBlue.png differ diff --git a/images/flagBlue2.png b/images/flagBlue2.png new file mode 100644 index 00000000..ce036d3c Binary files /dev/null and b/images/flagBlue2.png differ diff --git a/images/flagBlueHanging.png b/images/flagBlueHanging.png new file mode 100644 index 00000000..8821d1b5 Binary files /dev/null and b/images/flagBlueHanging.png differ diff --git a/images/flagGreen.png b/images/flagGreen.png new file mode 100644 index 00000000..a6d8e381 Binary files /dev/null and b/images/flagGreen.png differ diff --git a/images/flagGreen2.png b/images/flagGreen2.png new file mode 100644 index 00000000..d7e3fbf6 Binary files /dev/null and b/images/flagGreen2.png differ diff --git a/images/flagGreenHanging.png b/images/flagGreenHanging.png new file mode 100644 index 00000000..e9f5dcda Binary files /dev/null and b/images/flagGreenHanging.png differ diff --git a/images/flagRed.png b/images/flagRed.png new file mode 100644 index 00000000..e33b298e Binary files /dev/null and b/images/flagRed.png differ diff --git a/images/flagRed2.png b/images/flagRed2.png new file mode 100644 index 00000000..d5995d65 Binary files /dev/null and b/images/flagRed2.png differ diff --git a/images/flagRedHanging.png b/images/flagRedHanging.png new file mode 100644 index 00000000..d6a97a29 Binary files /dev/null and b/images/flagRedHanging.png differ diff --git a/images/flagYellow.png b/images/flagYellow.png new file mode 100644 index 00000000..92b736a6 Binary files /dev/null and b/images/flagYellow.png differ diff --git a/images/flagYellow2.png b/images/flagYellow2.png new file mode 100644 index 00000000..58b45660 Binary files /dev/null and b/images/flagYellow2.png differ diff --git a/images/flagYellowHanging.png b/images/flagYellowHanging.png new file mode 100644 index 00000000..15b0dd10 Binary files /dev/null and b/images/flagYellowHanging.png differ diff --git a/images/gemBlue.png b/images/gemBlue.png new file mode 100644 index 00000000..8beef0cf Binary files /dev/null and b/images/gemBlue.png differ diff --git a/images/gemGreen.png b/images/gemGreen.png new file mode 100644 index 00000000..9e9c3f6b Binary files /dev/null and b/images/gemGreen.png differ diff --git a/images/gemRed.png b/images/gemRed.png new file mode 100644 index 00000000..deb5cb05 Binary files /dev/null and b/images/gemRed.png differ diff --git a/images/gemYellow.png b/images/gemYellow.png new file mode 100644 index 00000000..0e4b4db2 Binary files /dev/null and b/images/gemYellow.png differ diff --git a/images/hud_coins.png b/images/hud_coins.png new file mode 100644 index 00000000..4419b29e Binary files /dev/null and b/images/hud_coins.png differ diff --git a/images/hud_gem_blue.png b/images/hud_gem_blue.png new file mode 100644 index 00000000..82b21fcd Binary files /dev/null and b/images/hud_gem_blue.png differ diff --git a/images/hud_gem_green.png b/images/hud_gem_green.png new file mode 100644 index 00000000..cff6068f Binary files /dev/null and b/images/hud_gem_green.png differ diff --git a/images/hud_gem_red.png b/images/hud_gem_red.png new file mode 100644 index 00000000..cfce0213 Binary files /dev/null and b/images/hud_gem_red.png differ diff --git a/images/hud_gem_yellow.png b/images/hud_gem_yellow.png new file mode 100644 index 00000000..f5d07d63 Binary files /dev/null and b/images/hud_gem_yellow.png differ diff --git a/images/hud_heartEmpty.png b/images/hud_heartEmpty.png new file mode 100644 index 00000000..2dfc0c9c Binary files /dev/null and b/images/hud_heartEmpty.png differ diff --git a/images/hud_heartFull.png b/images/hud_heartFull.png new file mode 100644 index 00000000..4b9aba20 Binary files /dev/null and b/images/hud_heartFull.png differ diff --git a/images/hud_heartHalf.png b/images/hud_heartHalf.png new file mode 100644 index 00000000..5d64f726 Binary files /dev/null and b/images/hud_heartHalf.png differ diff --git a/images/hud_keyBlue.png b/images/hud_keyBlue.png new file mode 100644 index 00000000..daf6025e Binary files /dev/null and b/images/hud_keyBlue.png differ diff --git a/images/hud_keyBlue_disabled.png b/images/hud_keyBlue_disabled.png new file mode 100644 index 00000000..7b3ec246 Binary files /dev/null and b/images/hud_keyBlue_disabled.png differ diff --git a/images/hud_keyGreem_disabled.png b/images/hud_keyGreem_disabled.png new file mode 100644 index 00000000..42cb1778 Binary files /dev/null and b/images/hud_keyGreem_disabled.png differ diff --git a/images/hud_keyGreen.png b/images/hud_keyGreen.png new file mode 100644 index 00000000..cd5e763d Binary files /dev/null and b/images/hud_keyGreen.png differ diff --git a/images/hud_keyRed.png b/images/hud_keyRed.png new file mode 100644 index 00000000..9669883b Binary files /dev/null and b/images/hud_keyRed.png differ diff --git a/images/hud_keyRed_disabled.png b/images/hud_keyRed_disabled.png new file mode 100644 index 00000000..484d2dbe Binary files /dev/null and b/images/hud_keyRed_disabled.png differ diff --git a/images/hud_keyYellow.png b/images/hud_keyYellow.png new file mode 100644 index 00000000..b011dbce Binary files /dev/null and b/images/hud_keyYellow.png differ diff --git a/images/hud_keyYellow_disabled.png b/images/hud_keyYellow_disabled.png new file mode 100644 index 00000000..16491aea Binary files /dev/null and b/images/hud_keyYellow_disabled.png differ diff --git a/images/hud_p1.png b/images/hud_p1.png new file mode 100644 index 00000000..b9c1b279 Binary files /dev/null and b/images/hud_p1.png differ diff --git a/images/hud_p2.png b/images/hud_p2.png new file mode 100644 index 00000000..76db891b Binary files /dev/null and b/images/hud_p2.png differ diff --git a/images/hud_p3.png b/images/hud_p3.png new file mode 100644 index 00000000..bbf28f8b Binary files /dev/null and b/images/hud_p3.png differ diff --git a/images/keyBlue.png b/images/keyBlue.png new file mode 100644 index 00000000..af17f80e Binary files /dev/null and b/images/keyBlue.png differ diff --git a/images/keyGreen.png b/images/keyGreen.png new file mode 100644 index 00000000..839294e6 Binary files /dev/null and b/images/keyGreen.png differ diff --git a/images/keyRed.png b/images/keyRed.png new file mode 100644 index 00000000..e2ebd915 Binary files /dev/null and b/images/keyRed.png differ diff --git a/images/keyYellow.png b/images/keyYellow.png new file mode 100644 index 00000000..bc58e185 Binary files /dev/null and b/images/keyYellow.png differ diff --git a/images/lock_blue.png b/images/lock_blue.png new file mode 100644 index 00000000..a2d073b8 Binary files /dev/null and b/images/lock_blue.png differ diff --git a/images/lock_green.png b/images/lock_green.png new file mode 100644 index 00000000..983e9ffc Binary files /dev/null and b/images/lock_green.png differ diff --git a/images/lock_red.png b/images/lock_red.png new file mode 100644 index 00000000..3394335d Binary files /dev/null and b/images/lock_red.png differ diff --git a/images/lock_yellow.png b/images/lock_yellow.png new file mode 100644 index 00000000..d7c36bd5 Binary files /dev/null and b/images/lock_yellow.png differ diff --git a/images/switchLeft.png b/images/switchLeft.png new file mode 100644 index 00000000..092a7d3e Binary files /dev/null and b/images/switchLeft.png differ diff --git a/images/switchMid.png b/images/switchMid.png new file mode 100644 index 00000000..a146b6c9 Binary files /dev/null and b/images/switchMid.png differ diff --git a/images/switchRight.png b/images/switchRight.png new file mode 100644 index 00000000..2457eec5 Binary files /dev/null and b/images/switchRight.png differ