#include #include #include #include #include #include #include #include #include #include "bettola/network/tcpsocket.h" #include "bettola/network/udpsocket.h" #include "bettola/network/net_common.h" #include "bettola/network/message.h" #include "bettola/network/player_input_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::TCPSocket server_socket; /* For TCP connections. */ BettolaLib::Network::UDPSocket udp_socket; /* For UDP traffic. */ /* Create and bind TCP 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: TCP Listening on port %hu...\n", BettolaLib::Network::DEFAULT_PORT); /* Create and bind UDP socket. */ if(!udp_socket.create()) { printf("Bettola Server: Failed to create UDP socket.\n"); return 1; } if(!udp_socket.bind(BettolaLib::Network::DEFAULT_PORT)) { printf("Bettola Server: Failed to bind UDP Socket to port %hu.\n", BettolaLib::Network::DEFAULT_PORT); udp_socket.close(); return 1; } printf("Bettola Server: UDP 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); int udp_flags = fcntl(udp_socket.get_sockfd(), F_GETFL, 0); fcntl(udp_socket.get_sockfd(), F_SETFL, udp_flags | O_NONBLOCK); auto last_time = std::chrono::high_resolution_clock::now(); /* Main server loop. */ while(true) { fd_set read_fds; FD_ZERO(&read_fds); FD_SET(server_socket.get_sockfd(), &read_fds); FD_SET(udp_socket.get_sockfd(), &read_fds); int max_fd = std::max(server_socket.get_sockfd(), udp_socket.get_sockfd()); for(const auto& client : client_sockets) { FD_SET(client->get_sockfd(), &read_fds); if(client->get_sockfd() > max_fd) { max_fd = client->get_sockfd(); } } struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; /* 1ms */ int activity = select(max_fd+1, &read_fds, nullptr, nullptr, &tv); if(activity > 0) { /* Handle new TCP connections. */ if(FD_ISSET(server_socket.get_sockfd(), &read_fds)) { BettolaLib::Network::TCPSocket* 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)); int client_flags = fcntl(client_socket->get_sockfd(), F_GETFL, 0); fcntl(client_socket->get_sockfd(), F_SETFL, client_flags | O_NONBLOCK); client_sockets.push_back(client_socket); } } /* Handle UDP messages. */ if(FD_ISSET(udp_socket.get_sockfd(), &read_fds)) { char buffer[1024]; sockaddr_in from_addr; ssize_t bytes_received; while((bytes_received = udp_socket.recv_from(buffer, sizeof(buffer), from_addr)) > 0) { game.process_udp_message(buffer, bytes_received, from_addr); } } /* Handle TCP messages from clients. */ for(auto it = client_sockets.begin(); it != client_sockets.end();) { BettolaLib::Network::TCPSocket* client = *it; if(FD_ISSET(client->get_sockfd(), &read_fds)) { bool client_disconnected = false; while (true) { BettolaLib::Network::MessageHeader header; ssize_t bytes_received = client->recv(&header, sizeof(header)); if(bytes_received == 0) { client_disconnected = true; break; } if(bytes_received < 0) { break; } } if(client_disconnected) { Player* player = game.get_player_by_socket(client); if(player) game.remove_player(player->get_id()); it = client_sockets.erase(it); delete client; } else { ++it; } } else { ++it; } } } /* Update dynamic game state, like chunk loading for players. */ game.update_player_chunks(); /* Broadcase game state. */ game.broadcast_game_state(udp_socket); /* Sleep for a short time to avoid busy-waiting. */ std::this_thread::sleep_for(std::chrono::milliseconds(10)); } server_socket.close(); /* Shouldn't reach here. */ printf("=== Bettola Server: Shutting Down ===\n"); return 0; }