2 Commits

Author SHA1 Message Date
86275a38a6 update timer gui
All checks were successful
Linux arm64 / Build (push) Successful in 5m24s
2024-03-26 21:41:50 +01:00
48cf4b350c game states 2024-03-26 21:41:37 +01:00
18 changed files with 229 additions and 23 deletions

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Player.h"
#include "blitz/misc/Time.h"
#include <map>
namespace blitz {
@@ -8,12 +9,22 @@ namespace game {
typedef std::map<PlayerID, Player> PlayerMap;
enum GameState : std::uint8_t {
gsNone = 0,
gsWaiting,
gsPreparing,
gsGame,
gsEnd,
};
class Game {
private:
protected:
PlayerMap m_Players;
GameState m_GameState;
utils::Timer m_GameTimer;
public:
Game() {}
Game() : m_GameState(gsNone) {}
virtual void Tick(std::uint64_t delta) = 0;
@@ -28,8 +39,16 @@ class Game {
return m_Players;
}
GameState GetGameState() const {
return m_GameState;
}
virtual void AddPlayer(PlayerID player, const std::string& name);
virtual void RemovePlayer(PlayerID player);
std::uint64_t GetGameStateRemainingTime() const {
return m_GameTimer.GetTimeRemaining();
}
};

View File

@@ -59,6 +59,15 @@ class Timer {
bool Update(std::uint64_t delta);
void Reset();
// void ResetSoft(); // don't trigger the timer
std::uint64_t GetTimeRemaining() const {
return m_Interval - m_InternalTime;
}
std::uint64_t GetTimeElapsed() const {
return m_InternalTime;
}
void SetInterval(std::uint64_t newInterval) {
m_Interval = newInterval;

View File

@@ -32,6 +32,7 @@ class PacketHandler {
virtual void HandlePacket(const PlayerShootPacket* packet) {}
virtual void HandlePacket(const PlayerStatsPacket* packet) {}
virtual void HandlePacket(const ServerTpsPacket* packet) {}
virtual void HandlePacket(const UpdateGameStatePacket* packet) {}
virtual void HandlePacket(const UpdateHealthPacket* packet) {}
};

View File

@@ -10,4 +10,5 @@
#include "packets/PlayerShootPacket.h"
#include "packets/PlayerStatsPacket.h"
#include "packets/ServerTpsPacket.h"
#include "packets/UpdateGameStatePacket.h"
#include "packets/UpdateHealthPacket.h"

View File

@@ -15,6 +15,7 @@ class PlayerPositionAndRotationPacket;
class PlayerShootPacket;
class PlayerStatsPacket;
class ServerTpsPacket;
class UpdateGameStatePacket;
class UpdateHealthPacket;
} // namespace protocol

View File

@@ -24,12 +24,13 @@ enum class PacketType : std::uint8_t {
// client <-- server
ConnexionInfo, /**< Corresponds to ConnexionInfoPacket */
PlayerJoin, /**< Corresponds to PlayerJoinPacket */
PlayerLeave, /**< Corresponds to PlayerLeavePacket */
PlayerList, /**< Corresponds to PlayerListPacket */
PlayerStats, /**< Corresponds to PlayerStatsPacket */
ServerTps, /**< Corresponds to ServerTpsPacket */
ConnexionInfo, /**< Corresponds to ConnexionInfoPacket */
PlayerJoin, /**< Corresponds to PlayerJoinPacket */
PlayerLeave, /**< Corresponds to PlayerLeavePacket */
PlayerList, /**< Corresponds to PlayerListPacket */
PlayerStats, /**< Corresponds to PlayerStatsPacket */
ServerTps, /**< Corresponds to ServerTpsPacket */
UpdateGameState, /**< Corresponds to UpdateGameStatePacket */
// client <--> server

View File

@@ -0,0 +1,38 @@
#pragma once
#include "blitz/game/Game.h"
#include "blitz/protocol/Protocol.h"
namespace blitz {
namespace protocol {
class UpdateGameStatePacket : public Packet {
private:
game::GameState m_GameState;
std::uint64_t m_TimeRemaining;
public:
UpdateGameStatePacket() {}
UpdateGameStatePacket(game::GameState a_GameState, std::uint64_t a_TimeRemaining = 0) :
m_GameState(a_GameState), m_TimeRemaining(a_TimeRemaining) {}
virtual ~UpdateGameStatePacket() {}
virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
game::GameState GetGameState() const {
return m_GameState;
}
std::uint64_t GetTimeRemaining() const {
return m_TimeRemaining;
}
virtual PacketType GetType() const {
return PacketType::UpdateGameState;
}
};
} // namespace protocol
} // namespace blitz

View File

@@ -26,6 +26,7 @@ class ClientGame : public game::Game, public protocol::PacketHandler {
virtual void HandlePacket(const protocol::PlayerShootPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateHealthPacket* packet) override;
virtual void HandlePacket(const protocol::PlayerStatsPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateGameStatePacket* packet) override;
private:
void RegisterHandlers();

View File

@@ -10,15 +10,16 @@ class Client;
namespace gui {
class Hud : public GuiWidget {
private:
/* data */
void Draw(const char* title, bool* p_open);
unsigned int m_GunTexture;
unsigned int m_JP;
unsigned int m_JPTexture;
public:
Hud(GuiWidget* parent, Client* client);
virtual void Render() override;
private:
void Draw(const char* title, bool* p_open);
void RenderTime();
};
} // namespace gui

View File

@@ -57,6 +57,7 @@ class ServerConnexion : public network::Connexion {
void InitConnection();
void InitPlayerChatColor();
void SendPlayers();
void SendGameState();
};
} // namespace server

View File

@@ -1,7 +1,6 @@
#pragma once
#include "blitz/game/Game.h"
#include "blitz/misc/Time.h"
namespace blitz {
namespace server {
@@ -24,11 +23,16 @@ class ServerGame : public game::Game {
void Tick(std::uint64_t delta) override;
void SetGameState(game::GameState gameState, std::uint64_t duration);
private:
void SendPlayerPositions();
void DamagePlayer(game::Player& player, game::Player& shooter);
void UpdateHP(game::Player& player, float newHP);
void UpdatePlayerStats();
void StartGame(); // when at least 2 players joined
void CancelGame(); // when not enough players are left
void ResetPlayerStats();
};
} // namespace server

View File

@@ -21,6 +21,7 @@ static std::array<PacketPtr, static_cast<std::size_t>(PacketType::PACKET_COUNT)>
std::make_unique<PlayerListPacket>(),
std::make_unique<PlayerStatsPacket>(),
std::make_unique<ServerTpsPacket>(),
std::make_unique<UpdateGameStatePacket>(),
std::make_unique<KeepAlivePacket>(),
std::make_unique<DisconnectPacket>(),
std::make_unique<ChatPacket>(),

View File

@@ -27,6 +27,7 @@ REGISTER_DISPATCH_CLASS(PlayerPositionAndRotationPacket)
REGISTER_DISPATCH_CLASS(PlayerShootPacket);
REGISTER_DISPATCH_CLASS(UpdateHealthPacket);
REGISTER_DISPATCH_CLASS(PlayerStatsPacket);
REGISTER_DISPATCH_CLASS(UpdateGameStatePacket);
} // namespace protocol
} // namespace blitz

View File

@@ -0,0 +1,19 @@
#include "blitz/protocol/packets/UpdateGameStatePacket.h"
namespace blitz {
namespace protocol {
DataBuffer UpdateGameStatePacket::Serialize(bool packetID) const {
DataBuffer data;
WritePacketID(data, packetID);
data << m_GameState << m_TimeRemaining;
return data;
}
void UpdateGameStatePacket::Deserialize(DataBuffer& data) {
data >> m_GameState >> m_TimeRemaining;
}
} // namespace protocol
} // namespace blitz

View File

@@ -9,6 +9,7 @@
#include "blitz/protocol/packets/PlayerPositionAndRotationPacket.h"
#include "blitz/protocol/packets/PlayerShootPacket.h"
#include "blitz/protocol/packets/PlayerStatsPacket.h"
#include "blitz/protocol/packets/UpdateGameStatePacket.h"
#include "blitz/protocol/packets/UpdateHealthPacket.h"
#include "client/Client.h"
@@ -31,6 +32,7 @@ void ClientGame::RegisterHandlers() {
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerPositionAndRotation, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerShoot, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerStats, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateGameState, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateHealth, this);
}
@@ -69,6 +71,11 @@ void ClientGame::HandlePacket(const protocol::UpdateHealthPacket* packet) {
}
}
void ClientGame::HandlePacket(const protocol::UpdateGameStatePacket* packet) {
m_GameState = packet->GetGameState();
m_GameTimer.SetInterval(packet->GetTimeRemaining());
m_GameTimer.Reset();
}
void ClientGame::HandlePacket(const protocol::PlayerShootPacket* packet) {
m_Client->NotifyListeners(
@@ -93,6 +100,11 @@ void ClientGame::Tick(std::uint64_t delta) {
for (auto& [playerId, player] : GetPlayers()) {
player.SetPosition(player.GetPosition() + player.GetVelocity() * (static_cast<float>(delta) / 100.0f));
}
if (m_GameTimer.GetInterval() > 0) {
if (m_GameTimer.Update(delta)) {
m_GameTimer.SetInterval(0); // disables the timer so it does not loop
}
}
}
} // namespace client

View File

@@ -11,7 +11,7 @@ namespace gui {
Hud::Hud(GuiWidget* parent, Client* client) : GuiWidget(parent, client) {
m_GunTexture = TextureLoader::LoadGLTexture("fingergun.png");
m_JP = TextureLoader::LoadGLTexture("jp.png");
m_JPTexture = TextureLoader::LoadGLTexture("jp.png");
}
void Hud::Draw(const char* title, bool* p_open) {
@@ -20,7 +20,6 @@ void Hud::Draw(const char* title, bool* p_open) {
ImGui::Begin(title, nullptr, GetWindowFullScreenFlags() | ImGuiWindowFlags_NoInputs);
auto displaySize = ImGui::GetIO().DisplaySize;
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
const static ImVec2 buttonSize = {300, 60};
const static ImVec2 fingergunSize = {256, 134.5};
@@ -34,17 +33,12 @@ void Hud::Draw(const char* title, bool* p_open) {
displaySize.x - fingergunSize.x - paddingHeight, displaySize.y - fingergunSize.y + 1.0f / 2.5f * paddingHeight};
ImVec2 spacing = ImGui::GetStyle().ItemInnerSpacing;
const float timetextWidth = ImGui::CalcTextSize("03 : 00").x;
const float timetextHeight = ImGui::CalcTextSize("03 : 00").y;
ImGui::SetCursorPosX(center.x - timetextWidth / 2);
ImGui::SetCursorPosY(timetextHeight / 2);
ImGui::Text("03 : 00");
RenderTime();
ImGui::SetCursorPosX(3 * paddingHeight);
ImGui::SetCursorPosY(pvBarPos.y - 2 * paddingHeight);
ImGui::BeginGroup();
ImGui::Image(reinterpret_cast<ImTextureID>(m_JP), jpSize);
ImGui::Image(reinterpret_cast<ImTextureID>(m_JPTexture), jpSize);
ImGui::SameLine(0.0f, paddingHeight);
@@ -78,6 +72,34 @@ void Hud::Draw(const char* title, bool* p_open) {
ImGui::End();
}
void Hud::RenderTime() {
std::string timeFormated;
std::uint64_t timeRemaining = m_Client->GetGame()->GetGameStateRemainingTime();
if (timeRemaining == 0) {
timeFormated = "Waiting for players ...";
} else {
timeRemaining += 1000;
int seconds = timeRemaining / 1000 % 60;
int minutes = timeRemaining / 1000 / 60;
timeFormated = (minutes < 10 ? "0" + std::to_string(minutes) : std::to_string(minutes)) + " : " +
(seconds < 10 ? "0" + std::to_string(seconds) : std::to_string(seconds));
}
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
const float timetextWidth = ImGui::CalcTextSize(timeFormated.c_str()).x;
const float timetextHeight = ImGui::CalcTextSize(timeFormated.c_str()).y;
ImGui::SetCursorPosX(center.x - timetextWidth / 2);
ImGui::SetCursorPosY(timetextHeight / 2);
ImGui::Text("%s", timeFormated.c_str());
}
void Hud::Render() {
if (!m_Client->IsConnected())
return;

View File

@@ -16,6 +16,7 @@
#include "blitz/protocol/packets/PlayerLoginPacket.h"
#include "blitz/protocol/packets/PlayerPositionAndRotationPacket.h"
#include "blitz/protocol/packets/PlayerShootPacket.h"
#include "blitz/protocol/packets/UpdateGameStatePacket.h"
#include "client/gui/ColorFulText.h"
#include "server/Server.h"
#include <unordered_map>
@@ -94,6 +95,7 @@ void ServerConnexion::InitPlayerChatColor() {
void ServerConnexion::HandlePacket(const protocol::PlayerLoginPacket* packet) {
SendPlayers();
SendGameState();
m_Server->GetGame().AddPlayer(m_ID, packet->GetPlayerName());
@@ -143,6 +145,11 @@ void ServerConnexion::Start() {
SendKeepAlive();
}
void ServerConnexion::SendGameState() {
protocol::UpdateGameStatePacket packet(m_Server->GetGame().GetGameState(), m_Server->GetGame().GetGameStateRemainingTime());
SendPacket(&packet);
}
void ServerConnexion::SendPlayers() {
protocol::PlayerList list;

View File

@@ -8,6 +8,7 @@
#include "blitz/protocol/packets/PlayerJoinPacket.h"
#include "blitz/protocol/packets/PlayerPositionAndRotationPacket.h"
#include "blitz/protocol/packets/PlayerStatsPacket.h"
#include "blitz/protocol/packets/UpdateGameStatePacket.h"
#include "blitz/protocol/packets/UpdateHealthPacket.h"
#include "server/Server.h"
#include <cmath>
@@ -15,14 +16,52 @@
namespace blitz {
namespace server {
ServerGame::ServerGame(Server* server) : m_Server(server), m_PositionTimer(SERVER_TPS) {}
static const std::uint64_t PREP_DURATION = 1000 * 10;
static const std::uint64_t GAME_DURATION = 1000 * 60 * 3;
static const std::uint64_t END_DURATION = 1000 * 30;
ServerGame::ServerGame(Server* server) : m_Server(server), m_PositionTimer(SERVER_TPS) {
CancelGame();
}
ServerGame::~ServerGame() {}
void ServerGame::StartGame() {
SetGameState(game::gsPreparing, PREP_DURATION);
m_Server->BroadcastChatMessage(protocol::ChatPacket::GetTextColor(protocol::AQUA) + "La partie commence dans 10s !");
}
void ServerGame::CancelGame() {
SetGameState(game::gsWaiting, 0);
}
void ServerGame::Tick(std::uint64_t delta) {
if (m_PositionTimer.Update(delta)) {
SendPlayerPositions();
}
if (m_GameState != game::gsWaiting && m_GameTimer.Update(delta)) {
switch (m_GameState) {
case game::gsPreparing:
SetGameState(game::gsGame, GAME_DURATION);
break;
case game::gsGame:
SetGameState(game::gsEnd, END_DURATION);
break;
case game::gsEnd: {
if (m_Players.size() > 1) {
SetGameState(game::gsGame, GAME_DURATION);
} else {
SetGameState(game::gsWaiting, 0);
}
break;
}
default:
break;
}
}
}
void ServerGame::SendPlayerPositions() {
@@ -70,10 +109,18 @@ void ServerGame::AddPlayer(game::PlayerID player, const std::string& name) {
utils::LOG("[Server] " + joinMessage);
m_Server->BroadcastChatMessage(protocol::ChatPacket::GetTextColor(protocol::YELLOW) + joinMessage);
if (m_GameState == game::gsWaiting && m_Players.size() > 1) {
StartGame();
}
}
void ServerGame::RemovePlayer(game::PlayerID player) {
Game::RemovePlayer(player);
if (m_GameState == game::gsGame && m_Players.size() <= 1) {
CancelGame();
}
}
void ServerGame::DamagePlayer(game::Player& player, game::Player& shooter) {
@@ -110,5 +157,25 @@ void ServerGame::UpdatePlayerStats() {
}
}
void ServerGame::ResetPlayerStats() {
for (auto& [playerId, player] : GetPlayers()) {
player.GetStats() = {};
}
}
void ServerGame::SetGameState(game::GameState gameState, std::uint64_t duration) {
m_GameState = gameState;
m_GameTimer.SetInterval(duration);
m_GameTimer.Reset();
if (gameState == game::gsGame) {
ResetPlayerStats();
UpdatePlayerStats();
}
protocol::UpdateGameStatePacket packet(gameState, duration);
m_Server->BroadcastPacket(&packet);
}
} // namespace server
} // namespace blitz