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.
This commit is contained in:
stiffly
2016-02-19 15:38:09 +01:00
parent 46987beefb
commit 993d804cef
12 changed files with 178 additions and 51 deletions
+23 -4
View File
@@ -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<Client, Events::PlayerSpawned> 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<ServerInfo> m_Serverlist;
bool m_SearchingForServers = false;
std::clock_t m_StartSearchTime;
double m_SearchingTime = 2000; // Config I guess
};
#endif
@@ -0,0 +1,12 @@
#ifndef Events_SearchForServers_h__
#define Events_SearchForServers_h__
#include "Core/Event.h"
namespace Events
{
struct SearchForServers : public Event { };
}
#endif
+1 -1
View File
@@ -19,7 +19,7 @@ enum class MessageType
EntityDeleted,
ComponentDeleted,
PlayerTransform,
Heartbeat,
ServerlistRequest,
Invalid
};
+2 -4
View File
@@ -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<Events::InputCommand> 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
+7 -2
View File
@@ -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<PlayerID, PlayerDefinition>& 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
+1
View File
@@ -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
+3 -1
View File
@@ -13,7 +13,9 @@ public:
void AcceptNewConnections(int& nextPlayerID, std::map<PlayerID, PlayerDefinition>& 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
+54 -20
View File
@@ -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<std::string>("Networking.Name", "Raptorcopter");
m_SendInputIntervalMs = config->Get<int>("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> 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<ConfigFile>("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<int>();
packet.ReadPrimitive<int>();
packet.ReadPrimitive<int>();
std::string serverName = packet.ReadString();
int playersConnected = packet.ReadPrimitive<int>();
std::string address = packet.ReadString();
int port = packet.ReadPrimitive<int>();
//TODO: save these to some kind of list which can be represented to the player
std::string serverName = packet.ReadString();
int playersConnected = packet.ReadPrimitive<int>();
//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<EntityID>();
PlayerID inflictorID = packet.ReadPrimitive<EntityID>();
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()) {
+33 -16
View File
@@ -2,6 +2,7 @@
Server::Server(World* world, EventBroker* eventBroker, int port)
: Network(world, eventBroker)
, m_ServerlistRequest(13)
{
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
snapshotInterval = 1000 * config->Get<float>("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<int>(); // Pop size
packet.ReadPrimitive<int>(); // Pop MsgType
packet.ReadPrimitive<int>(); // Pop packet ID
int port = packet.ReadPrimitive<int>();
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<int>(m_ConnectedPlayers.size());
packet.WriteString(m_Reliable.Address());
packet.WritePrimitive<int>(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<int>(m_Reliable.Port());
packet.WriteString("SERVERNAME");
packet.WritePrimitive<int>(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<EntityID>());
e.Damage = packet.ReadPrimitive<double>();
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());
}
+7 -1
View File
@@ -4,6 +4,8 @@ using namespace boost::asio::ip;
TCPServer::TCPServer()
{
acceptor = std::unique_ptr<tcp::acceptor>(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(), "");
+13 -2
View File
@@ -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<boost::asio::ip::udp::socket>(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()
+22
View File
@@ -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)
{