diff --git a/doomsday/apps/client/include/network/serverlink.h b/doomsday/apps/client/include/network/serverlink.h index 7e524f39b3..f000c112cc 100644 --- a/doomsday/apps/client/include/network/serverlink.h +++ b/doomsday/apps/client/include/network/serverlink.h @@ -38,6 +38,7 @@ class ServerLink : public de::shell::AbstractLink public: DENG2_DEFINE_AUDIENCE2(DiscoveryUpdate, void linkDiscoveryUpdate(ServerLink const &link)) + DENG2_DEFINE_AUDIENCE2(PingResponse, void pingResponse(de::Address const &, de::TimeDelta)) DENG2_DEFINE_AUDIENCE2(MapOutline, void mapOutlineReceived(de::Address const &, de::shell::MapOutlinePacket const &)) DENG2_DEFINE_AUDIENCE2(Join, void networkGameJoined()) @@ -89,6 +90,8 @@ class ServerLink : public de::shell::AbstractLink void requestMapOutline(de::Address const &address); + void ping(de::Address const &address); + void connectDomain(de::String const &domain, de::TimeDelta const &timeout = 0) override; void connectHost(de::Address const &address) override; diff --git a/doomsday/apps/client/src/network/serverlink.cpp b/doomsday/apps/client/src/network/serverlink.cpp index d7a50cd183..8e7e38553a 100644 --- a/doomsday/apps/client/src/network/serverlink.cpp +++ b/doomsday/apps/client/src/network/serverlink.cpp @@ -45,6 +45,8 @@ #include #include #include + +#include #include using namespace de; @@ -53,6 +55,8 @@ enum LinkState { None, Discovering, + Pinging, + WaitingForPong, QueryingMapOutline, WaitingForInfoResponse, Joining, @@ -60,6 +64,8 @@ enum LinkState InGame }; +static int const NUM_PINGS = 5; + DENG2_PIMPL(ServerLink) { std::unique_ptr finder; ///< Finding local servers. @@ -68,6 +74,9 @@ DENG2_PIMPL(ServerLink) typedef QMap Servers; Servers discovered; Servers fromMaster; + QElapsedTimer pingTimer; + QList pings; + int pingCounter; std::unique_ptr serverProfile; ///< Profile used when joining. std::function profileResultCallback; std::function profileResultCallbackWithAddress; @@ -326,12 +335,14 @@ DENG2_PIMPL(ServerLink) } DENG2_PIMPL_AUDIENCE(DiscoveryUpdate) + DENG2_PIMPL_AUDIENCE(PingResponse) DENG2_PIMPL_AUDIENCE(MapOutline) DENG2_PIMPL_AUDIENCE(Join) DENG2_PIMPL_AUDIENCE(Leave) }; DENG2_AUDIENCE_METHOD(ServerLink, DiscoveryUpdate) +DENG2_AUDIENCE_METHOD(ServerLink, PingResponse) DENG2_AUDIENCE_METHOD(ServerLink, MapOutline) DENG2_AUDIENCE_METHOD(ServerLink, Join) DENG2_AUDIENCE_METHOD(ServerLink, Leave) @@ -462,6 +473,12 @@ void ServerLink::requestMapOutline(Address const &address) LOG_NET_VERBOSE("Querying %s for map outline") << address; } +void ServerLink::ping(const Address &address) +{ + AbstractLink::connectHost(address); + d->state = Pinging; +} + void ServerLink::connectDomain(String const &domain, TimeDelta const &timeout) { LOG_AS("ServerLink::connectDomain"); @@ -601,6 +618,14 @@ void ServerLink::initiateCommunications() d->state = WaitingForInfoResponse; break; + case Pinging: + *this << ByteRefArray("Ping?", 5); + d->state = WaitingForPong; + d->pingCounter = NUM_PINGS; + d->pings.clear(); + d->pingTimer.start(); + break; + case QueryingMapOutline: *this << ByteRefArray("MapOutline?", 11); d->state = WaitingForInfoResponse; @@ -663,6 +688,34 @@ void ServerLink::handleIncomingPackets() if (!d->handleJoinResponse(*packet)) return; break; + case WaitingForPong: + if (packet->size() == 4 && *packet == "Pong" && + d->pingCounter-- > 0) + { + d->pings.append(TimeDelta::fromMilliSeconds(d->pingTimer.elapsed())); + *this << ByteRefArray("Ping?", 5); + d->pingTimer.restart(); + } + else + { + Address const svAddress = address(); + disconnect(); + + // Notify about the average ping time. + if (d->pings.count()) + { + TimeDelta average = 0; + for (auto const &d : d->pings) average += d; + average = average / d->pings.count(); + + DENG2_FOR_AUDIENCE2(PingResponse, i) + { + i->pingResponse(svAddress, average); + } + } + } + break; + case InGame: { /// @todo The incoming packets should be handled immediately. diff --git a/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp b/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp index 6e5e0f0e9c..7a2d02a568 100644 --- a/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp +++ b/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp @@ -45,6 +45,7 @@ static DialogWidget::RoleFlags const ID_JOIN = DialogWidget::Id2; DENG_GUI_PIMPL(ServerInfoDialog) , DENG2_OBSERVES(ServerLink, MapOutline) +, DENG2_OBSERVES(ServerLink, PingResponse) , public PackagesWidget::IPackageStatus { // Server info & status. @@ -60,6 +61,7 @@ DENG_GUI_PIMPL(ServerInfoDialog) { QueryNone, QueryStatus, + QueryPing, QueryMapOutline, }; Query pendingQuery = QueryNone; @@ -80,7 +82,8 @@ DENG_GUI_PIMPL(ServerInfoDialog) , serverInfo(sv) , link(ServerLink::ManualConnectionOnly) { - link.audienceForMapOutline() += this; + link.audienceForMapOutline() += this; + link.audienceForPingResponse() += this; connect(&queryTimer, &QTimer::timeout, [this] () { beginPendingQuery(); }); self().useInfoStyle(); @@ -386,6 +389,10 @@ DENG_GUI_PIMPL(ServerInfoDialog) } break; + case QueryPing: + link.ping(host); + break; + case QueryMapOutline: link.requestMapOutline(host); break; @@ -412,6 +419,12 @@ DENG_GUI_PIMPL(ServerInfoDialog) void mapOutlineReceived(Address const &, shell::MapOutlinePacket const &packet) { mapOutline->setOutline(packet); + startQuery(QueryPing); + } + + void pingResponse(Address const &, TimeDelta pingTime) + { + qDebug() << "Ping:" << pingTime.asMilliSeconds() << "ms"; } }; diff --git a/doomsday/apps/server/src/remoteuser.cpp b/doomsday/apps/server/src/remoteuser.cpp index 929f76b880..36c00db432 100644 --- a/doomsday/apps/server/src/remoteuser.cpp +++ b/doomsday/apps/server/src/remoteuser.cpp @@ -136,6 +136,10 @@ DENG2_PIMPL(RemoteUser) LOGDEV_NET_VERBOSE("Info reply:\n%s") << String::fromUtf8(msg); self() << msg; } + else if (command == "Ping?") + { + self() << Block("Pong"); + } else if (command == "MapOutline?") { shell::MapOutlinePacket packet;