#include "server.h" Server::Server(LOG_DEST log) { m_log = log; if (log == LOG_DEST::LOGFILE) { m_logfile = std::ofstream("server.log", std::ofstream::out); if (!m_logfile.is_open()) { m_log = LOG_DEST::CONSOLE; // Fallback console. Log("Ouverture fichier log: repli vers console.", true, false); } } } Server::~Server() { if (m_logfile.is_open()) m_logfile.close(); if (m_sock_udp) closesocket(m_sock_udp); if (m_sock_tcp) closesocket(m_sock_tcp); for (const auto& [key, player] : m_players) closesocket(player->getSock()); m_players.clear(); #ifdef _WIN32 WSACleanup(); #endif } int Server::Init() { Log("Initialisation du serveur...", false, false); #ifdef _WIN32 if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) { /* Initialisation de l'environnement reseau (Windows only) */ Log("Initialisation WinSock.", true, true); return 1; } #endif m_sock_udp = socket(AF_INET, SOCK_DGRAM, 0); if (m_sock_udp == INVALID_SOCKET) { /* Creation du socket UDP */ Log("Creation Socket UDP.", true, true); return 2; } m_sock_tcp = socket(AF_INET, SOCK_STREAM, 0); if (m_sock_tcp == INVALID_SOCKET) { /* Creation du socket TCP */ Log("Creation Socket TCP.", true, true); return 3; } /* Creation structure donnes descripteur du socket serveur */ sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(SRV_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(m_sock_udp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket UDP au port */ Log("Association Socket UDP.", true, true); return 4; } if (bind(m_sock_tcp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket TCP au port */ Log("Association Socket TCP.", true, true); return 5; } return 0; } int Server::Ready() { int nbrjoueurs = 0, nbrconn = 0; bool readystart = false; do { Log("Entrez la duree de la partie: ", false, false); std::cin.getline(m_buf.ptr, BUFFER_LENGTH); try { m_game.countdown = std::stoi(m_buf.ptr); } catch(const std::exception& e) { Log(e.what(), true, false); m_game.countdown = 0; } } while (m_game.countdown < 1); do { Log("Entrez le seed de la partie: ", false, false); std::cin.getline(m_buf.ptr, BUFFER_LENGTH); try { m_game.seed = std::stoi(m_buf.ptr); } catch(const std::exception& e) { Log(e.what(), true, false); m_game.seed = 0; } } while (m_game.seed < 1); do { Log("Entrez le nombre de joueurs: ", false, false); std::cin.getline(m_buf.ptr, BUFFER_LENGTH); try { nbrjoueurs = std::stoi(m_buf.ptr); } catch(const std::exception& e) { Log(e.what(), true, false); nbrjoueurs = 0; } if (nbrjoueurs <= 0 || nbrjoueurs > MAX_CONNECTIONS) Log("Nombre de joueurs invalide.", true, false); } while (nbrjoueurs <= 0 || nbrjoueurs > MAX_CONNECTIONS); m_game.gameType = 1; if (listen(m_sock_tcp, MAX_CONNECTIONS) < 0) { Log("Ecoute sur le port TCP.", true, true); return 1; } buildIdList(ID_LIST_SIZE); Log("A l'ecoute sur le port: " + std::to_string(SRV_PORT), false, false); while (!readystart) { sockaddr_in sockad; addrlen_t addrlen = sizeof(sockad); SOCKET sock = accept(m_sock_tcp, (sockaddr*)&sockad, &addrlen); if (sock < 0) Log("Erreur de connexion", true, false); else if (sock > 0) { std::string str = "Nouvelle connexion provenant de: "; str.append(inet_ntop(AF_INET, &sockad.sin_addr, m_buf.ptr, m_buf.len)).append(": ").append(std::to_string(sockad.sin_port)); if (recv(sock, m_buf.ptr, m_buf.len, 0) > 0) { PlayerInfo play; m_buf.len = BUFFER_LENGTH; Packet pck = getPack(&m_buf); if (pck.type != PACKET_TYPE::LOGINF) { Log("Paquet invalide.", true, false); if (pck.type != PACKET_TYPE::ERR) netprot::emptyPack(pck); continue; // Passer au prochain appel si c'est pas un LoginInfo ou un LoginInfo invalide qui rentre. } LoginInfo* log = (LoginInfo*)pck.ptr; log->sid = getUniqueId(); log->tid = 1145389380; // TODO: À changer si on implemente un mode en equipe. Log(str.append(" Nom: ").append(log->name), false, false); str.clear(); Log(str.append(log->name).append(" SID: [").append(std::to_string(log->sid).append("]")), false, false); sendPack(sock, log, &m_buf); play.id = getUniqueId(); strcpy(play.name, log->name); play.tid = log->tid; sendPack(sock, &m_game, &m_buf); std::unique_ptr conn = std::make_unique(sock, sockad, *log, play); for (auto& [key, player] : m_players) { sendPack(player->getSock(), &play, &m_buf); // Envoyer les infos de joueur distant aux joueurs d�j� connect�s sendPack(sock, player->getInfo(), &m_buf); // et envoyer les infos des joueurs distants au nouveau joueur. } m_players[log->sid] = std::move(conn); delete log; if (++nbrconn >= nbrjoueurs) readystart = true; } } } return 0; } void Server::Run() { Input in; sockaddr_in sockad; addrlen_t socklen = sizeof(sockad); Log("Debut de la partie...", false, false); m_world = std::make_unique(); m_world->SetSeed(m_game.seed); m_world->GetChunks().Reset(nullptr); m_world->BuildWorld(); for (auto& [key, conn] : m_players) { // Creation des instances de joueurs et premier sync. conn->player = std::make_unique(Vector3f(8.5f, CHUNK_SIZE_Y + 1.8f, 8.5f)); Sync sync; sync.position = conn->player->GetPosition(); sync.hp = conn->player->GetHP(); sync.sid = key; sync.ammo = 0; sync.timestamp = 0; sync.timer = m_game.countdown; sendPack(conn->getSock(), &sync, &m_buf); } while (true) { if (recvfrom(m_sock_udp, m_buf.ptr, m_buf.len, 0, (sockaddr*)&sockad, &socklen) > 0) { Packet pck = getPack(&m_buf); switch (pck.type) { using enum netprot::PACKET_TYPE; case ERR: std::puts("ERROR!"); break; case INPUT: std::puts("INPUT!"); break; case OUTPUT: std::puts("OUTPUT!"); break; case SYNC: std::puts("SYNC!"); break; case TEAMINF: std::puts("TEAMINF!"); break; case SELFINF: std::puts("SELFINF!"); break; case PLAYINF: std::puts("PLAYINF!"); break; case LOGINF: std::puts("LOGINF!"); break; case CHUNKMOD: std::puts("CHUNKMOD!"); break; case PLAYERMOD: std::puts("PLAYERMOD!"); break; case PICKUPMOD: std::puts("PICKUPMOD!"); break; case GAMEINFO: std::puts("GAMEINFO!"); break; case ENDINFO: std::puts("ENDINFO!"); break; case CHAT: std::puts("CHAT!"); break; case ERRLOG: std::puts("ERRLOG!"); break; case LAST_PACK: [[falltrough]]; default: std::puts("wtf?!"); break; } netprot::emptyPack(pck); } } } inline std::string Server::LogTimestamp() { time_t rawtime; tm timeinfo; char buffer[50]; time(&rawtime); #ifdef _WIN32 localtime_s(&timeinfo, &rawtime); #else localtime_r(&rawtime, &timeinfo); #endif strftime(buffer, sizeof(buffer), "%d-%m-%Y %H:%M:%S", &timeinfo); std::string str(buffer); return "[" + str + "] "; } void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) { switch (m_log) { using enum LOG_DEST; // C++20! case LOGFILE: m_logfile << LogTimestamp() << (is_fatal ? "FATAL " : "") << (is_error ? "ERROR " : "") << str << std::endl; break; case CONSOLE: [[fallthrough]]; // Pour dire que c'est voulu que ça traverse vers le case en dessous (C++17!) default: std::cout << LogTimestamp() << (is_fatal ? "FATAL " : "") << (is_error ? "ERROR " : "") << str << std::endl; break; } if (is_fatal) { if (m_logfile.is_open()) m_logfile.close(); if (m_sock_udp) closesocket(m_sock_udp); if (m_sock_tcp) closesocket(m_sock_tcp); for (const auto& [key, player] : m_players) { closesocket(player->getSock()); } m_players.clear(); #ifdef _WIN32 WSACleanup(); #endif exit(-1); } } void Server::buildIdList(size_t size) { std::set lst; srand(time(NULL)); do lst.insert(((uint64_t)rand() << 32 | rand())); while (lst.size() < size); m_ids = std::vector(lst.begin(), lst.end()); } uint64_t Server::getUniqueId() { uint64_t id = m_ids.back(); m_ids.pop_back(); return id; }