From 993d804cef57dc77a405301f9c572cb4f33db6cc Mon Sep 17 00:00:00 2001 From: stiffly Date: Fri, 19 Feb 2016 15:38:09 +0100 Subject: [PATCH] Added an event to search for servers. Client now broadcasts a serverlistrequest. An active server will then answer the request and send info about the server. The client saves this data to a list and presents it to the user. --- include/Engine/Network/Client.h | 27 ++++++-- include/Engine/Network/ESearchForServers.h | 12 ++++ include/Engine/Network/MessageType.h | 2 +- include/Engine/Network/Server.h | 6 +- include/Engine/Network/TCPServer.h | 9 ++- include/Engine/Network/UDPClient.h | 1 + include/Engine/Network/UDPServer.h | 4 +- src/Engine/Network/Client.cpp | 74 ++++++++++++++++------ src/Engine/Network/Server.cpp | 49 +++++++++----- src/Engine/Network/TCPServer.cpp | 8 ++- src/Engine/Network/UDPClient.cpp | 15 ++++- src/Engine/Network/UDPServer.cpp | 22 +++++++ 12 files changed, 178 insertions(+), 51 deletions(-) create mode 100644 include/Engine/Network/ESearchForServers.h diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index 01e18da8..7d23670a 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -14,7 +14,6 @@ #include "Network/MessageType.h" #include "Network/PlayerDefinition.h" #include "Network/UDPClient.h" -#include "Network/UDPServer.h" //LOL #include "Network/TCPClient.h" #include "Network/SnapshotDefinitions.h" #include "Core/World.h" @@ -25,6 +24,19 @@ #include "Network/EInterpolate.h" #include "Network/SnapshotFilter.h" #include "Core/EPlayerSpawned.h" +#include "Network/ESearchForServers.h" + +struct ServerInfo +{ + ServerInfo(std::string a, int b, std::string c, int d) + { + Address = a; Port = b; Name = c; PlayersConnected = d; + } + std::string Address = ""; + int Port = 0; + std::string Name = ""; + int PlayersConnected = 0; +}; class Client : public Network { @@ -83,10 +95,11 @@ public: void parseTCPConnect(Packet& packet); void parsePlayerConnected(Packet& packet); void parsePing(); - void parseHeartbeat(Packet& packet, PlayerDefinition); + void parseServerlist(Packet& packet); void parseKick(); void parsePlayersSpawned(Packet& packet); void parseEntityDeletion(Packet& packet); + void parsePlayerDamage(Packet& packet); void parseComponentDeletion(Packet& packet); void InterpolateFields(Packet & packet, const ComponentInfo & componentInfo, const EntityID & entityID, const std::string & componentType); void parseSnapshot(Packet& packet); @@ -96,6 +109,7 @@ public: void sendInputCommands(); void sendLocalPlayerTransform(); void becomePlayer(); + void displayServerlist(); // Mapping Logic // Returns if local EntityID exist in map bool clientServerMapsHasEntity(EntityID clientEntityID); @@ -111,11 +125,16 @@ public: bool OnPlayerDamage(const Events::PlayerDamage& e); EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(const Events::PlayerSpawned& e); - void parsePlayerDamage(Packet& packet); + EventRelay< Client, Events::SearchForServers> m_ESearchForServers; + bool OnSearchForServers(const Events::SearchForServers& e); private: UDPClient m_Unreliable; - UDPServer m_Heartbeat; + UDPClient m_ServerlistRequest; TCPClient m_Reliable; + std::vector m_Serverlist; + bool m_SearchingForServers = false; + std::clock_t m_StartSearchTime; + double m_SearchingTime = 2000; // Config I guess }; #endif diff --git a/include/Engine/Network/ESearchForServers.h b/include/Engine/Network/ESearchForServers.h new file mode 100644 index 00000000..1b08a8f3 --- /dev/null +++ b/include/Engine/Network/ESearchForServers.h @@ -0,0 +1,12 @@ +#ifndef Events_SearchForServers_h__ +#define Events_SearchForServers_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct SearchForServers : public Event { }; + +} +#endif diff --git a/include/Engine/Network/MessageType.h b/include/Engine/Network/MessageType.h index 2b3b02c0..93695063 100644 --- a/include/Engine/Network/MessageType.h +++ b/include/Engine/Network/MessageType.h @@ -19,7 +19,7 @@ enum class MessageType EntityDeleted, ComponentDeleted, PlayerTransform, - Heartbeat, + ServerlistRequest, Invalid }; diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index 0a2cb029..982df3b1 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -33,7 +33,7 @@ private: // Network channels TCPServer m_Reliable; UDPServer m_Unreliable; - UDPClient m_Heartbeat; + UDPServer m_ServerlistRequest; // dont forget to set these in the childrens receive logic boost::asio::ip::address m_Address; int m_Port = 27666; @@ -46,13 +46,11 @@ private: // time for previouse message std::clock_t previousePingMessage = std::clock(); std::clock_t previousSnapshotMessage = std::clock(); - std::clock_t previousHeartbeat = std::clock(); std::clock_t timOutTimer = std::clock(); // How often we send messages (milliseconds) float pingIntervalMs; float snapshotInterval; - float heartbeatInterval = 5000; int checkTimeOutInterval = 100; int m_NextPlayerID = 0; std::vector m_InputCommandsToBroadcast; @@ -71,7 +69,6 @@ private: void addChildrenToPacket(Packet& packet, EntityID entityID); void addInputCommandsToPacket(Packet& packet); void sendPing(); - void sendHeartBeat(); void checkForTimeOuts(); void disconnect(PlayerID playerID); void parseMessageType(Packet& packet); @@ -86,6 +83,7 @@ private: void parseUDPConnect(Packet & packet); void parseTCPConnect(Packet & packet); void parseDisconnect(); + void parseServerlistRequest(boost::asio::ip::udp::endpoint endpoint); bool shouldSendToClient(EntityWrapper childEntity); // Debug event diff --git a/include/Engine/Network/TCPServer.h b/include/Engine/Network/TCPServer.h index 424599c9..1140579a 100644 --- a/include/Engine/Network/TCPServer.h +++ b/include/Engine/Network/TCPServer.h @@ -16,8 +16,9 @@ public: void Send(Packet & packet, PlayerDefinition & playerDefinition); void Send(Packet & packet); void Disconnect(); - int Port() { return acceptor->local_endpoint().port(); } - std::string Address(); + int Port() { return m_Port; } + std::string Address() { return m_Address; } + private: // TCP logic boost::asio::io_service m_IOService; @@ -28,6 +29,10 @@ private: int& nextPlayerID, std::map& connectedPlayers, const boost::system::error_code& error); int readBuffer(char* data, PlayerDefinition& playerDefinition); + int GetPort(); + std::string GetAddress(); + int m_Port = 0; + std::string m_Address = ""; }; #endif \ No newline at end of file diff --git a/include/Engine/Network/UDPClient.h b/include/Engine/Network/UDPClient.h index 3a458d3e..f986dd08 100644 --- a/include/Engine/Network/UDPClient.h +++ b/include/Engine/Network/UDPClient.h @@ -14,6 +14,7 @@ public: void Disconnect(); void Receive(Packet& packet); void Send(Packet & packet); + void Broadcast(Packet& packet, int port); bool IsSocketAvailable(); private: // Assio UDP logic diff --git a/include/Engine/Network/UDPServer.h b/include/Engine/Network/UDPServer.h index 6ba7cd96..15dd977c 100644 --- a/include/Engine/Network/UDPServer.h +++ b/include/Engine/Network/UDPServer.h @@ -13,7 +13,9 @@ public: void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers); void Receive(Packet & packet, PlayerDefinition & playerDefinition); void Send(Packet & packet, PlayerDefinition & playerDefinition); - void Send(Packet & packet); + void Send(Packet & packet); + void Send(Packet & packet, boost::asio::ip::udp::endpoint endpoint); + void Broadcast(Packet & packet, int port); bool IsSocketAvailable(); private: // UDP logic diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index a9a67bae..8d0f40ae 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -1,9 +1,8 @@ #include "Network/Client.h" using namespace boost::asio::ip; -Client::Client(World* world, EventBroker* eventBroker) +Client::Client(World* world, EventBroker* eventBroker) : Network(world, eventBroker) - , m_Heartbeat(13) { // Asumes root node is EntityID_Invalid insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid); @@ -14,6 +13,8 @@ Client::Client(World* world, EventBroker* eventBroker) m_PlayerName = config->Get("Networking.Name", "Raptorcopter"); m_SendInputIntervalMs = config->Get("Networking.SendInputIntervalMs", 33); LOG_INFO("Client initialized"); + + m_ServerlistRequest.Connect(m_PlayerName, "192.168.1.255", 32554); } Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr snapshotFilter) @@ -31,6 +32,7 @@ void Client::Connect(std::string address, int port) EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage); EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_ESearchForServers, &Client::OnSearchForServers); auto config = ResourceManager::Load("Config.ini"); m_Address = address; if (address.empty()) { @@ -66,15 +68,22 @@ void Client::Update() } } - while (m_Heartbeat.IsSocketAvailable()) { + + while (m_ServerlistRequest.IsSocketAvailable()) { Packet packet(MessageType::Invalid); - PlayerDefinition localArea; - localArea.Endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address().from_string("127.0.0.1"), 13); - m_Heartbeat.Receive(packet, localArea); - if(packet.GetMessageType() == MessageType::Heartbeat) { - parseHeartbeat(packet, localArea); + m_ServerlistRequest.Receive(packet); + if (packet.GetMessageType() == MessageType::ServerlistRequest) { + parseServerlist(packet); } } + + if (m_SearchingForServers) { + if (m_SearchingTime < (1000* (std::clock() - m_StartSearchTime) / (double)CLOCKS_PER_SEC)) { + m_SearchingForServers = false; + displayServerlist(); + } + } + if (m_IsConnected) { // Don't send 1 input in 1 packet, bunch em up. if (m_SendInputIntervalMs < (1000 * (std::clock() - m_TimeSinceSentInputs) / (double)CLOCKS_PER_SEC)) { @@ -183,20 +192,18 @@ void Client::parsePing() } -void Client::parseHeartbeat(Packet& packet, PlayerDefinition pd) +void Client::parseServerlist(Packet& packet) { // Pop size, message type, and ID packet.ReadPrimitive(); packet.ReadPrimitive(); packet.ReadPrimitive(); - std::string serverName = packet.ReadString(); - int playersConnected = packet.ReadPrimitive(); std::string address = packet.ReadString(); int port = packet.ReadPrimitive(); - //TODO: save these to some kind of list which can be represented to the player + std::string serverName = packet.ReadString(); + int playersConnected = packet.ReadPrimitive(); //TODO: This should not happen when a client is connected to a server - - LOG_INFO("Serverlist\nName\tPlayers\tIP\t\tPort\n%s\t%i\t%s\t%i\n", serverName.c_str(), playersConnected, address, port); + m_Serverlist.push_back({ address, port, serverName, playersConnected }); } void Client::parseKick() @@ -216,12 +223,12 @@ void Client::parseSpawnEvents() } 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.PlayerID = -1; e.PlayerName = m_PlayerSpawnEvents.at(i).PlayerName; m_EventBroker->Publish(e); } m_PlayerSpawnEvents = tempSpawn; - // m_PlayerSpawnEvents.clear(); + // m_PlayerSpawnEvents.clear(); } void Client::parsePlayersSpawned(Packet& packet) @@ -338,7 +345,7 @@ void Client::parseSnapshot(Packet& packet) if (serverClientMapsHasEntity(serverEntityID)) { EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); EntityWrapper localEntity(m_World, localEntityID); - + // Update entity if (m_World->HasComponent(localEntityID, componentType)) { SharedComponentWrapper newComponent = createSharedComponent(packet, localEntityID, componentInfo); @@ -397,6 +404,12 @@ void Client::disconnect() bool Client::OnInputCommand(const Events::InputCommand & e) { + // TEMP + if (e.Command == "SearchForServers" && e.Value > 0) { + Events::SearchForServers e; + m_EventBroker->Publish(e); + } + if (e.PlayerID != -1) { return false; } @@ -459,12 +472,23 @@ bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e) return true; } +bool Client::OnSearchForServers(const Events::SearchForServers& e) +{ + m_SearchingForServers = true; + m_StartSearchTime = std::clock(); + m_Serverlist.clear(); + LOG_INFO("Searching for LAN servers...\n"); + Packet packet(MessageType::ServerlistRequest); + m_ServerlistRequest.Broadcast(packet, 13); // TODO: Config + return true; +} + void Client::parsePlayerDamage(Packet& packet) { Events::PlayerDamage e; PlayerID victimID = packet.ReadPrimitive(); PlayerID inflictorID = packet.ReadPrimitive(); - if(!serverClientMapsHasEntity(victimID) || !serverClientMapsHasEntity(inflictorID)){ + if (!serverClientMapsHasEntity(victimID) || !serverClientMapsHasEntity(inflictorID)) { return; } e.Inflictor = EntityWrapper(m_World, m_ServerIDToClientID.at(victimID)); @@ -493,7 +517,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) { @@ -501,7 +525,7 @@ void Client::sendLocalPlayerTransform() packet.WritePrimitive((int)cAssaultWeapon["MagazineAmmo"]); packet.WritePrimitive((int)cAssaultWeapon["Ammo"]); } - + m_Unreliable.Send(packet); } @@ -554,6 +578,16 @@ void Client::becomePlayer() m_Reliable.Send(packet); } + +void Client::displayServerlist() +{ + LOG_INFO("This is a serverlist:\n"); + for (int i = 0; i < m_Serverlist.size(); i++) { + ServerInfo si = m_Serverlist[i]; + LOG_INFO("%s:%i\t%s\t%i\n", si.Address, si.Port, si.Name, si.PlayersConnected); + } +} + bool Client::clientServerMapsHasEntity(EntityID clientEntityID) { if (m_ClientIDToServerID.find(clientEntityID) != m_ClientIDToServerID.end()) { diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index 8dd398bd..0bed0b62 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -2,6 +2,7 @@ Server::Server(World* world, EventBroker* eventBroker, int port) : Network(world, eventBroker) + , m_ServerlistRequest(13) { ConfigFile* config = ResourceManager::Load("Config.ini"); snapshotInterval = 1000 * config->Get("Networking.SnapshotInterval", 0.05f); @@ -19,7 +20,6 @@ Server::Server(World* world, EventBroker* eventBroker, int port) } m_Port = port; LOG_INFO("Server initialized and bound to port %i", port); - m_Heartbeat.Connect("Server", "127.0.0.1", 13); } Server::~Server() @@ -60,8 +60,23 @@ void Server::Update() } } + while (m_ServerlistRequest.IsSocketAvailable()) { + Packet packet(MessageType::Invalid); + PlayerDefinition localArea; + localArea.Endpoint = boost::asio::ip::udp::endpoint(); + m_ServerlistRequest.Receive(packet, localArea); + if(packet.GetMessageType() == MessageType::ServerlistRequest) { + packet.ReadPrimitive(); // Pop size + packet.ReadPrimitive(); // Pop MsgType + packet.ReadPrimitive(); // Pop packet ID + int port = packet.ReadPrimitive(); + std::string address = localArea.Endpoint.address().to_string(); + parseServerlistRequest(boost::asio::ip::udp::endpoint(boost::asio::ip::address().from_string(address), port)); + } + } + // Check if players have disconnected - for (int i = 0; i < m_PlayersToDisconnect.size(); i++) { + for (int i = 0; i < m_PlayersToDisconnect.size(); i++) { disconnect(m_PlayersToDisconnect.at(i)); } m_PlayersToDisconnect.clear(); @@ -77,11 +92,7 @@ void Server::Update() sendPing(); previousePingMessage = currentTime; } - // Server heartbeat (display server list on clients) - if (heartbeatInterval < (1000 * (currentTime - previousHeartbeat) / (double)CLOCKS_PER_SEC)) { - sendHeartBeat(); - previousHeartbeat = currentTime; - } + // Time out logic if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) { checkForTimeOuts(); @@ -238,15 +249,6 @@ void Server::sendPing() } -void Server::sendHeartBeat() -{ - Packet packet(MessageType::Heartbeat); - packet.WriteString("Bob"); // server name - packet.WritePrimitive(m_ConnectedPlayers.size()); - packet.WriteString(m_Reliable.Address()); - packet.WritePrimitive(m_Reliable.Port()); - m_Heartbeat.Send(packet); -} void Server::checkForTimeOuts() { @@ -340,6 +342,20 @@ void Server::parseDisconnect() } } + +void Server::parseServerlistRequest(boost::asio::ip::udp::endpoint endpoint) +{ + Packet packet(MessageType::ServerlistRequest); + packet.WriteString(m_Reliable.Address()); + packet.WritePrimitive(m_Reliable.Port()); + packet.WriteString("SERVERNAME"); + packet.WritePrimitive(m_ConnectedPlayers.size()); + //PlayerDefinition pDef; + //pDef.Endpoint = boost::asio::ip::udp::endpoint(endpoint.address(), 13); + + m_ServerlistRequest.Send(packet/*, endpoint*/); +} + void Server::disconnect(PlayerID playerID) { //broadcast("A player disconnected"); @@ -364,6 +380,7 @@ void Server::parseOnPlayerDamage(Packet & packet) e.Victim = EntityWrapper(m_World, packet.ReadPrimitive()); e.Damage = packet.ReadPrimitive(); m_EventBroker->Publish(e); + //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } diff --git a/src/Engine/Network/TCPServer.cpp b/src/Engine/Network/TCPServer.cpp index a55eee98..452efa62 100644 --- a/src/Engine/Network/TCPServer.cpp +++ b/src/Engine/Network/TCPServer.cpp @@ -4,6 +4,8 @@ using namespace boost::asio::ip; TCPServer::TCPServer() { acceptor = std::unique_ptr(new tcp::acceptor(m_IOService, tcp::endpoint(tcp::v4(), 27666))); + m_Port = GetPort(); + m_Address = GetAddress(); } TCPServer::~TCPServer() @@ -76,8 +78,12 @@ void TCPServer::Disconnect() { } +int TCPServer::GetPort() +{ + return acceptor->local_endpoint().port(); +} -std::string TCPServer::Address() +std::string TCPServer::GetAddress() { boost::asio::ip::tcp::resolver resolver(m_IOService); boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), boost::asio::ip::host_name(), ""); diff --git a/src/Engine/Network/UDPClient.cpp b/src/Engine/Network/UDPClient.cpp index c76de084..68aebb03 100644 --- a/src/Engine/Network/UDPClient.cpp +++ b/src/Engine/Network/UDPClient.cpp @@ -15,9 +15,9 @@ 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_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); + m_Socket->open(boost::asio::ip::udp::v4()); } void UDPClient::Disconnect() @@ -55,6 +55,17 @@ void UDPClient::Send(Packet& packet) packet.Data(), packet.Size()), m_ReceiverEndpoint, 0); +} + +void UDPClient::Broadcast(Packet& packet, int port) +{ + m_Socket->set_option(boost::asio::socket_base::broadcast(true)); + m_Socket->send_to(boost::asio::buffer( + packet.Data(), + packet.Size()), + udp::endpoint(boost::asio::ip::address_v4().broadcast(), port) + , 0); + m_Socket->set_option(boost::asio::socket_base::broadcast(false)); } bool UDPClient::IsSocketAvailable() diff --git a/src/Engine/Network/UDPServer.cpp b/src/Engine/Network/UDPServer.cpp index b4046003..f751d369 100644 --- a/src/Engine/Network/UDPServer.cpp +++ b/src/Engine/Network/UDPServer.cpp @@ -36,7 +36,29 @@ void UDPServer::Send(Packet & packet) 0); } +// Broadcasting respond specific logic +void UDPServer::Send(Packet & packet, boost::asio::ip::udp::endpoint endpoint) +{ + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + endpoint, + 0); +} +// Broadcasting +void UDPServer::Broadcast(Packet & packet, int port) +{ + m_Socket->set_option(boost::asio::socket_base::broadcast(true)); + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4().broadcast(),port), + 0); + m_Socket->set_option(boost::asio::socket_base::broadcast(false)); +} void UDPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition) {