/* * Kangaroo Punch MultiPlayer Game Server Mark II * Copyright (C) 2020-2021 Scott Duensing * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "server.h" #include "console.h" #include "network.h" #include "client.h" #include "packet.h" ENetHost *_server = NULL; pthread_t _serverThreadHandle = { 0 }; void *_serverThreadStatus = NULL; void serverShutdown(void) { pthread_join(_serverThreadHandle, &_serverThreadStatus); enet_host_destroy(_server); } void serverStartup(uint32_t port, uint32_t maxClients) { ENetAddress address = { 0 }; pthread_attr_t serverThreadAttributes = { 0 }; // Set up listening socket. address.host = ENET_HOST_ANY; address.port = port; _server = enet_host_create(&address, /* the address to bind the server host to */ maxClients, /* allow up to XX clients and/or outgoing connections */ 1, /* allow up to 2 channels to be used, 0 and 1 */ 0, /* assume any amount of incoming bandwidth */ 0 /* assume any amount of outgoing bandwidth */ ); if (_server == NULL) utilDie("Unable to open server listening port.\n"); // Start server thread. if (pthread_attr_init(&serverThreadAttributes) != 0) utilDie("Unable to create server thread attributes.\n"); pthread_attr_setdetachstate(&serverThreadAttributes, PTHREAD_CREATE_JOINABLE); if (pthread_create(&_serverThreadHandle, &serverThreadAttributes, serverThread, (void *)_server) != 0) utilDie("Unable to start server thread.\n"); } void *serverThread(void *data) { ENetHost *server = (ENetHost *)data; ENetEvent event = { 0 }; ClientThreadT *client = NULL; uint64_t nextIndex = 0; size_t i = 0; void *status = NULL; char buffer[2048] = { 0 }; while (_running) { while (enet_host_service(server, &event, 1) > 0) { switch (event.type) { case ENET_EVENT_TYPE_NONE: break; case ENET_EVENT_TYPE_CONNECT: // Create new client. NEW(ClientThreadT, client); if (!client) utilDie("Unable to allocate new client.\n"); client->packetThreadData = packetThreadDataCreate(); if (!client->packetThreadData) { utilDie("Unable to allocate packetThreadData for new client.\n"); break; // This break silences an invalid clang warning. } client->threadIndex = nextIndex++; client->running = 1; client->packetQueue = NULL; memset(&client->packetQueueMutex, 1, sizeof(pthread_mutex_t)); pthread_mutex_init(&client->packetQueueMutex, NULL); // Keep our client in the peer data for later. event.peer->data = (void *)client; // Make new thread for this client. if (pthread_attr_init(&client->threadAttributes) != 0) utilDie("Unable to create client thread attributes.\n"); pthread_attr_setdetachstate(&client->threadAttributes, PTHREAD_CREATE_JOINABLE); if (pthread_create(&client->threadHandle, &client->threadAttributes, clientThread, (void *)event.peer) != 0) utilDie("Unable to start client thread.\n"); // Tell the console. enet_address_get_host_ip(&event.peer->address, buffer, 2047); consoleMessageQueue("%ld: [%s] connected.\n", client->threadIndex, buffer); break; case ENET_EVENT_TYPE_RECEIVE: // Send the raw packet to the client thread for processing. clientQueuePacket((ClientThreadT *)event.peer->data, event.packet->data, event.packet->dataLength); enet_packet_destroy(event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: // Get our client. client = (ClientThreadT *)event.peer->data; // Stop client processing. client->running = 0; pthread_join(client->threadHandle, &status); // Tell the console. enet_address_get_host_ip(&event.peer->address, buffer, 2047); consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer); break; } } } // Anyone still running, be rude. Nuke 'em. for (i=0; ipeerCount; i++) { client = (ClientThreadT *)server->peers[i].data; if (client) { // Stop client processing. client->running = 0; pthread_join(client->threadHandle, &status); // Hang up. enet_peer_reset(&server->peers[i]); } } pthread_exit(NULL); }