diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index fb367874..f2b18eee 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -13,6 +13,8 @@ #include "Network/Network.h" #include "Network/MessageType.h" #include "Network/PlayerDefinition.h" +#include "Network/UDPClient.h" +#include "Network/TCPClient.h" #include "Network/SnapshotDefinitions.h" #include "Core/World.h" #include "Core/EventBroker.h" @@ -33,17 +35,15 @@ public: void Connect(std::string address, int port); void Update() override; -private: + std::vector m_PlayerSpawnEvents; + void parseSpawnEvents(); + // Save for children std::unique_ptr m_SnapshotFilter = nullptr; - // Assio UDP logic - boost::asio::ip::udp::endpoint m_ReceiverEndpoint; - boost::asio::io_service m_IOService; - boost::asio::ip::udp::socket m_Socket; - + std::string m_Address; + int m_Port = 0; // Sending message to server logic size_t bytesRead = 0; - char readBuf[INPUTSIZE] = { 0 }; // Packet loss logic PacketID m_PacketID = 0; @@ -72,16 +72,14 @@ private: std::vector m_InputCommandBuffer; // Private member functions - void readFromServer(); size_t receive(char* data); - void send(Packet& packet); - void connect(); void disconnect(); void parseMessageType(Packet& packet); void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID); SharedComponentWrapper createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo); void ignoreFields(Packet& packet, const ComponentInfo& componentInfo); - void parseConnect(Packet& packet); + void parseUDPConnect(Packet& packet); + void parseTCPConnect(Packet& packet); void parsePlayerConnected(Packet& packet); void parsePing(); void parseKick(); @@ -91,7 +89,7 @@ private: void InterpolateFields(Packet & packet, const ComponentInfo & componentInfo, const EntityID & entityID, const std::string & componentType); void parseSnapshot(Packet& packet); void identifyPacketLoss(); - bool hasServerTimedOut(); + void hasServerTimedOut(); EntityID createPlayer(); void sendInputCommands(); void sendLocalPlayerTransform(); @@ -111,6 +109,10 @@ private: bool OnPlayerDamage(const Events::PlayerDamage& e); EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(const Events::PlayerSpawned& e); + +private: + UDPClient m_Unreliable; + TCPClient m_Reliable; }; #endif diff --git a/include/Engine/Network/HybridClient.h b/include/Engine/Network/HybridClient.h new file mode 100644 index 00000000..8d96bf6e --- /dev/null +++ b/include/Engine/Network/HybridClient.h @@ -0,0 +1,13 @@ +#ifndef HybridClient_h__ +#define HybridClient_h__ + +class HybridClient +{ +public: + HybridClient(); + ~HybridClient(); +private: + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/HybridServer.h b/include/Engine/Network/HybridServer.h new file mode 100644 index 00000000..48d6fe63 --- /dev/null +++ b/include/Engine/Network/HybridServer.h @@ -0,0 +1,12 @@ +#ifndef HybridServer_h__ +#define HybridServer_h__ + +class HybridServer +{ +public: + HybridServer(); + ~HybridServer(); +private: +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/MessageType.h b/include/Engine/Network/MessageType.h index 85f22649..a72f054e 100644 --- a/include/Engine/Network/MessageType.h +++ b/include/Engine/Network/MessageType.h @@ -18,7 +18,8 @@ enum class MessageType OnPlayerSpawned, EntityDeleted, ComponentDeleted, - PlayerTransform + PlayerTransform, + Invalid }; #endif diff --git a/include/Engine/Network/Network.h b/include/Engine/Network/Network.h index 0dbc4915..b4de053e 100644 --- a/include/Engine/Network/Network.h +++ b/include/Engine/Network/Network.h @@ -12,7 +12,7 @@ #include #include -#define INPUTSIZE 32000 +#define BUFFERSIZE 32000 typedef unsigned int PlayerID; typedef unsigned int PacketID; @@ -35,6 +35,8 @@ protected: std::clock_t m_SaveDataTimer; unsigned int m_MaxConnections; double m_TimeoutMs; + void logSentData(int bytesSent); + void logReceivedData(int bytesReceived); void saveToFile(); void updateNetworkData(); }; diff --git a/include/Engine/Network/NetworkClient.h b/include/Engine/Network/NetworkClient.h new file mode 100644 index 00000000..4c623209 --- /dev/null +++ b/include/Engine/Network/NetworkClient.h @@ -0,0 +1,21 @@ +#ifndef NetworkClient_h__ +#define NetworkClient_h__ + +#include "Network/Packet.h" +#define BUFFERSIZE 32000 +typedef unsigned int PlayerID; +typedef unsigned int PacketID; + +class NetworkClient +{ +public: + virtual void Connect(std::string playerName, std::string address, int port) = 0; + virtual void Disconnect() = 0; + virtual void Receive(Packet& packet) = 0; + virtual void Send(Packet & packet) = 0; + virtual bool IsSocketAvailable() = 0; +protected: + char m_ReadBuffer[BUFFERSIZE] = { 0 }; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/NetworkServer.h b/include/Engine/Network/NetworkServer.h new file mode 100644 index 00000000..d6406eab --- /dev/null +++ b/include/Engine/Network/NetworkServer.h @@ -0,0 +1,21 @@ +#ifndef NetworkServer_h__ +#define NetworkServer_h__ +#include +#include "Network/Packet.h" +#include "Network/PlayerDefinition.h" +#define BUFFERSIZE 32000 +typedef unsigned int PlayerID; +typedef unsigned int PacketID; + +class NetworkServer +{ +public: + virtual void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) = 0; + virtual void Receive(Packet & packet, PlayerDefinition & playerDefinition) = 0; + virtual void Send(Packet & packet, PlayerDefinition & playerDefinition) = 0; + virtual void Send(Packet & packet) = 0; +protected: + char m_ReadBuffer[BUFFERSIZE] = { 0 }; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/Packet.h b/include/Engine/Network/Packet.h index 009d8563..b688b8c6 100644 --- a/include/Engine/Network/Packet.h +++ b/include/Engine/Network/Packet.h @@ -49,10 +49,15 @@ public: void WriteData(char* data, int sizeOfData); // Pops the first element as if it was a string. std::string ReadString(); + // Construct a packet + void ReconstructFromData(char* data, size_t SizeOfData); + // Update size of packet variable in header + void UpdateSize(); char* ReadData(int SizeOfData); void ChangePacketID(unsigned int& packetID); size_t Size() { return m_Offset; }; char* Data() { return m_Data; }; + MessageType GetMessageType(); size_t DataReadSize() { return m_ReturnDataOffset; } size_t MaxSize() { return m_MaxPacketSize; } size_t HeaderSize() { return m_HeaderSize; } @@ -64,6 +69,7 @@ private: size_t m_MaxPacketSize = 512; size_t m_HeaderSize = 0; void resizeData(); + void resizeData(int size); }; #endif \ No newline at end of file diff --git a/include/Engine/Network/PlayerDefinition.h b/include/Engine/Network/PlayerDefinition.h index 863948b3..afd5d889 100644 --- a/include/Engine/Network/PlayerDefinition.h +++ b/include/Engine/Network/PlayerDefinition.h @@ -2,6 +2,7 @@ #define PlayerDefinition_h__ #include #include "../Core/Entity.h" +#include struct PlayerDefinition { ::EntityID EntityID = EntityID_Invalid; @@ -9,6 +10,10 @@ struct PlayerDefinition { boost::asio::ip::udp::endpoint Endpoint; unsigned int PacketID; std::clock_t StopTime; + boost::asio::ip::address TCPAddress; + unsigned short TCPPort; + // use for tcp connections + boost::shared_ptr TCPSocket; }; #endif diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index fbba5340..8a1ee514 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -5,8 +5,9 @@ #include #include -#include +#include "Network/TCPServer.h" +#include "Network/UDPServer.h" #include "Network/MessageType.h" #include "Network/PlayerDefinition.h" #include "Core/World.h" @@ -28,28 +29,29 @@ public: void Update() override; private: + // Network channels + TCPServer m_Reliable; + UDPServer m_Unreliable; + // dont forget to set these in the childrens receive logic + boost::asio::ip::address m_Address; int m_Port = 27666; - // UDP logic - boost::asio::ip::udp::endpoint m_ReceiverEndpoint; - boost::asio::io_service m_IOService; - std::unique_ptr m_Socket; - // Sending messages to client logic std::map m_ConnectedPlayers; + std::vector m_PlayersToDisconnect; // HACK: Fix INPUTSIZE - char readBuffer[INPUTSIZE] = { 0 }; + char readBuffer[BUFFERSIZE] = { 0 }; size_t bytesRead = 0; // time for previouse message std::clock_t previousePingMessage = std::clock(); std::clock_t previousSnapshotMessage = std::clock(); std::clock_t timOutTimer = std::clock(); + // How often we send messages (milliseconds) float pingIntervalMs; float snapshotInterval; int checkTimeOutInterval = 100; int m_NextPlayerID = 0; std::vector m_InputCommandsToBroadcast; - //Timers std::clock_t m_StartPingTime; @@ -58,11 +60,9 @@ private: PacketID m_PreviousPacketID = 0; // Private member functions - size_t receive(char* data); - void readFromClients(); - void send(PlayerID player, Packet& packet); - void send(Packet& packet); - void broadcast(Packet& packet); + //int receive(char* data); + void reliableBroadcast(Packet& packet); + void unreliableBroadcast(Packet& packet); void sendSnapshot(); void addChildrenToPacket(Packet& packet, EntityID entityID); void addInputCommandsToPacket(Packet& packet); @@ -70,15 +70,18 @@ private: void checkForTimeOuts(); void disconnect(PlayerID playerID); void parseMessageType(Packet& packet); - void parseOnInputCommand(Packet& packet); void parseOnPlayerDamage(Packet& packet); - void parseConnect(Packet& packet); - void parseDisconnect(); - void parseClientPing(); - void parsePing(); void identifyPacketLoss(); void kick(PlayerID player); - PlayerID GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint); + PlayerID GetPlayerIDFromEndpoint(); + void parsePlayerTransform(Packet& packet); + void parseOnInputCommand(Packet& packet); + void parseClientPing(); + void parsePing(); + void parseUDPConnect(Packet & packet); + void parseTCPConnect(Packet & packet); + void parseDisconnect(); + // Debug event EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); @@ -88,7 +91,6 @@ private: bool OnEntityDeleted(const Events::EntityDeleted& e); EventRelay m_EComponentDeleted; bool OnComponentDeleted(const Events::ComponentDeleted& e); - void parsePlayerTransform(Packet& packet); bool shouldSendToClient(EntityWrapper childEntity); }; diff --git a/include/Engine/Network/TCPClient.h b/include/Engine/Network/TCPClient.h new file mode 100644 index 00000000..a666cbbe --- /dev/null +++ b/include/Engine/Network/TCPClient.h @@ -0,0 +1,28 @@ +#ifndef TCPClient_h__ +#define TCPClient_h__ + +#include +#include "NetworkClient.h" + +class TCPClient : public NetworkClient +{ +public: + TCPClient(); + ~TCPClient(); + + void Connect(std::string playerName, std::string address, int port); + void Disconnect(); + void Receive(Packet& packet); + void Send(Packet & packet); + bool IsSocketAvailable(); +private: + // Assio TCP logic + boost::asio::ip::tcp::endpoint m_Endpoint; + boost::asio::io_service m_IOService; + std::unique_ptr m_Socket; + size_t readBuffer(char* data); + PacketID m_SendPacketID = 0; + bool m_IsConnected = false; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/TCPServer.h b/include/Engine/Network/TCPServer.h new file mode 100644 index 00000000..9cc7646a --- /dev/null +++ b/include/Engine/Network/TCPServer.h @@ -0,0 +1,31 @@ +#ifndef TCPServer_h__ +#define TCPServer_h__ + +#include +#include +#include +#include "NetworkServer.h" + +class TCPServer : public NetworkServer +{ +public: + TCPServer(); + ~TCPServer(); + void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers); + void Receive(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet); + void Disconnect(); +private: + // TCP logic + boost::asio::io_service m_IOService; + std::unique_ptr acceptor; + boost::shared_ptr lastReceivedSocket; + + void handle_accept(boost::shared_ptr socket, + int& nextPlayerID, std::map& connectedPlayers, + const boost::system::error_code& error); + int readBuffer(char* data, PlayerDefinition& playerDefinition); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/UDPClient.h b/include/Engine/Network/UDPClient.h new file mode 100644 index 00000000..3a458d3e --- /dev/null +++ b/include/Engine/Network/UDPClient.h @@ -0,0 +1,27 @@ +#ifndef UDPClient_h__ +#define UDPClient_h__ + +#include +#include "Network/NetworkClient.h" + +class UDPClient : public NetworkClient +{ +public: + UDPClient(); + ~UDPClient(); + + void Connect(std::string playerName, std::string address, int port); + void Disconnect(); + void Receive(Packet& packet); + void Send(Packet & packet); + bool IsSocketAvailable(); +private: + // Assio UDP logic + boost::asio::io_service m_IOService; + boost::asio::ip::udp::endpoint m_ReceiverEndpoint; + boost::shared_ptr m_Socket; + int readBuffer(char* data); + PacketID m_SendPacketID = 0; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/UDPServer.h b/include/Engine/Network/UDPServer.h new file mode 100644 index 00000000..246fb333 --- /dev/null +++ b/include/Engine/Network/UDPServer.h @@ -0,0 +1,25 @@ +#ifndef UDPServer_h__ +#define UDPServer_h__ + +#include "NetworkServer.h" +#include + +class UDPServer : public NetworkServer +{ +public: + UDPServer(); + ~UDPServer(); + void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers); + void Receive(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet); + bool IsSocketAvailable(); +private: + // UDP logic + boost::asio::io_service m_IOService; + boost::asio::ip::udp::endpoint m_ReceiverEndpoint; + std::unique_ptr m_Socket; + int readBuffer(char* data); +}; + +#endif \ No newline at end of file diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index b0c8697f..020e0cb7 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -1,10 +1,8 @@ #include "Network/Client.h" - using namespace boost::asio::ip; Client::Client(World* world, EventBroker* eventBroker) : Network(world, eventBroker) - , m_Socket(m_IOService) { // Asumes root node is EntityID_Invalid insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid); @@ -14,7 +12,6 @@ Client::Client(World* world, EventBroker* eventBroker) auto config = ResourceManager::Load("Config.ini"); m_PlayerName = config->Get("Networking.Name", "Raptorcopter"); m_SendInputIntervalMs = config->Get("Networking.SendInputIntervalMs", 33); - LOG_INFO("Client initialized"); } @@ -26,70 +23,77 @@ Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr("Config.ini"); + m_Address = address; if (address.empty()) { - address = config->Get("Networking.Address", "127.0.0.1"); + m_Address = config->Get("Networking.Address", "127.0.0.1"); } + m_Port = port; if (port == 0) { - port = config->Get("Networking.Port", 27666); + m_Port = config->Get("Networking.Port", 27666); } - - m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port); - LOG_INFO("Client connecting..."); - m_Socket.connect(m_ReceiverEndpoint); - connect(); } void Client::Update() { m_EventBroker->Process(); - readFromServer(); + while (m_Unreliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Unreliable.Receive(packet); + if (packet.GetMessageType() == MessageType::Connect) { + parseUDPConnect(packet); + } else { + parseMessageType(packet); + } + } + while (m_Reliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Reliable.Receive(packet); + if (packet.GetMessageType() == MessageType::Connect) { + parseTCPConnect(packet); + } else { + parseMessageType(packet); + } + + } + if (m_IsConnected) { - hasServerTimedOut(); - // Don't sent 1 input in 1 packet, bunch em up. + // Don't send 1 input in 1 packet, bunch em up. if (m_SendInputIntervalMs < (1000 * (std::clock() - m_TimeSinceSentInputs) / (double)CLOCKS_PER_SEC)) { sendInputCommands(); m_TimeSinceSentInputs = std::clock(); } // HACK: Send absolute player positions for now to avoid desync until we have reliable messages sendLocalPlayerTransform(); - } - Network::Update(); -} -void Client::readFromServer() -{ - while (m_Socket.available()) { - bytesRead = receive(readBuf); - if (bytesRead > 0) { - Packet packet(readBuf, bytesRead); - parseMessageType(packet); - } + hasServerTimedOut(); } + //Network::Update(); } void Client::parseMessageType(Packet& packet) { + // Pop packetSize which is used by TCP Client to + // create a packet of the correct size + packet.ReadPrimitive(); int messageType = packet.ReadPrimitive(); if (messageType == -1) return; // Read packet ID m_PreviousPacketID = m_PacketID; // Set previous packet id m_PacketID = packet.ReadPrimitive(); //Read new packet id - identifyPacketLoss(); + //identifyPacketLoss(); switch (static_cast(messageType)) { - case MessageType::Connect: - parseConnect(packet); - break; case MessageType::Ping: parsePing(); break; @@ -120,12 +124,32 @@ void Client::parseMessageType(Packet& packet) } } -void Client::parseConnect(Packet& packet) +void Client::parseUDPConnect(Packet& packet) { // Map ServerEntityID and your PlayerID LOG_INFO("I be connected PogChamp"); } +void Client::parseTCPConnect(Packet& packet) +{ + LOG_INFO("Received TCP connect from server"); + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + // parse player id and other stuff + m_PlayerID = packet.ReadPrimitive(); + m_PlayerID = packet.ReadPrimitive(); + LOG_INFO("A Player connected"); + Packet UnreliablePacket(MessageType::Connect, m_SendPacketID); + // Add player id and other stuff + packet.WritePrimitive(m_PlayerID); + m_Unreliable.Send(packet); + LOG_INFO("Sent UDP Connect Server"); +} + void Client::parsePlayerConnected(Packet & packet) { // Map ServerEntityID and other player's PlayerID @@ -143,7 +167,7 @@ void Client::parsePing() Packet packet(MessageType::Ping, m_SendPacketID); packet.WriteString("Ping recieved"); - send(packet); + m_Reliable.Send(packet); } void Client::parseKick() @@ -152,14 +176,42 @@ void Client::parseKick() m_IsConnected = false; } +void Client::parseSpawnEvents() +{ + std::vector tempSpawn; + for (int i = 0; i < m_PlayerSpawnEvents.size(); i++) { + Events::PlayerSpawned e; + if (!serverClientMapsHasEntity(m_PlayerSpawnEvents.at(i).Player.ID) || + !serverClientMapsHasEntity(m_PlayerSpawnEvents.at(i).Spawner.ID)) { + tempSpawn.push_back(m_PlayerSpawnEvents.at(i)); + continue; + } + e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Player.ID)); + e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Spawner.ID)); + e.PlayerID = -1; + e.PlayerName = m_PlayerSpawnEvents.at(i).PlayerName; + m_EventBroker->Publish(e); + } + m_PlayerSpawnEvents = tempSpawn; + // m_PlayerSpawnEvents.clear(); +} + void Client::parsePlayersSpawned(Packet& packet) { + //Events::PlayerSpawned e; + //e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + //e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + //e.PlayerID = -1; + //e.PlayerName = packet.ReadString(); + //m_EventBroker->Publish(e); + Events::PlayerSpawned e; - e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); - e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + e.Player = EntityWrapper(m_World, packet.ReadPrimitive()); + e.Spawner = EntityWrapper(m_World, packet.ReadPrimitive()); e.PlayerID = -1; e.PlayerName = packet.ReadString(); - m_EventBroker->Publish(e); + m_PlayerSpawnEvents.push_back(e); + parseSpawnEvents(); } void Client::parseEntityDeletion(Packet & packet) @@ -235,7 +287,8 @@ void Client::parseSnapshot(Packet& packet) for (std::size_t i = 0; i < numInputCommands; ++i) { Events::InputCommand e; e.PlayerID = packet.ReadPrimitive(); - e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(packet.ReadPrimitive())); + EntityID player = packet.ReadPrimitive(); + e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(player)); e.Command = packet.ReadString(); e.Value = packet.ReadPrimitive(); m_EventBroker->Publish(e); @@ -297,56 +350,17 @@ void Client::parseSnapshot(Packet& packet) m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID)); } } -} - -size_t Client::receive(char* data) -{ - boost::system::error_code error; - - size_t bytesReceived = m_Socket.receive_from(boost - ::asio::buffer((void*)data, INPUTSIZE), - m_ReceiverEndpoint, - 0, error); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataReceived += bytesReceived; - m_NetworkData.DataReceivedThisInterval += bytesReceived; - m_NetworkData.AmountOfMessagesReceived++; - } - if (error) { - //LOG_ERROR("receive: %s", error.message().c_str()); - } - return bytesReceived; -} - -void Client::send(Packet& packet) -{ - m_Socket.send_to(boost::asio::buffer( - packet.Data(), - packet.Size()), - m_ReceiverEndpoint, 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; - } -} - -void Client::connect() -{ - Packet packet(MessageType::Connect, m_SendPacketID); - packet.WriteString(m_PlayerName); - m_StartPingTime = std::clock(); - send(packet); + parseSpawnEvents(); } void Client::disconnect() { + m_IsConnected = false; m_PreviousPacketID = 0; m_PacketID = 0; Packet packet(MessageType::Disconnect, m_SendPacketID); - send(packet); + m_Reliable.Send(packet); + m_Reliable.Disconnect(); } bool Client::OnInputCommand(const Events::InputCommand & e) @@ -357,7 +371,8 @@ bool Client::OnInputCommand(const Events::InputCommand & e) if (e.Command == "ConnectToServer") { // Connect for now if (e.Value > 0) { - connect(); + m_Reliable.Connect(m_PlayerName, m_Address, m_Port); + m_Unreliable.Connect(m_PlayerName, m_Address, m_Port); } //LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); return true; @@ -380,7 +395,9 @@ bool Client::OnInputCommand(const Events::InputCommand & e) m_SaveDataTimer = std::clock(); } } else { - m_InputCommandBuffer.push_back(e); + if (m_IsConnected) { + m_InputCommandBuffer.push_back(e); + } //LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); return true; } @@ -393,7 +410,7 @@ bool Client::OnPlayerDamage(const Events::PlayerDamage & e) packet.WritePrimitive(m_ClientIDToServerID.at(e.Inflictor.ID)); packet.WritePrimitive(m_ClientIDToServerID.at(e.Victim.ID)); packet.WritePrimitive(e.Damage); - send(packet); + m_Reliable.Send(packet); return false; } @@ -422,7 +439,7 @@ void Client::sendLocalPlayerTransform() packet.WritePrimitive(orientation.x); packet.WritePrimitive(orientation.y); packet.WritePrimitive(orientation.z); - + bool hasAssaultWeapon = m_LocalPlayer.HasComponent("AssaultWeapon"); packet.WritePrimitive(hasAssaultWeapon); if (hasAssaultWeapon) { @@ -430,8 +447,8 @@ void Client::sendLocalPlayerTransform() packet.WritePrimitive((int)cAssaultWeapon["MagazineAmmo"]); packet.WritePrimitive((int)cAssaultWeapon["Ammo"]); } - - send(packet); + + m_Unreliable.Send(packet); } void Client::identifyPacketLoss() @@ -443,17 +460,15 @@ void Client::identifyPacketLoss() } } -bool Client::hasServerTimedOut() +void Client::hasServerTimedOut() { // Time in ms double timeSincePing = 1000 * (std::clock() - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); if (timeSincePing > m_TimeoutMs) { // Clear everything and go to menu. LOG_INFO("Server has timed out, returning to menu, Beep Boop."); - m_IsConnected = false; - return true; + disconnect(); } - return false; } EntityID Client::createPlayer() @@ -474,7 +489,7 @@ void Client::sendInputCommands() packet.WriteString(m_InputCommandBuffer[i].Command); packet.WritePrimitive(m_InputCommandBuffer[i].Value); } - send(packet); + m_Reliable.Send(packet); m_InputCommandBuffer.clear(); } } @@ -482,7 +497,7 @@ void Client::sendInputCommands() void Client::becomePlayer() { Packet packet = Packet(MessageType::BecomePlayer, m_SendPacketID); - send(packet); + m_Reliable.Send(packet); } bool Client::clientServerMapsHasEntity(EntityID clientEntityID) @@ -513,7 +528,6 @@ void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID client { m_ServerIDToClientID.insert(std::make_pair(serverEntityID, clientEntityID)); m_ClientIDToServerID.insert(std::make_pair(clientEntityID, serverEntityID)); - } void Client::deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID) diff --git a/src/Engine/Network/HybridClient.cpp b/src/Engine/Network/HybridClient.cpp new file mode 100644 index 00000000..4200e8e3 --- /dev/null +++ b/src/Engine/Network/HybridClient.cpp @@ -0,0 +1,10 @@ +#include "Network/HybridClient.h" + + +HybridClient::HybridClient() +{ +} + +HybridClient::~HybridClient() +{ +} \ No newline at end of file diff --git a/src/Engine/Network/HybridServer.cpp b/src/Engine/Network/HybridServer.cpp new file mode 100644 index 00000000..bfcdaee0 --- /dev/null +++ b/src/Engine/Network/HybridServer.cpp @@ -0,0 +1,9 @@ +#include "Network/HybridServer.h" + +HybridServer::HybridServer() +{ +} + +HybridServer::~HybridServer() +{ +} \ No newline at end of file diff --git a/src/Engine/Network/Network.cpp b/src/Engine/Network/Network.cpp index 534df1cd..6ce9ef82 100644 --- a/src/Engine/Network/Network.cpp +++ b/src/Engine/Network/Network.cpp @@ -14,6 +14,21 @@ void Network::Update() updateNetworkData(); } +void Network::logSentData(int bytesSent) +{ + +} + +void Network::logReceivedData(int bytesReceived) +{ + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataReceived += bytesReceived; + m_NetworkData.DataReceivedThisInterval += bytesReceived; + m_NetworkData.AmountOfMessagesReceived++; + } +} + void Network::saveToFile() { std::ofstream outfile; diff --git a/src/Engine/Network/NetworkClient.cpp b/src/Engine/Network/NetworkClient.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/Engine/Network/Packet.cpp b/src/Engine/Network/Packet.cpp index 21226a07..475ca673 100644 --- a/src/Engine/Network/Packet.cpp +++ b/src/Engine/Network/Packet.cpp @@ -34,10 +34,12 @@ void Packet::Init(MessageType type, unsigned int & packetID) m_ReturnDataOffset = 0; m_Offset = 0; // Create message header + // allocate memory for size of packet(only used in tcp) + WritePrimitive(0); // Add message type int messageType = static_cast(type); - Packet::WritePrimitive(messageType); - Packet::WritePrimitive(packetID); + WritePrimitive(messageType); + WritePrimitive(packetID); packetID++; m_HeaderSize = m_Offset; } @@ -56,9 +58,12 @@ void Packet::WriteString(const std::string& str) void Packet::WriteData(char * data, int sizeOfData) { + if (m_Offset + sizeOfData > m_MaxPacketSize) { //LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size. New size is %i bytes\n", m_MaxPacketSize*2); - resizeData(); + while (m_Offset + sizeOfData > m_MaxPacketSize) { + resizeData(); + } } memcpy(m_Data + m_Offset, data, sizeOfData); m_Offset += sizeOfData; @@ -76,14 +81,35 @@ std::string Packet::ReadString() return returnValue; } -char * Packet::ReadData(int SizeOfData) +void Packet::ReconstructFromData(char * data, size_t sizeOfData) { - if (m_Offset < m_ReturnDataOffset + SizeOfData) { + if (sizeOfData > m_MaxPacketSize) { + // Delete our data + delete[] m_Data; + // Set new max size + m_MaxPacketSize = sizeOfData; + m_Data = new char[m_MaxPacketSize]; + // while we resized the old data container. + } + memcpy(m_Data, data, sizeOfData); + m_Offset = sizeOfData; + +} + +void Packet::UpdateSize() +{ + int whatisoffset = m_Offset; + memcpy(m_Data, &m_Offset, sizeof(int)); +} + +char * Packet::ReadData(int sizeOfData) +{ + if (m_Offset < m_ReturnDataOffset + sizeOfData) { //LOG_WARNING("packet ReadData(): Oh no! You are trying to remove things outside my memory kingdom"); return nullptr; } size_t oldReturnDataOffset = m_ReturnDataOffset; - m_ReturnDataOffset += SizeOfData; + m_ReturnDataOffset += sizeOfData; return (m_Data + oldReturnDataOffset); } @@ -91,25 +117,36 @@ void Packet::ChangePacketID(unsigned int & packetID) { packetID = packetID + 1; // Overwrite old PacketID - memcpy(m_Data + sizeof(int), &packetID, sizeof(int)); + memcpy(m_Data + 2*sizeof(int), &packetID, sizeof(int)); +} + +MessageType Packet::GetMessageType() +{ + MessageType messagType; + memcpy(&messagType, m_Data + sizeof(int), sizeof(int)); + return messagType; } void Packet::resizeData() { + resizeData(m_MaxPacketSize * 2); +} +void Packet::resizeData(int size) +{ // Allocate memory to store our data in char* holdData = new char[m_MaxPacketSize]; // Copy our data to the newly allocated memory memcpy(holdData, m_Data, m_Offset); // Increase max packet size - m_MaxPacketSize = m_MaxPacketSize * 2; + m_MaxPacketSize = size; // Delete our data - delete m_Data; - // Allocate twice the memory we had before + delete[] m_Data; + // Allocate memory m_Data = new char[m_MaxPacketSize]; // Copy our data to new location memcpy(m_Data, holdData, m_Offset); // Delete the memory allocated to hold our data // while we resized the old data container. - delete holdData; + delete[] holdData; } diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index bb2115de..1500377a 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -6,7 +6,6 @@ Server::Server(World* world, EventBroker* eventBroker, int port) ConfigFile* config = ResourceManager::Load("Config.ini"); snapshotInterval = 1000 * config->Get("Networking.SnapshotInterval", 0.05f); pingIntervalMs = config->Get("Networking.PingIntervalMs", 1000); - // Subscribe to events EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned); @@ -18,7 +17,6 @@ Server::Server(World* world, EventBroker* eventBroker, int port) port = config->Get("Networking.Port", 27666); } m_Port = port; - m_Socket = std::make_unique(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)); LOG_INFO("Server initialized and bound to port %i", port); } @@ -29,7 +27,58 @@ Server::~Server() void Server::Update() { - readFromClients(); + PlayerDefinition pd; + + m_Reliable.AcceptNewConnections(m_NextPlayerID, m_ConnectedPlayers); + for (auto& kv : m_ConnectedPlayers) { + while (kv.second.TCPSocket->available()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Reliable.Receive(packet, kv.second); + m_Address = kv.second.TCPSocket->remote_endpoint().address(); + m_Port = kv.second.TCPSocket->remote_endpoint().port(); + if (packet.GetMessageType() == MessageType::Connect) { + parseTCPConnect(packet); + } else { + parseMessageType(packet); + } + } + } + + while (m_Unreliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Unreliable.Receive(packet, pd); + m_Address = pd.Endpoint.address(); + m_Port = pd.Endpoint.port(); + if (packet.GetMessageType() == MessageType::Connect) { + parseUDPConnect(packet); + } else { + parseMessageType(packet); + } + } + // Check if players have disconnected + for (int i = 0; i < m_PlayersToDisconnect.size(); i++) { + disconnect(m_PlayersToDisconnect.at(i)); + } + m_PlayersToDisconnect.clear(); + + std::clock_t currentTime = std::clock(); + // Send snapshot + if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) { + sendSnapshot(); + previousSnapshotMessage = currentTime; + } + // Send pings each + if (pingIntervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) { + sendPing(); + previousePingMessage = currentTime; + } + // Time out logic + if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) { + checkForTimeOuts(); + timOutTimer = currentTime; + } m_EventBroker->Process(); if (isReadingData) { Network::Update(); @@ -37,48 +86,20 @@ void Server::Update() } -void Server::readFromClients() -{ - while (m_Socket->available()) { - try { - bytesRead = receive(readBuffer); - Packet packet(readBuffer, bytesRead); - parseMessageType(packet); - } catch (const std::exception&) { - //LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what()); - } - } - std::clock_t currentTime = std::clock(); - // Send snapshot - if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) { - sendSnapshot(); - previousSnapshotMessage = currentTime; - } - - // Send pings each - if (pingIntervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) { - sendPing(); - previousePingMessage = currentTime; - } - - // Time out logic - if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) { - checkForTimeOuts(); - timOutTimer = currentTime; - } -} - void Server::parseMessageType(Packet& packet) { - int messageType = packet.ReadPrimitive(); // Read what type off message was sent from server + // Pop packetSize which is used by TCP Client to + // create a packet of the correct size + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); // Read what type off message was sent from server // Read packet ID m_PreviousPacketID = m_PacketID; // Set previous packet id m_PacketID = packet.ReadPrimitive(); //Read new packet id //identifyPacketLoss(); switch (static_cast(messageType)) { case MessageType::Connect: - parseConnect(packet); + //parseConnect(packet); break; case MessageType::Ping: parsePing(); @@ -104,60 +125,19 @@ void Server::parseMessageType(Packet& packet) } } -size_t Server::receive(char * data) -{ - size_t length = m_Socket->receive_from( - boost::asio::buffer((void*)data - , INPUTSIZE) - , m_ReceiverEndpoint, 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataReceived += length; - m_NetworkData.DataReceivedThisInterval += length; - m_NetworkData.AmountOfMessagesReceived++; - } - return length; -} - -void Server::send(PlayerID player, Packet& packet) -{ - try { - size_t bytesSent = m_Socket->send_to( - boost::asio::buffer(packet.Data(), packet.Size()), - m_ConnectedPlayers[player].Endpoint, - 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; - } - } catch (const boost::system::system_error&) { - // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later - m_ConnectedPlayers[player].Endpoint = boost::asio::ip::udp::endpoint(); - } -} - -void Server::send(Packet & packet) -{ - m_Socket->send_to( - boost::asio::buffer( - packet.Data(), - packet.Size()), - m_ReceiverEndpoint, - 0); - if (isReadingData) { - // Network Debug data - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - } -} - -void Server::broadcast(Packet& packet) +void Server::reliableBroadcast(Packet& packet) { for (auto& kv : m_ConnectedPlayers) { packet.ChangePacketID(kv.second.PacketID); - send(kv.first, packet); + m_Reliable.Send(packet, kv.second); + } +} + +void Server::unreliableBroadcast(Packet& packet) +{ + for (auto& kv : m_ConnectedPlayers) { + packet.ChangePacketID(kv.second.PacketID); + m_Unreliable.Send(packet, kv.second); } } @@ -167,7 +147,7 @@ void Server::sendSnapshot() Packet packet(MessageType::Snapshot); addInputCommandsToPacket(packet); addChildrenToPacket(packet, EntityID_Invalid); - broadcast(packet); + unreliableBroadcast(packet); } void Server::addInputCommandsToPacket(Packet& packet) @@ -248,7 +228,7 @@ void Server::sendPing() // Time message m_StartPingTime = std::clock(); // Send message - broadcast(packet); + reliableBroadcast(packet); } void Server::checkForTimeOuts() @@ -256,16 +236,91 @@ void Server::checkForTimeOuts() double startPing = 1000 * m_StartPingTime / static_cast(CLOCKS_PER_SEC); - for (int i = 0; i < m_ConnectedPlayers.size(); i++) { - if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { - double stopPing = 1000 * m_ConnectedPlayers[i].StopTime / + std::vector playersToRemove; + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress != boost::asio::ip::address()) { + int stopPing = 1000 * kv.second.StopTime / static_cast(CLOCKS_PER_SEC); if (startPing > stopPing + m_TimeoutMs) { - //LOG_INFO("User %i timed out!", i); - //disconnect(i); + LOG_INFO("User %i timed out!", kv.second.Name); + playersToRemove.push_back(kv.first); } } } + for (size_t i = 0; i < playersToRemove.size(); i++) { + disconnect(playersToRemove.at(i)); + } +} + +void Server::parseUDPConnect(Packet & packet) +{ + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + // parse player id and other stuff + PlayerID playerID = packet.ReadPrimitive(); + // Do something here? + boost::asio::ip::udp::endpoint endpoint(m_Address, m_Port); + m_ConnectedPlayers.at(playerID).Endpoint = endpoint; + LOG_INFO("parseUDPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(), m_ConnectedPlayers.at(playerID).Endpoint.address().to_string().c_str()); + // Send a message to the player that connected + Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID); + m_Unreliable.Send(connnectPacket); + LOG_INFO("UDP Connect sent to client"); +} + +void Server::parseTCPConnect(Packet & packet) +{ + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + + LOG_INFO("Parsing connections"); + // Check if player is already connected + // Ska vara till lagd i TCPServer receive + PlayerID playerID = GetPlayerIDFromEndpoint(); + if (playerID == -1) { + return; + } + // Create a new player + m_ConnectedPlayers.at(playerID).EntityID = 0; // Overlook this + m_ConnectedPlayers.at(playerID).Name = packet.ReadString(); + m_ConnectedPlayers.at(playerID).PacketID = 0; + m_ConnectedPlayers.at(playerID).StopTime = std::clock(); + m_ConnectedPlayers.at(playerID).TCPAddress = m_Address; + m_ConnectedPlayers.at(playerID).TCPPort = m_Port; + + LOG_INFO("parseTCPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(), + m_ConnectedPlayers.at(playerID).TCPAddress.to_string().c_str()); + + // Send a message to the player that connected + Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID); + // Write playerID to packet + connnectPacket.WritePrimitive(playerID); + m_Reliable.Send(connnectPacket); + + // Send notification that a player has connected + //Packet notificationPacket(MessageType::PlayerConnected); + //broadcast(notificationPacket); +} + +void Server::parseDisconnect() +{ + LOG_INFO("%i: Parsing disconnect", m_PacketID); + + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress == m_Address && + kv.second.TCPPort == m_Port) { + m_PlayersToDisconnect.push_back(kv.first); + break; + } + } } void Server::disconnect(PlayerID playerID) @@ -274,33 +329,14 @@ void Server::disconnect(PlayerID playerID) LOG_INFO("User %s disconnected/timed out", m_ConnectedPlayers[playerID].Name.c_str()); // Remove enteties and stuff (When we can remove entity, remove it and tell clients to remove the copy they have) Events::PlayerDisconnected e; - e.Entity = m_ConnectedPlayers[playerID].EntityID; + e.Entity = m_ConnectedPlayers.at(playerID).EntityID; e.PlayerID = playerID; m_EventBroker->Publish(e); - + //m_World->DeleteEntity(m_ConnectedPlayers[playerID].EntityID); + m_ConnectedPlayers[playerID].TCPSocket->shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_ConnectedPlayers[playerID].TCPSocket->close(); m_ConnectedPlayers.erase(playerID); -} - -void Server::parseOnInputCommand(Packet& packet) -{ - PlayerID player = -1; - // Check which player it was who sent the message - player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - if (player != -1) { - while (packet.DataReadSize() < packet.Size()) { - Events::InputCommand e; - e.Command = packet.ReadString(); - e.PlayerID = player; // Set correct player id - e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); - e.Value = packet.ReadPrimitive(); - m_EventBroker->Publish(e); - - if (e.Command == "PrimaryFire" || e.Command == "Reload") { - m_InputCommandsToBroadcast.push_back(e); - } - //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); - } - } + // Send disconnect to the other players. } void Server::parseOnPlayerDamage(Packet & packet) @@ -313,75 +349,6 @@ void Server::parseOnPlayerDamage(Packet & packet) //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } -void Server::parseConnect(Packet& packet) -{ - LOG_INFO("Parsing connections"); - // Check if player is already connected - if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { - return; - } - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && - kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { - // Already connected - return; - } - } - // Create a new player - PlayerDefinition pd; - pd.EntityID = 0; // Overlook this - pd.Endpoint = m_ReceiverEndpoint; - pd.Name = packet.ReadString(); - pd.PacketID = 0; - pd.StopTime = std::clock(); - m_ConnectedPlayers[m_NextPlayerID++] = pd; - LOG_INFO("Spectator \"%s\" connected on IP: %s", pd.Name.c_str(), pd.Endpoint.address().to_string().c_str()); - - // Send a message to the player that connected - Packet connnectPacket(MessageType::Connect, pd.PacketID); - send(connnectPacket); - - // Send notification that a player has connected - Packet notificationPacket(MessageType::PlayerConnected); - broadcast(notificationPacket); -} - -void Server::parseDisconnect() -{ - LOG_INFO("%i: Parsing disconnect", m_PacketID); - - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && - kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { - disconnect(kv.first); - break; - } - } -} - -void Server::parseClientPing() -{ - LOG_INFO("%i: Parsing ping", m_PacketID); - PlayerID player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - if (player == -1) { - return; - } - // Return ping - Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); - packet.WriteString("Ping received"); - send(packet); -} - -void Server::parsePing() -{ - for (int i = 0; i < m_ConnectedPlayers.size(); i++) { - if (m_ConnectedPlayers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - m_ConnectedPlayers[i].StopTime = std::clock(); - break; - } - } -} - void Server::identifyPacketLoss() { // if no packets lost, difference should be equal to 1 @@ -395,18 +362,7 @@ void Server::kick(PlayerID player) { disconnect(player); Packet packet = Packet(MessageType::Kick); - send(packet); -} - -PlayerID Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) -{ - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == endpoint.address() && - kv.second.Endpoint.port() == endpoint.port()) { - return kv.first; - } - } - return -1; + m_Reliable.Send(packet); } bool Server::OnInputCommand(const Events::InputCommand & e) @@ -435,7 +391,7 @@ bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e) packet.WritePrimitive(e.Spawner.ID); // We don't send PlayerID here because it will always be set to -1 packet.WriteString(m_ConnectedPlayers[e.PlayerID].Name); - send(e.PlayerID, packet); + m_Reliable.Send(packet, m_ConnectedPlayers[e.PlayerID]); return false; } @@ -445,7 +401,7 @@ bool Server::OnEntityDeleted(const Events::EntityDeleted & e) if (shouldSendToClient(EntityWrapper(m_World, e.DeletedEntity))) { Packet packet = Packet(MessageType::EntityDeleted); packet.WritePrimitive(e.DeletedEntity); - broadcast(packet); + reliableBroadcast(packet); } } return false; @@ -458,12 +414,58 @@ bool Server::OnComponentDeleted(const Events::ComponentDeleted & e) Packet packet = Packet(MessageType::ComponentDeleted); packet.WritePrimitive(e.Entity); packet.WriteString(e.ComponentType); - broadcast(packet); + reliableBroadcast(packet); } } return false; } + +void Server::parseClientPing() +{ + LOG_INFO("%i: Parsing ping", m_PacketID); + PlayerID player = GetPlayerIDFromEndpoint(); + if (player == -1) { + return; + } + // Return ping + Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); + packet.WriteString("Ping received"); + m_Reliable.Send(packet); +} + +void Server::parsePing() +{ + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress == m_Address && + kv.second.TCPPort == m_Port) { + kv.second.StopTime = std::clock(); + break; + } + } +} + +void Server::parseOnInputCommand(Packet& packet) +{ + PlayerID player = -1; + // Check which player it was who sent the message + player = GetPlayerIDFromEndpoint(); + if (player != -1) { + while (packet.DataReadSize() < packet.Size()) { + Events::InputCommand e; + e.Command = packet.ReadString(); + e.PlayerID = player; // Set correct player id + e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); + e.Value = packet.ReadPrimitive(); + m_EventBroker->Publish(e); + if (e.Command == "PrimaryFire") { + m_InputCommandsToBroadcast.push_back(e); + } + //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); + } + } +} + void Server::parsePlayerTransform(Packet& packet) { glm::vec3 position; @@ -475,6 +477,7 @@ void Server::parsePlayerTransform(Packet& packet) orientation.y = packet.ReadPrimitive(); orientation.z = packet.ReadPrimitive(); + PlayerID playerID = GetPlayerIDFromEndpoint(); bool hasAssaultWeapon = packet.ReadPrimitive(); int magazineAmmo; int ammo; @@ -483,7 +486,6 @@ void Server::parsePlayerTransform(Packet& packet) ammo = packet.ReadPrimitive(); } - PlayerID playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID); if (player.Valid()) { player["Transform"]["Position"] = position; @@ -500,3 +502,17 @@ bool Server::shouldSendToClient(EntityWrapper childEntity) { return childEntity.HasComponent("Player") || childEntity.FirstParentWithComponent("Player").Valid(); } + +PlayerID Server::GetPlayerIDFromEndpoint() +{ + // check both tcp and udp connection + for (auto& kv : m_ConnectedPlayers) { + if ((kv.second.TCPAddress == m_Address + && kv.second.TCPPort == m_Port) + || (kv.second.Endpoint.address() == m_Address + && kv.second.Endpoint.port() == m_Port)) { + return kv.first; + } + } + return -1; +} \ No newline at end of file diff --git a/src/Engine/Network/TCPClient.cpp b/src/Engine/Network/TCPClient.cpp new file mode 100644 index 00000000..df4f3826 --- /dev/null +++ b/src/Engine/Network/TCPClient.cpp @@ -0,0 +1,107 @@ +#include "Network/TCPClient.h" + +using namespace boost::asio::ip; + +TCPClient::TCPClient() +{ +} + +TCPClient::~TCPClient() +{ +} + +void TCPClient::Connect(std::string playerName, std::string address, int port) +{ + if (m_Socket) { + if (m_IsConnected) { + Packet packet(MessageType::Connect, m_SendPacketID); + packet.WriteString(playerName); + Send(packet); + LOG_INFO("Connect message sent again!"); + } + } + else if (!m_IsConnected) { + boost::system::error_code error = boost::asio::error::host_not_found; + m_Endpoint = tcp::endpoint(boost::asio::ip::address::from_string(address), port); + m_Socket = std::unique_ptr(new tcp::socket(m_IOService)); + m_Socket->connect(m_Endpoint, error); + tcp::no_delay option(true); + m_Socket->set_option(option); + LOG_INFO(error.message().c_str()); + if (!error) { + m_IsConnected = true; + Packet packet(MessageType::Connect, m_SendPacketID); + packet.WriteString(playerName); + Send(packet); + LOG_INFO("Connect message sent!"); + } + // If error + else { + m_Socket->close(); + m_Socket = nullptr; + } + } +} + +void TCPClient::Disconnect() +{ + if (!m_IsConnected) { + return; + } + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_Socket->close(); + m_Socket = nullptr; + m_IsConnected = false; +} + +void TCPClient::Receive(Packet& packet) +{ + size_t bytesRead = readBuffer(m_ReadBuffer); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } +} + +size_t TCPClient::readBuffer(char* data) +{ + if (!m_Socket) { + return 0; + } + boost::system::error_code error; + // Read size of packet + size_t bytesReceived = m_Socket->read_some(boost + ::asio::buffer((void*)data, sizeof(int)), + error); + int sizeOfPacket = 0; + memcpy(&sizeOfPacket, data, sizeof(int)); + + // Read the rest of the message + bytesReceived += m_Socket->read_some(boost + ::asio::buffer((void*)(data + bytesReceived), sizeOfPacket - bytesReceived), + error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + return bytesReceived; +} + +void TCPClient::Send(Packet & packet) +{ + if (!m_Socket) { + LOG_WARNING("TCPClient::Send: Socket is null"); + return; + } + packet.UpdateSize(); + boost::system::error_code error; + m_Socket->send(boost::asio::buffer( + packet.Data(), + packet.Size()), 0, error); +} + +bool TCPClient::IsSocketAvailable() +{ + if (!m_Socket) { + return false; + } + return m_Socket->available(); +} \ No newline at end of file diff --git a/src/Engine/Network/TCPServer.cpp b/src/Engine/Network/TCPServer.cpp new file mode 100644 index 00000000..a449e684 --- /dev/null +++ b/src/Engine/Network/TCPServer.cpp @@ -0,0 +1,110 @@ +#include "Network/TCPServer.h" +using namespace boost::asio::ip; + +TCPServer::TCPServer() +{ + acceptor = std::unique_ptr(new tcp::acceptor(m_IOService, tcp::endpoint(tcp::v4(), 27666))); +} + +TCPServer::~TCPServer() +{ +} + +void TCPServer::AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) +{ + boost::shared_ptr newSocket = boost::shared_ptr(new tcp::socket(m_IOService)); + m_IOService.poll(); + acceptor->async_accept(*newSocket, + boost::bind(&TCPServer::handle_accept, this, newSocket, boost::ref(nextPlayerID), boost::ref(connectedPlayers), + boost::asio::placeholders::error)); +} + +PlayerID GetPlayerIDFromEndpoint(const std::map& connectedPlayers, + boost::asio::ip::address address, unsigned short port) +{ + for (auto& kv : connectedPlayers) { + if (kv.second.TCPAddress == address && + kv.second.TCPPort == port) { + return kv.first; + } + } + return -1; +} + +void TCPServer::handle_accept(boost::shared_ptr socket, + int& nextPlayerID, std::map& connectedPlayers, + const boost::system::error_code& error) +{ + if (!error && GetPlayerIDFromEndpoint(connectedPlayers, socket->remote_endpoint().address(), + socket->remote_endpoint().port()) == -1) { + // Add tcp socket to connections + boost::asio::ip::tcp::no_delay option(true); + socket->set_option(option); + PlayerDefinition pd; + pd.StopTime = std::clock(); + pd.TCPSocket = socket; + pd.TCPAddress = socket.get()->remote_endpoint().address(); + pd.TCPPort = socket.get()->remote_endpoint().port(); + connectedPlayers[nextPlayerID++] = pd; + } +} + +void TCPServer::Send(Packet & packet, PlayerDefinition & playerDefinition) +{ + try { + packet.UpdateSize(); + int bytesSent = playerDefinition.TCPSocket->send( + boost::asio::buffer(packet.Data(), packet.Size()), + 0); + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + playerDefinition.Endpoint = boost::asio::ip::udp::endpoint(); + } +} + +void TCPServer::Send(Packet & packet) +{ + packet.UpdateSize(); + lastReceivedSocket->send( + boost::asio::buffer( + packet.Data(), + packet.Size()), + 0); +} + +void TCPServer::Disconnect() +{ + +} + +void TCPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition) +{ + int bytesRead = readBuffer(m_ReadBuffer, playerDefinition); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } + lastReceivedSocket = playerDefinition.TCPSocket; +} + +int TCPServer::readBuffer(char* data, PlayerDefinition & playerDefinition) +{ + if (!playerDefinition.TCPSocket) { + return 0; + } + boost::system::error_code error; + // Read size of packet + size_t bytesReceived = playerDefinition.TCPSocket->read_some(boost + ::asio::buffer((void*)data, sizeof(int)), + error); + int sizeOfPacket = 0; + memcpy(&sizeOfPacket, data, sizeof(int)); + + // Read the rest of the message + bytesReceived += playerDefinition.TCPSocket->read_some(boost + ::asio::buffer((void*)(data + bytesReceived), sizeOfPacket - bytesReceived), + error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + return bytesReceived; +} \ No newline at end of file diff --git a/src/Engine/Network/UDPClient.cpp b/src/Engine/Network/UDPClient.cpp new file mode 100644 index 00000000..c76de084 --- /dev/null +++ b/src/Engine/Network/UDPClient.cpp @@ -0,0 +1,66 @@ +#include "Network/UDPClient.h" + +using namespace boost::asio::ip; + +UDPClient::UDPClient() +{ +} + +UDPClient::~UDPClient() +{ +} + +void UDPClient::Connect(std::string playerName, std::string address, int port) +{ + if (m_Socket) { + return; + } + m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port); + m_Socket = boost::shared_ptr(new boost::asio::ip::udp::socket(m_IOService)); + m_Socket->connect(m_ReceiverEndpoint); +} + +void UDPClient::Disconnect() +{ + +} + +void UDPClient::Receive(Packet& packet) +{ + int bytesRead = readBuffer(m_ReadBuffer); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } +} + +int UDPClient::readBuffer(char* data) +{ + if (!m_Socket) { + return 0; + } + boost::system::error_code error; + int bytesReceived = m_Socket->receive_from(boost + ::asio::buffer((void*)data, BUFFERSIZE), + m_ReceiverEndpoint, + 0, error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + return bytesReceived; +} + +void UDPClient::Send(Packet& packet) +{ + m_Socket->send_to(boost::asio::buffer( + packet.Data(), + packet.Size()), + m_ReceiverEndpoint, 0); +} + +bool UDPClient::IsSocketAvailable() +{ + if (!m_Socket) { + return false; + } + return m_Socket->available(); +} \ No newline at end of file diff --git a/src/Engine/Network/UDPServer.cpp b/src/Engine/Network/UDPServer.cpp new file mode 100644 index 00000000..4b0a08ba --- /dev/null +++ b/src/Engine/Network/UDPServer.cpp @@ -0,0 +1,62 @@ +#include "Network/UDPServer.h" + +UDPServer::UDPServer() +{ + m_Socket = std::unique_ptr(new boost::asio::ip::udp::socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666))); +} + +UDPServer::~UDPServer() +{ } + +void UDPServer::Send(Packet& packet, PlayerDefinition & playerDefinition) +{ + try { + int bytesSent = m_Socket->send_to( + boost::asio::buffer(packet.Data(), packet.Size()), + playerDefinition.Endpoint, + 0); + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + playerDefinition.Endpoint = boost::asio::ip::udp::endpoint(); + } +} +// Send back to endpoint of received packet +void UDPServer::Send(Packet & packet) +{ + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + m_ReceiverEndpoint, + 0); +} + +void UDPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition) +{ + int bytesRead = readBuffer(m_ReadBuffer); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } + playerDefinition.Endpoint = m_ReceiverEndpoint; +} + +bool UDPServer::IsSocketAvailable() +{ + return m_Socket->available(); +} + +int UDPServer::readBuffer(char* data) +{ + boost::system::error_code error = boost::asio::error::host_not_found; + unsigned int length = m_Socket->receive_from( + boost::asio::buffer((void*)data + , BUFFERSIZE) + , m_ReceiverEndpoint, 0, error); + if (error) { + LOG_WARNING(error.message().c_str()); + } + return length; +} + +void UDPServer::AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) +{ } \ No newline at end of file diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index 33649b3a..ecd0a4b8 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -42,22 +42,22 @@ void PickingPass::InitializeShaderPrograms() m_PickingProgram->BindFragDataLocation(0, "TextureFragment"); m_PickingProgram->Link(); - m_PickingSkinnedProgram = ResourceManager::Load("#PickingSkinnedProgram"); + m_PickingSkinnedProgram = ResourceManager::Load("#PickingSkinnedProgram"); - m_PickingSkinnedProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/PickingSkinned.vert.glsl"))); - m_PickingSkinnedProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/Picking.frag.glsl"))); - m_PickingSkinnedProgram->Compile(); - m_PickingSkinnedProgram->BindFragDataLocation(0, "TextureFragment"); - m_PickingSkinnedProgram->Link(); + m_PickingSkinnedProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/PickingSkinned.vert.glsl"))); + m_PickingSkinnedProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/Picking.frag.glsl"))); + m_PickingSkinnedProgram->Compile(); + m_PickingSkinnedProgram->BindFragDataLocation(0, "TextureFragment"); + m_PickingSkinnedProgram->Link(); } void PickingPass::Draw(RenderScene& scene) { PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle()); - + //TODO: Render: Add code for more jobs than modeljobs. GLuint shaderHandle = m_PickingProgram->GetHandle(); - GLuint shaderSkinnedHandle = m_PickingSkinnedProgram->GetHandle(); + GLuint shaderSkinnedHandle = m_PickingSkinnedProgram->GetHandle(); m_PickingProgram->Bind(); if (scene.ClearDepth) { @@ -92,15 +92,14 @@ void PickingPass::Draw(RenderScene& scene) m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - if (modelJob->Model->IsSkinned()) - { - m_PickingSkinnedProgram->Bind(); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + if (modelJob->Model->IsSkinned()) { + m_PickingSkinnedProgram->Bind(); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); - if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { + if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { std::vector frameBones; if (modelJob->AnimationOffset.animation != nullptr) { @@ -109,15 +108,14 @@ void PickingPass::Draw(RenderScene& scene) frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations); } glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); - - } - } else { + } + } else { m_PickingProgram->Bind(); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); - } + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + } glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); @@ -207,7 +205,7 @@ void PickingPass::Draw(RenderScene& scene) m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - if(modelJob->Model->IsSkinned()) { + if (modelJob->Model->IsSkinned()) { m_PickingSkinnedProgram->Bind(); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); @@ -266,7 +264,7 @@ void PickingPass::Draw(RenderScene& scene) if (modelJob->Model->IsSkinned()) { m_PickingSkinnedProgram->Bind(); - + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); @@ -293,7 +291,7 @@ void PickingPass::Draw(RenderScene& scene) glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); } - + glBindVertexArray(modelJob->Model->VAO); @@ -301,7 +299,7 @@ void PickingPass::Draw(RenderScene& scene) glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } }*/ - + m_PickingBuffer.Unbind(); GLERROR("PickingPass Error"); diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index e43cee13..1fec64d3 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -126,11 +126,12 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) m_PlayerIDs[e.Player.ID] = e.PlayerID; // When a player is actually spawned (since the actual spawning is handled on the server) + // Hack should be moved. + if (!IsClient) { return false; } - // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); bool outOfBodyExperience = ResourceManager::Load("Config.ini")->Get("Debug.OutOfBodyExperience", false);