#include #include #include #include #include #include #include #include "bettola/network/socket.h" #include "bettola/network/net_common.h" #include "bettola/network/message.h" #include "bettola/network/player_pos_message.h" #include "game/game.h" int main(void) { Game game; std::vector client_sockets; signal(SIGPIPE, SIG_IGN); printf("=== Bettola Server: Starting ===\n"); BettolaLib::Network::Socket server_socket; if(!server_socket.create()) { printf("Bettola Server: Failed to create socket.\n"); return 1; } if(!server_socket.bind(BettolaLib::Network::DEFAULT_PORT)) { printf("Bettola Server: Failed to bind socket to port %hu.\n", BettolaLib::Network::DEFAULT_PORT); server_socket.close(); return 1; } if(!server_socket.listen()) { printf("Bettola Server: Failed to listen on socket.\n"); server_socket.close(); return 1; } printf("Bettola Server: Listening on port %hu...\n", BettolaLib::Network::DEFAULT_PORT); /* Set the server socket to non-blocking. */ int flags = fcntl(server_socket.get_sockfd(), F_GETFL, 0); if(flags == -1) { perror("fcntl F_GETFL, failed."); return 1; } fcntl(server_socket.get_sockfd(), F_SETFL, flags | O_NONBLOCK); auto last_time = std::chrono::high_resolution_clock::now(); /* Main server loop. */ while(true) { /* Accept new connections. */ BettolaLib::Network::Socket* client_socket = server_socket.accept(); if(client_socket != nullptr) { Player* new_player = game.add_player(client_socket); printf("Bettola Server: Client connected! Player ID: %u\n", new_player->get_id()); BettolaLib::Network::MessageHeader header; header.type = BettolaLib::Network::MessageType::PlayerId; header.size = sizeof(unsigned int); client_socket->send(&header, sizeof(header)); unsigned int id = new_player->get_id(); client_socket->send(&id, sizeof(id)); /* Set the client socket to non-blocking. */ int client_flags = fcntl(client_socket->get_sockfd(), F_GETFL, 0); if(client_flags == -1) { perror("fcntl F_GETFL failed"); return 1; } fcntl(client_socket->get_sockfd(), F_SETFL, client_flags | O_NONBLOCK); client_sockets.push_back(client_socket); } /* Process messages from clients. */ for(auto it = client_sockets.begin(); it != client_sockets.end();) { BettolaLib::Network::Socket* client = *it; bool client_disconnected = false; while(true) { BettolaLib::Network::MessageHeader header; /* Non-blocking. */ ssize_t bytes_received = client->recv(&header, sizeof(header)); if(bytes_received == 0) { client_disconnected = true; break; } if(bytes_received < 0) { /* No data to read.. */ break; } if(header.type == BettolaLib::Network::MessageType::PlayerPosition) { BettolaLib::Network::PlayerPosMessage msg; bytes_received = client->recv(&msg, sizeof(msg)); if(bytes_received > 0) { Player* player = game.get_player_by_socket(client); if(player) { game.update_player_pos(player->get_id(), msg.x, msg.y); } } } } if(client_disconnected) { Player* player = game.get_player_by_socket(client); if(player) game.remove_player(player->get_id()); it = client_sockets.erase(it); } else { ++it; } } /* Broadcase game state. */ game.broadcast_game_state(); /* Sleep for a short time to avoid busy-waiting. */ std::this_thread::sleep_for(std::chrono::milliseconds(1000/60)); } server_socket.close(); /* Shouldn't reach here. */ printf("=== Bettola Server: Shutting Down ===\n"); return 0; }