diff --git a/include/ClientLib/ClientSessionHandler.hpp b/include/ClientLib/ClientSessionHandler.hpp index 1e1dffbe..9c694cb4 100644 --- a/include/ClientLib/ClientSessionHandler.hpp +++ b/include/ClientLib/ClientSessionHandler.hpp @@ -43,6 +43,8 @@ namespace tsom ClientSessionHandler(NetworkSession* session, Nz::ApplicationBase& app, ConfigFile& config, Nz::EnttWorld& world, ClientBlockLibrary& blockLibrary); ~ClientSessionHandler(); + template void ForEachPlayer(F&& callback) const; + inline entt::handle GetControlledEntity() const; inline EntityRegistry& GetEntityRegistry(); inline const EntityRegistry& GetEntityRegistry() const; @@ -92,6 +94,7 @@ namespace tsom NazaraSignal(OnPlayerJoined, const PlayerInfo& /*playerInfo*/); NazaraSignal(OnPlayerLeave, const PlayerInfo& /*playerInfo*/); NazaraSignal(OnPlayerNameUpdate, const PlayerInfo& /*playerInfo*/, const std::string& /*newNickname*/); + NazaraSignal(OnPlayerListUpdated); static constexpr Packets::Helper::EntityId InvalidEntity = Nz::MaxValue(); diff --git a/include/ClientLib/ClientSessionHandler.inl b/include/ClientLib/ClientSessionHandler.inl index 4f3a2336..cb8f6ca5 100644 --- a/include/ClientLib/ClientSessionHandler.inl +++ b/include/ClientLib/ClientSessionHandler.inl @@ -6,6 +6,17 @@ namespace tsom { + template + void ClientSessionHandler::ForEachPlayer(F&& callback) const + { + for (PlayerIndex index = 0; index < m_players.size(); ++index) + { + auto& playerInfoOpt = m_players[index]; + if (playerInfoOpt) + callback(index, *playerInfoOpt); + } + } + inline entt::handle ClientSessionHandler::GetControlledEntity() const { return m_playerControlledEntity; diff --git a/src/ClientLib/ClientSessionHandler.cpp b/src/ClientLib/ClientSessionHandler.cpp index 08086210..03005bf9 100644 --- a/src/ClientLib/ClientSessionHandler.cpp +++ b/src/ClientLib/ClientSessionHandler.cpp @@ -468,6 +468,8 @@ namespace tsom playerInfo.nickname = std::move(playerData.nickname).Str(); playerInfo.isAuthenticated = playerData.isAuthenticated; } + + OnPlayerListUpdated(); } void ClientSessionHandler::HandlePacket(Packets::S_NetworkStrings&& networkStrings) @@ -499,6 +501,7 @@ namespace tsom playerInfo.isAuthenticated = playerJoin.isAuthenticated; OnPlayerJoined(playerInfo); + OnPlayerListUpdated(); } void ClientSessionHandler::HandlePacket(Packets::S_PlayerLeave&& playerLeave) @@ -510,6 +513,7 @@ namespace tsom } OnPlayerLeave(*m_players[playerLeave.index]); + OnPlayerListUpdated(); m_players[playerLeave.index].reset(); } @@ -525,6 +529,8 @@ namespace tsom auto& playerInfo = *m_players[playerNameUpdate.index]; OnPlayerNameUpdate(playerInfo, playerNameUpdate.newNickname); + OnPlayerListUpdated(); + playerInfo.nickname = std::move(playerNameUpdate.newNickname).Str(); if (playerInfo.textSprite) playerInfo.textSprite->Update(Nz::SimpleTextDrawer::Draw(playerInfo.nickname, 48, Nz::TextStyle_Regular, (playerInfo.isAuthenticated) ? Nz::Color::White() : Nz::Color::Gray()), 0.01f); diff --git a/src/Game/States/GameState.cpp b/src/Game/States/GameState.cpp index e1841701..47e76ec3 100644 --- a/src/Game/States/GameState.cpp +++ b/src/Game/States/GameState.cpp @@ -368,6 +368,9 @@ namespace tsom }); m_interactionLabel = CreateWidget(); + m_playerListWidget = CreateWidget(); + RefreshPlayerListWidget(); + m_playerListWidget->Hide(); m_onUnhandledKeyPressed.Connect(stateData.canvas->OnUnhandledKeyPressed, [this](const Nz::WindowEventHandler*, const Nz::WindowEvent::KeyEvent& event) { @@ -416,10 +419,10 @@ namespace tsom m_toolsMenu->Show(); UpdateMouseLock(); } - break; } + case Nz::Keyboard::Scancode::Escape: { if (m_escapeMenu->IsVisible()) @@ -550,6 +553,12 @@ namespace tsom break; } + case Nz::Keyboard::Scancode::F10: + { + m_playerListWidget->Show(); + break; + } + default: break; } @@ -557,8 +566,6 @@ namespace tsom m_onUnhandledKeyReleased.Connect(stateData.canvas->OnUnhandledKeyReleased, [this](const Nz::WindowEventHandler*, const Nz::WindowEvent::KeyEvent& event) { - auto& stateData = GetStateData(); - switch (event.scancode) { case Nz::Keyboard::Scancode::Tab: @@ -584,6 +591,12 @@ namespace tsom break; } + case Nz::Keyboard::Scancode::F10: + { + m_playerListWidget->Hide(); + break; + } + default: break; } @@ -647,7 +660,7 @@ namespace tsom { Chatbox::ColorItem{ Nz::Color::White() } }, { Chatbox::TextItem{ " joined the server" } } } - }); + }); spdlog::info("{0} joined the server", playerInfo.nickname); }); @@ -682,6 +695,11 @@ namespace tsom spdlog::info("{0} renamed to {1}", playerInfo.nickname, newNickname); }); + m_onPlayerListUpdated.Connect(stateData.sessionHandler->OnPlayerListUpdated, [this] + { + RefreshPlayerListWidget(); + }); + m_onControlledShip.Connect(stateData.sessionHandler->OnControlledShip, [this](entt::handle shipEntity, entt::handle shipExteriorEntity, const Nz::Quaternionf& referenceRotation) { m_pilotedShip = PilotedShip{ @@ -1298,11 +1316,33 @@ namespace tsom m_crosshairEntity.get().SetPosition({ newSize.x * 0.5f, newSize.y * 0.5f }); m_healthOxygen.entity.get().SetPosition({ newSize.x * 0.5f - m_healthOxygen.entity.get().GetAABB().x / 2.f, hudNextHeight }); + m_playerListWidget->Center(); m_escapeMenu->Center(); m_toolsMenu->Center(); } + void GameState::RefreshPlayerListWidget() + { + Nz::RichTextDrawer textDrawer; + textDrawer.AppendText(""); //< will override later + + std::size_t playerCount = 0; + GetStateData().sessionHandler->ForEachPlayer([&](PlayerIndex playerIndex, const ClientSessionHandler::PlayerInfo& playerInfo) + { + textDrawer.SetTextColor((playerInfo.isAuthenticated) ? Nz::Color::Yellow() : Nz::Color::Gray()); + textDrawer.AppendText(playerInfo.nickname, (playerCount == 0) ? true : false); //< force a new block to avoid being merged with the first one + textDrawer.AppendText("\n"); + + playerCount++; + }); + + textDrawer.SetBlockText(0, fmt::format("Players ({0})\n", playerCount)); + + m_playerListWidget->UpdateText(textDrawer); + m_playerListWidget->Center(); + } + Nz::Vector3f GameState::RaycastCamera(const Nz::Vector3f& from, const Nz::Vector3f& to) { auto& physSystem = GetStateData().world->GetSystem(); diff --git a/src/Game/States/GameState.hpp b/src/Game/States/GameState.hpp index e4007ab5..7449b641 100644 --- a/src/Game/States/GameState.hpp +++ b/src/Game/States/GameState.hpp @@ -97,6 +97,7 @@ namespace tsom void BindControlledEntitySignals(); void LayoutWidgets(const Nz::Vector2f& newSize) override; + void RefreshPlayerListWidget(); Nz::Vector3f RaycastCamera(const Nz::Vector3f& from, const Nz::Vector3f& to); void OnTick(Nz::Time elapsedTime, bool lastTick); void SendInputs(); @@ -114,6 +115,7 @@ namespace tsom NazaraSlot(ClientSessionHandler, OnPlayerJoined, m_onPlayerJoined); NazaraSlot(ClientSessionHandler, OnPlayerLeave, m_onPlayerLeave); NazaraSlot(ClientSessionHandler, OnPlayerNameUpdate, m_onPlayerNameUpdate); + NazaraSlot(ClientSessionHandler, OnPlayerListUpdated, m_onPlayerListUpdated); NazaraSlot(Nz::Canvas, OnUnhandledKeyPressed, m_onUnhandledKeyPressed); NazaraSlot(Nz::Canvas, OnUnhandledKeyReleased, m_onUnhandledKeyReleased); NazaraSlot(Nz::Canvas, OnUnhandledMouseButtonPressed, m_mouseButtonReleasedSlot); @@ -160,7 +162,7 @@ namespace tsom std::optional m_consoleExecutor; std::optional m_pilotedShip; - std::shared_ptr m_debugOverlay; + std::shared_ptr m_debugOverlay; std::size_t m_toolIndex; std::vector m_predictedInputRotations; std::vector> m_tools; @@ -187,6 +189,7 @@ namespace tsom Nz::UInt8 m_nextInputIndex; CameraMode m_cameraMode; HealthOxygen m_healthOxygen; + Nz::LabelWidget* m_playerListWidget; Nz::SimpleLabelWidget* m_interactionLabel; BlockSelectionBar* m_blockSelectionBar; Chatbox* m_chatBox;