Files
Tower-Defense/src/server/Server.cpp
2023-08-13 13:44:05 +02:00

177 lines
3.9 KiB
C++

#include "server/Server.h"
#include "td/protocol/PacketFactory.h"
#include "td/protocol/packets/DisconnectPacket.h"
#include "td/protocol/packets/ServerTpsPacket.h"
#include "td/protocol/packets/PlayerLeavePacket.h"
#include "td/misc/Format.h"
namespace td {
namespace server {
Server::Server() : m_ServerRunning(false) {
}
Server::~Server() {
if (m_Thread.joinable())
m_Thread.join();
}
bool Server::LoadMap(const std::string& worldFilePath) {
return m_Game.GetWorld()->LoadMapFromFile(worldFilePath);
}
bool Server::IsMapLoaded() {
return !m_Game.GetWorld()->GetTilePalette().empty();
}
void Server::ServerLoop() {
std::uint64_t lastTime = td::utils::GetTime();
while (m_ServerRunning) {
std::uint64_t time = td::utils::GetTime();
std::uint64_t delta = time - lastTime;
if (delta >= SERVER_TICK) {
Tick(delta);
lastTime = td::utils::GetTime();
m_TickCounter.SetMSPT(lastTime - time);
std::uint64_t sleepTime = SERVER_TICK - (delta - SERVER_TICK);
std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
}
}
Clean();
}
void Server::StartThread() {
m_Thread = std::thread([this]() {
ServerLoop();
});
}
void Server::Close() {
StopThread();
}
void Server::StopThread() {
m_ServerRunning = false;
}
bool Server::Start(std::uint16_t port, bool blocking) {
if (!IsMapLoaded()) {
utils::LOGE(utils::format("No map loaded !"));
return false;
}
if (!m_Listener.Listen(port, 10)) {
utils::LOGE(utils::format("Failed to bind port %u !", port));
return false;
}
if (!m_Listener.SetBlocking(false)) {
utils::LOGE("Failed to block server socket !");
return false;
}
utils::LOG(utils::format("Server started at port %u !", port));
m_TickCounter.Reset();
m_ServerRunning = true;
if (blocking) {
ServerLoop();
} else {
StartThread();
}
return true;
}
void Server::Clean() {
m_Listener.Close();
m_Listener.Destroy();
m_Connections.clear();
GetPlayers().clear();
utils::LOG("Server successfully stopped !");
}
void Server::Stop() {
if (!m_ServerRunning)
return;
protocol::DisconnectPacket packet("Server closed");
BroadcastPacket(&packet);
StopThread();
}
void Server::Tick(std::uint64_t delta) {
Accept();
UpdateSockets();
m_Lobby.Tick();
m_Game.Tick(delta);
if (m_TickCounter.Update()) {
protocol::ServerTpsPacket packet(m_TickCounter.GetTPS(), m_TickCounter.GetMSPT(), utils::GetTime());
BroadcastPacket(&packet);
}
}
void Server::Accept() {
static std::uint8_t newPlayerID = 0;
network::TCPSocket newSocket;
if (m_Listener.Accept(newSocket)) {
ServerConnexion con(newSocket, newPlayerID);
m_Connections.insert(std::move(ConnexionMap::value_type{ newPlayerID, std::move(con) }));
OnPlayerJoin(newPlayerID);
m_Connections[newPlayerID].SetServer(this);
newPlayerID++;
}
}
void Server::UpdateSockets() {
std::int16_t closedConnexionID = -1;
for (auto& connection : m_Connections) {
ServerConnexion& con = connection.second;
if (con.GetSocketStatus() != network::Socket::Status::Connected) {
closedConnexionID = connection.first;
} else {
con.UpdateSocket();
}
}
if (closedConnexionID != -1) {
RemoveConnexion(closedConnexionID);
}
}
void Server::BroadcastPacket(const protocol::Packet* packet) {
for (auto& connection : m_Connections) {
ServerConnexion& con = connection.second;
con.SendPacket(packet);
}
}
void Server::RemoveConnexion(std::uint8_t connexionID) {
GetPlayers().erase(GetPlayers().find(connexionID));
m_Connections.erase(connexionID);
m_Lobby.OnPlayerLeave(connexionID);
OnPlayerLeave(connexionID);
}
void Server::OnPlayerJoin(std::uint8_t id) {
m_Lobby.OnPlayerJoin(id);
GetPlayers().insert({ id, game::Player{id} });
}
void Server::OnPlayerLeave(std::uint8_t id) {
protocol::PlayerLeavePacket packet(id);
BroadcastPacket(&packet);
if (GetPlayers().empty()) {
utils::LOG("All players left. Stopping server ...");
Stop();
}
}
} // namespace server
} // namespace td