160 lines
5.7 KiB
C
160 lines
5.7 KiB
C
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
|
|
#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;
|
|
|
|
|
|
static void serverPacketSender(char *data, uint32_t length, void *userData);
|
|
|
|
|
|
void serverDisconnectClient(ClientThreadT *client) {
|
|
ENetPeer *peer = (ENetPeer *)client->peer;
|
|
void *status = NULL;
|
|
char buffer[2048] = { 0 };
|
|
|
|
// Stop client processing.
|
|
client->running = 0;
|
|
pthread_join(client->threadHandle, &status);
|
|
// Tell the console.
|
|
enet_address_get_host_ip(&peer->address, buffer, 2047);
|
|
consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer);
|
|
// Hang up.
|
|
enet_peer_reset(peer);
|
|
}
|
|
|
|
|
|
static void serverPacketSender(char *data, uint32_t length, void *userData) {
|
|
ENetPeer *peer = (ENetPeer *)userData;
|
|
ENetPacket *packet = NULL;
|
|
|
|
// Send packet.
|
|
packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(peer, 0, packet);
|
|
}
|
|
|
|
|
|
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 };
|
|
|
|
// Tell the packet code how to send packets.
|
|
packetSenderRegister(serverPacketSender);
|
|
|
|
// 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 };
|
|
ENetPacket *packet = NULL;
|
|
ClientThreadT *client = NULL;
|
|
uint64_t nextIndex = 0;
|
|
size_t i = 0;
|
|
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(event.peer);
|
|
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;
|
|
client->peer = event.peer;
|
|
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);
|
|
// Send banner to client.
|
|
packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE);
|
|
enet_peer_send(event.peer, 0, packet);
|
|
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:
|
|
serverDisconnectClient((ClientThreadT *)event.peer->data);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Anyone still running, be rude. Nuke 'em.
|
|
for (i=0; i<server->peerCount; i++) {
|
|
client = (ClientThreadT *)server->peers[i].data;
|
|
if (client) {
|
|
serverDisconnectClient(client);
|
|
}
|
|
}
|
|
|
|
pthread_exit(NULL);
|
|
}
|