diff --git a/doomsday/apps/client/include/network/serverlink.h b/doomsday/apps/client/include/network/serverlink.h index bd43bebb28..6997192368 100644 --- a/doomsday/apps/client/include/network/serverlink.h +++ b/doomsday/apps/client/include/network/serverlink.h @@ -50,6 +50,8 @@ class ServerLink : public de::shell::AbstractLink }; Q_DECLARE_FLAGS(Flags, Flag) + static ServerLink &get(); + public: ServerLink(Flags flags = DiscoverLocalServers); diff --git a/doomsday/apps/client/src/network/base/net_main.cpp b/doomsday/apps/client/src/network/base/net_main.cpp index 1e9e4b2ceb..f8a3d0745f 100644 --- a/doomsday/apps/client/src/network/base/net_main.cpp +++ b/doomsday/apps/client/src/network/base/net_main.cpp @@ -95,17 +95,19 @@ dint gotFrame; ///< @c true if a frame packet has been received. dd_bool firstNetUpdate = true; -byte monitorMsgQueue; -byte netShowLatencies; -byte netDev; -dfloat netConnectTime; +static byte monitorMsgQueue; +static byte netDev; +#ifdef __SERVER__ +static byte netShowLatencies; +static byte netAllowJoin = true; +#endif +//static dfloat netConnectTime; //dint netCoordTime = 17; -dfloat netConnectTimeout = 10; dfloat netSimulatedLatencySeconds; // Local packets are stored into this buffer. -dd_bool reboundPacket; -netbuffer_t reboundStore; +static dd_bool reboundPacket; +static netbuffer_t reboundStore; #ifdef __CLIENT__ static dint coordTimer; @@ -1305,14 +1307,11 @@ void Net_Register() //C_VAR_CHARPTR ("net-master-path", &::masterPath, 0, 0, 0); C_VAR_CHARPTR ("net-name", &::playerName, 0, 0, 0); -#ifdef __CLIENT__ - C_VAR_FLOAT ("client-connect-timeout", &::netConnectTimeout, CVF_NO_MAX, 0, 0); -#endif - #ifdef __SERVER__ C_VAR_CHARPTR ("server-name", &::serverName, 0, 0, 0); C_VAR_CHARPTR ("server-info", &::serverInfo, 0, 0, 0); C_VAR_INT2 ("server-public", &::masterAware, 0, 0, 1, masterAwareChanged); + C_VAR_BYTE2 ("server-allowjoin", &netAllowJoin, 0, 0, 1, masterAwareChanged); C_VAR_CHARPTR ("server-password", &::netPassword, 0, 0, 0); C_VAR_BYTE ("server-latencies", &::netShowLatencies, 0, 0, 1); C_VAR_INT ("server-frame-interval", &::frameInterval, CVF_NO_MAX, 0, 0); diff --git a/doomsday/apps/client/src/network/serverlink.cpp b/doomsday/apps/client/src/network/serverlink.cpp index b810fd7e47..466a9d835a 100644 --- a/doomsday/apps/client/src/network/serverlink.cpp +++ b/doomsday/apps/client/src/network/serverlink.cpp @@ -29,6 +29,7 @@ #include "ui/widgets/taskbarwidget.h" #include "dd_def.h" #include "dd_main.h" +#include "clientapp.h" #include #include @@ -684,3 +685,8 @@ void ServerLink::handleIncomingPackets() } } } + +ServerLink &ServerLink::get() // static +{ + return ClientApp::serverLink(); +} diff --git a/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp b/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp index ec347b9500..6e5e0f0e9c 100644 --- a/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp +++ b/doomsday/apps/client/src/ui/dialogs/serverinfodialog.cpp @@ -40,6 +40,9 @@ using namespace de; +static DialogWidget::RoleFlags const ID_SV_PACKAGES = DialogWidget::Id1; +static DialogWidget::RoleFlags const ID_JOIN = DialogWidget::Id2; + DENG_GUI_PIMPL(ServerInfoDialog) , DENG2_OBSERVES(ServerLink, MapOutline) , public PackagesWidget::IPackageStatus @@ -86,14 +89,15 @@ DENG_GUI_PIMPL(ServerInfoDialog) // on what kind of package is being displayed. self().buttons() << new DialogButtonItem(Default | Accept, tr("Close")) - << new DialogButtonItem(Action, tr("Join Game"), new CallbackAction([this] () - { + << new DialogButtonItem(Action | ID_JOIN, tr("Join Game"), new CallbackAction([this] () { self().accept(); emit self().joinGame(); })) - << new DialogButtonItem(ActionPopup | Id1, style().images().image("package.icon"), tr("Server")); + << new DialogButtonItem(ActionPopup | ID_SV_PACKAGES, + style().images().image("package.icon"), tr("Server")); createWidgets(); + self().buttonWidget(ID_JOIN)->disable(); } bool isPackageHighlighted(String const &) const @@ -170,7 +174,7 @@ DENG_GUI_PIMPL(ServerInfoDialog) // Action buttons. - auto *svBut = self().popupButtonWidget(Id1); + auto *svBut = self().popupButtonWidget(ID_SV_PACKAGES); svBut->setPopup(*serverPopup); svBut->setText(tr("Server")); svBut->setTextAlignment(ui::AlignLeft); @@ -282,6 +286,9 @@ DENG_GUI_PIMPL(ServerInfoDialog) gameState->setText(msg); } + // Actions. + self().buttonWidget(ID_JOIN)->enable(serverInfo.flags().testFlag(shell::ServerInfo::AllowJoin)); + // Local packages. { localPackages->setDialogTitle(tr("Local Packages for %1 Multiplayer").arg(gameTitle)); @@ -330,8 +337,8 @@ DENG_GUI_PIMPL(ServerInfoDialog) serverPackages->setPopulationEnabled(true); serverPackages->setManualPackageIds(available); - self().buttonWidget(Id1)->enable(); - self().buttonWidget(Id1)->setText(tr("Server: %1").arg(serverInfo.packages().size())); + self().buttonWidget(ID_SV_PACKAGES)->enable(); + self().buttonWidget(ID_SV_PACKAGES)->setText(tr("Server: %1").arg(serverInfo.packages().size())); } } diff --git a/doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp b/doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp index 7d85694de5..4e6eb1f62c 100644 --- a/doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp +++ b/doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp @@ -21,16 +21,15 @@ #include "ui/widgets/taskbarwidget.h" #include "ui/home/headerwidget.h" #include "ui/clientwindow.h" -#include "clientapp.h" +#include "network/serverlink.h" #include +#include #include #include #include #include -#include "ui/commandaction.h" - using namespace de; DENG_GUI_PIMPL(MultiplayerColumnWidget) @@ -49,7 +48,8 @@ DENG_GUI_PIMPL(MultiplayerColumnWidget) << new ui::ActionItem(tr("Connect to Server..."), new SignalAction(&ClientWindow::main().taskBar(), SLOT(connectToServerManually()))) - << new ui::ActionItem(tr("Refresh List"), new CommandAction("net request")); + << new ui::ActionItem(tr("Refresh List"), new CallbackAction([] () { + ServerLink::get().discoverUsingMaster(); })); return menu; }, ui::Down); diff --git a/doomsday/apps/client/src/ui/home/multiplayerpanelbuttonwidget.cpp b/doomsday/apps/client/src/ui/home/multiplayerpanelbuttonwidget.cpp index 290e3975e0..4c555124fa 100644 --- a/doomsday/apps/client/src/ui/home/multiplayerpanelbuttonwidget.cpp +++ b/doomsday/apps/client/src/ui/home/multiplayerpanelbuttonwidget.cpp @@ -56,6 +56,8 @@ DENG_GUI_PIMPL(MultiplayerPanelButtonWidget) DoomsdayApp::games().audienceForReadiness() += this; joinButton = new ButtonWidget; + joinButton->setAttribute(AutomaticOpacity); + joinButton->disable(); joinButton->setText(tr("Join")); joinButton->useInfoStyle(); joinButton->setSizePolicy(ui::Expand, ui::Expand); @@ -189,6 +191,10 @@ void MultiplayerPanelButtonWidget::updateContent(shell::ServerInfo const &info) icon().setImage(nullptr); } + if (!info.flags().testFlag(shell::ServerInfo::AllowJoin)) + { + d->joinButton->disable(); + } infoText += "\n" _E(C) + String(info.description()) + _E(.); int const localCount = Game::localMultiplayerPackages(info.gameId()).size(); diff --git a/doomsday/apps/client/src/ui/widgets/homeitemwidget.cpp b/doomsday/apps/client/src/ui/widgets/homeitemwidget.cpp index b63542add8..564ffc7b22 100644 --- a/doomsday/apps/client/src/ui/widgets/homeitemwidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/homeitemwidget.cpp @@ -218,6 +218,7 @@ HomeItemWidget::HomeItemWidget(Flags flags, String const &name) , d(new Impl(this)) { setBehavior(Focusable | ContentClipping); + setAttribute(AutomaticOpacity); addEventHandler(new Impl::ClickHandler(*this)); Rule const &iconSize = d->label->margins().height() + diff --git a/doomsday/apps/client/src/ui/widgets/multiplayerservermenuwidget.cpp b/doomsday/apps/client/src/ui/widgets/multiplayerservermenuwidget.cpp index a56ac93a6a..35ea58f0ee 100644 --- a/doomsday/apps/client/src/ui/widgets/multiplayerservermenuwidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/multiplayerservermenuwidget.cpp @@ -100,8 +100,6 @@ DENG2_PIMPL(MultiplayerServerMenuWidget) void linkDiscoveryUpdate(ServerLink const &link) override { - bool changed = false; - ui::Data &items = self().items(); QSet foundHosts; @@ -121,7 +119,6 @@ DENG2_PIMPL(MultiplayerServerMenuWidget) if (!foundHosts.contains(id)) { items.remove(idx--); - changed = true; } } @@ -137,7 +134,6 @@ DENG2_PIMPL(MultiplayerServerMenuWidget) // Needs to be added. items.append(new ServerListItem(info, link.isServerOnLocalNetwork(info.address()))); - changed = true; } else { @@ -146,33 +142,30 @@ DENG2_PIMPL(MultiplayerServerMenuWidget) } } - if (changed) + items.stableSort([] (ui::Item const &a, ui::Item const &b) { - items.sort([] (ui::Item const &a, ui::Item const &b) - { - auto const &first = a.as(); - auto const &second = b.as(); + auto const &first = a.as(); + auto const &second = b.as(); - // LAN games shown first. - if (first.isLocal() == second.isLocal()) + // LAN games shown first. + if (first.isLocal() == second.isLocal()) + { + // Sort by number of players. + if (first.info().playerCount() == second.info().playerCount()) { - // Sort by number of players. - if (first.info().playerCount() == second.info().playerCount()) + // Finally, by game ID. + int cmp = first.info().gameId().compareWithCase(second.info().gameId()); + if (!cmp) { - // Finally, by game ID. - int cmp = first.info().gameId().compareWithCase(second.info().gameId()); - if (!cmp) - { - // Lastly by server name. - return first.info().name().compareWithoutCase(second.info().name()) < 0; - } - return cmp < 0; + // Lastly by server name. + return first.info().name().compareWithoutCase(second.info().name()) < 0; } - return first.info().playerCount() - second.info().playerCount() > 0; + return cmp < 0; } - return first.isLocal(); - }); - } + return first.info().playerCount() - second.info().playerCount() > 0; + } + return first.isLocal(); + }); } void currentGameChanged(Game const &newGame) override diff --git a/doomsday/apps/libdoomsday/net.dengine.base.pack/helpstrings.txt b/doomsday/apps/libdoomsday/net.dengine.base.pack/helpstrings.txt index 8e8d1b7de5..2fb0f48412 100644 --- a/doomsday/apps/libdoomsday/net.dengine.base.pack/helpstrings.txt +++ b/doomsday/apps/libdoomsday/net.dengine.base.pack/helpstrings.txt @@ -938,6 +938,9 @@ desc = 1=Enable shiny textures on surfaces of the map. [rend-tex] desc = 1=Render with textures. 2=Render with gray texture. +[server-allowjoin] +desc = 1=Allow new clients to join the game. + [server-frame-interval] desc = Minimum number of tics between sent frames. diff --git a/doomsday/apps/server/src/serverapp.cpp b/doomsday/apps/server/src/serverapp.cpp index 16b3d7e0c3..17e51a49c5 100644 --- a/doomsday/apps/server/src/serverapp.cpp +++ b/doomsday/apps/server/src/serverapp.cpp @@ -24,6 +24,8 @@ #include #include +#include + #include #include #include @@ -269,9 +271,10 @@ shell::ServerInfo ServerApp::currentServerInfo() // The server player is there, it's just hidden. info.setMaxPlayers(de::min(svMaxPlayers, DDMAXPLAYERS - (isDedicated ? 1 : 0))); - //info->canJoin = ; shell::ServerInfo::Flags flags(0); - if (isServer != 0 && Sv_GetNumPlayers() < svMaxPlayers) + if (CVar_Byte(Con_FindVariable("server-allowjoin")) + && isServer != 0 + && Sv_GetNumPlayers() < svMaxPlayers) { flags |= shell::ServerInfo::AllowJoin; } diff --git a/doomsday/apps/server/src/serversystem.cpp b/doomsday/apps/server/src/serversystem.cpp index 827c5e37da..733a12ea7a 100644 --- a/doomsday/apps/server/src/serversystem.cpp +++ b/doomsday/apps/server/src/serversystem.cpp @@ -260,6 +260,7 @@ RemoteUser &ServerSystem::user(Id const &id) const bool ServerSystem::isUserAllowedToJoin(RemoteUser &/*user*/) const { + if (!CVar_Byte(Con_FindVariable("server-allowjoin"))) return false; // If the server is full, attempts to connect are canceled. return (Sv_GetNumConnected() < svMaxPlayers); } diff --git a/doomsday/doc/engine/variable/server-allowjoin.ame b/doomsday/doc/engine/variable/server-allowjoin.ame new file mode 100644 index 0000000000..82babe60fb --- /dev/null +++ b/doomsday/doc/engine/variable/server-allowjoin.ame @@ -0,0 +1,3 @@ +@summary { + 1=Allow new clients to join the game. +}