diff --git a/Makefile.am b/Makefile.am index 6f59a34..37985b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,8 @@ SilverMUDServer_SOURCES = \ source/server/player-data.c \ source/server/connections.c \ source/server/scheme-integration.c \ - source/server/main.c + source/server/output-queue.c \ + source/server/main.c SilverMUDClient_SOURCES = \ source/messages.c \ diff --git a/source/server/main.c b/source/server/main.c index a7bab43..23fac61 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -19,6 +19,7 @@ #include #include +#include "output-queue.h" #include "player-data.h" #include "connections.h" #include "../messages.h" @@ -104,7 +105,7 @@ int main (int argc, char ** argv) // Create some structures needed to store global state: struct PlayerList * globalPlayerList = createPlayerList(); - + struct OutputQueue * globalOutputQueue = createOutputQueue(); // Start a REPL thread: //pthread_t schemeREPLThread; //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); @@ -213,23 +214,10 @@ int main (int argc, char ** argv) continue; } // ONLY FOR DEMO - - struct ServerToClientMessage outputMessage; - - // Copy the message to the output format: - outputMessage.type = LOCAL_CHAT; - - strncpy(outputMessage.name, connection->player->name, 64); - strncpy(outputMessage.content, message.content, MESSAGE_CONTENT_LENGTH); - - // Echo the message into all other clients: (Temporary) - struct ClientConnectionNode * currentClient = clientConnections.head; - while (currentClient != NULL) - { - gnutls_record_send(*currentClient->connection->tlsSession, &outputMessage, - sizeof(struct ServerToClientMessage)); - currentClient = currentClient->next; - } +] + pushOutputMessage(globalOutputQueue, false, globalPlayerList, LOCAL_CHAT, + connection->player->name, message.content, + MESSAGE_NAME_LENGTH, MESSAGE_CONTENT_LENGTH); } } else @@ -241,7 +229,29 @@ int main (int argc, char ** argv) close(events[index].data.fd); removeConnectionByFileDescriptor(&clientConnections, events[index].data.fd); } + } + } + + // Output the queue: + struct OutputMessage * currentMessage = NULL; + while (globalOutputQueue->count != 0) + { + currentMessage = popOutputMessage(globalOutputQueue); + struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; + + while (currentPlayerNode != NULL) + { + gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, + currentMessage->message, sizeof(struct ServerToClientMessage)); + currentPlayerNode = currentPlayerNode->next; } + + if (currentMessage->deallocatePlayerList == true) + { + deallocatePlayerList(¤tMessage->recepients); + } + + deallocateOutputMessage(¤tMessage); } } diff --git a/source/server/output-queue.c b/source/server/output-queue.c new file mode 100644 index 0000000..906638f --- /dev/null +++ b/source/server/output-queue.c @@ -0,0 +1,129 @@ +// ========================================= +// | SilverMUD Server - output-queue.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include "output-queue.h" + +struct OutputQueue * const createOutputQueue() +{ + // Allocate a new queue: + struct OutputQueue * const newQueue = calloc(1, sizeof(struct OutputQueue)); + + // Initialize it: + pthread_mutex_init(&newQueue->mutex, NULL); + newQueue->count = 0; + newQueue->front = NULL; + newQueue->back = NULL; + + // Return the new queue: + return newQueue; +} + +size_t pushOutputMessage(struct OutputQueue * const queue, + const bool deallocatePlayerList, + struct PlayerList * const recepients, + const enum MessageTypes type, + const char const * name, const char const * content, + const size_t nameLength, const size_t contentLength) +{ + // Allocate the appropriate memory for the queued message: + struct OutputMessage * newMessage = calloc(1, sizeof(struct OutputMessage)); + newMessage->message = calloc(1, sizeof(struct ServerToClientMessage)); + + // Copy in the appropriate values to the ServerToClientMessage: + newMessage->message->type = type; + + strncpy(newMessage->message->name, name, (nameLength < MESSAGE_NAME_LENGTH) ? + nameLength : MESSAGE_NAME_LENGTH); + newMessage->message->name[MESSAGE_NAME_LENGTH - 1] = '\0'; + + strncpy(newMessage->message->content, content, (contentLength < MESSAGE_CONTENT_LENGTH) ? + contentLength : MESSAGE_CONTENT_LENGTH); + newMessage->message->content[MESSAGE_CONTENT_LENGTH - 1] = '\0'; + + // Copy in the appropriate values to the OutputMessage: + newMessage->deallocatePlayerList = deallocatePlayerList; + newMessage->recepients = recepients; + + // Entering critical section - Lock the queue: + pthread_mutex_lock(&queue->mutex); + + // Add it to the queue: + if (queue->back != NULL) + { + queue->back->next = newMessage; + queue->back = newMessage; + } + + if (queue->front == NULL) + { + queue->front = newMessage; + queue->back = newMessage; + } + + queue->count++; + + // Leaving critical section - Unlock the queue: + pthread_mutex_unlock(&queue->mutex); + + return queue->count; +} + +struct OutputMessage * popOutputMessage(struct OutputQueue * queue) +{ + if (queue->count == 0) + { + return NULL; + } + + // Entering the critical section - Lock the queue: + pthread_mutex_lock(&queue->mutex); + + struct OutputMessage * message = queue->front; + queue->count--; + + if(queue->count == 0) + { + queue->front = NULL; + queue->back = NULL; + } + + else + { + queue->front = queue->front->next; + } + + // Leaving the critical section - Unlock the queue: + pthread_mutex_unlock(&queue->mutex); + + return message; +} + +void deallocateOutputMessage(struct OutputMessage ** message) +{ + // Free and set the pointer to NULL: + free(*message); + message = NULL; +} + +// ==================================================== +// | End of output-queue.c, copyright notice follows. | +// ==================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + diff --git a/source/server/output-queue.h b/source/server/output-queue.h new file mode 100644 index 0000000..d2b1f9a --- /dev/null +++ b/source/server/output-queue.h @@ -0,0 +1,61 @@ +// ========================================= +// | SilverMUD Server - output-queue.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef OUTPUT_QUEUE_H +#define OUTPUT_QUEUE_H +#include +#include + +#include "../messages.h" + +struct OutputMessage +{ + // Allows for easy reuse of existing player lists, such as the global list + // or an area's playerlist: + bool deallocatePlayerList; + + struct OutputMessage * next; + struct PlayerList * recepients; + struct ServerToClientMessage * message; +}; + +struct OutputQueue +{ + pthread_mutex_t mutex; + size_t count; + struct OutputMessage * front, * back; +}; + +struct OutputQueue * const createOutputQueue(); + +size_t pushOutputMessage(struct OutputQueue * const queue, + const bool deallocatePlayerList, + struct PlayerList * const recepients, + const enum MessageTypes type, + const char const * name, const char const * content, + const size_t nameLength, const size_t contentLength); + +struct OutputMessage * popOutputMessage(); + +void deallocateOutputMessage(struct OutputMessage ** message); + +#endif +// ==================================================== +// | End of output-queue.h, copyright notice follows. | +// ==================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +