diff --git a/Makefile b/Makefile index 90805f5..7abf4ec 100644 --- a/Makefile +++ b/Makefile @@ -25,5 +25,5 @@ clean: all: clean SilverMUDClient SilverMUDServer all: CFLAGS += -Wall -Wextra -Ofast -debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og +debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug debug: clean SilverMUDClientDebug SilverMUDServerDebug diff --git a/src/gamelogic.c b/src/gamelogic.c index 58b2d82..eaa0d91 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -136,6 +136,8 @@ int queueMessagedCommand(commandQueue * queue, inputMessage * messageToQueue) // Check that we're not overflowing the queue: if ((queue->currentLength + 1) > MAXQUEUELENGTH) { + // Free the new command, it's getting dumped: + free(newCommand); // Unlock the queue: queue->lock = false; return -1; diff --git a/src/gamelogic.h b/src/gamelogic.h index e916d1b..75eca38 100644 --- a/src/gamelogic.h +++ b/src/gamelogic.h @@ -1,5 +1,4 @@ -// gamelogic.h: Header file contatning function prototypes and datastructures -// for dealing with the game's logic. +// gamelogic.h: Function prototypes and data-structures for dealing with game logic. // Barry Kane, 2022. #ifndef GAMELOGIC_H #define GAMELOGIC_H @@ -12,7 +11,7 @@ // -=[ Main Game Loop ]=-: // ======================= -// A datastructure containing the needed parameters for a main game loop: +// A data-structure containing the needed parameters for a main game loop: typedef struct gameLogicParameters { int * playerCount; diff --git a/src/linkedlist.h b/src/linkedlist.h index 2f15653..b1cc8c3 100644 --- a/src/linkedlist.h +++ b/src/linkedlist.h @@ -47,9 +47,9 @@ typedef struct list listNode * tail; } list; -// ================== -// -=[ Functions ]=-: -// ================== +// ======================== +// -=[ Functions ]=-: +// ======================== // Allocates and instantiates a list of the specified type: list * createList(listDataType type); diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..df83496 --- /dev/null +++ b/src/queue.c @@ -0,0 +1,206 @@ +// queue.c: Implements the queue data type and associated functions for SilverMUD. +// Barry Kane, 2022 +#include "queue.h" + +// Allocates and instantiates a queue: +queue * createQueue(void) +{ + // Allocate the memory for the queue: + queue * newQueue = malloc(sizeof(queue)); + + // Instantiate the variables in the data-structure: + newQueue->itemCount = 0; + newQueue->front = NULL; + newQueue->back = NULL; + + // Return the pointer to the new queue: + return newQueue; +} + +// Destroys a queue and all of it's members: +void destroyQueue(queue ** queue) +{ + // Pop everything off of the queue: + while ((*queue)->itemCount > 0) + { + popQueue(*queue); + } + + // Deallocate the queue: + free(*queue); +} + +// Returns the data at the front of the given queue: +queueNode * peekQueue(queue * queue) +{ + return queue->front; +} + +// Removes the current data from the front of the queue: +void popQueue(queue * queue) +{ + // Check if the queue is locked, and wait: + while (queue->lock); + + // Lock the queue: + queue->lock = true; + + // Check there is actually anything to remove: + if (queue->itemCount == 0) + { + queue->lock = false; + return; + } + + // Handle the special case of being the last item in the queue: + else if (queue->itemCount == 1) + { + // Deallocate the correct data: + switch (queue->front->type) + { + case EVENT: + { + // TODO: Implement events. + } + case COMMAND: + { + free(queue->front->data.command->command); + free(queue->front->data.command->arguments); + free(queue->front->data.command); + break; + } + case INPUT_MESSAGE: + { + free(queue->front->data.inputMessage->content); + free(queue->front->data.inputMessage); + break; + } + case OUTPUT_MESSAGE: + { + free(queue->front->data.outputMessage->content); + free(queue->front->data.outputMessage); + break; + } + } + + // Deallocate the node: + free(queue->front); + + // Set the correct variables for the queue: + queue->front = NULL; + queue->back = NULL; + queue->itemCount = 0; + + // Unlock the queue: + queue->lock = false; + + return; + } + + // Remove the current front of the queue: + else + { + // Deallocate the correct data: + switch (queue->front->type) + { + case EVENT: + { + // TODO: Implement events. + break; + } + case COMMAND: + { + free(queue->front->data.command->command); + free(queue->front->data.command->arguments); + free(queue->front->data.command); + break; + } + case INPUT_MESSAGE: + { + free(queue->front->data.inputMessage->content); + free(queue->front->data.inputMessage); + break; + } + case OUTPUT_MESSAGE: + { + free(queue->front->data.outputMessage->content); + free(queue->front->data.outputMessage); + break; + } + } + + // Save a pointer to the current node so we don't leak it: + queueNode * nodeToDelete = queue->front; + + // Advance the queue: + queue->front = queue->front->next; + queue->itemCount--; + + // Deallocate the old node: + free(nodeToDelete); + + // Unlock the queue: + queue->lock = false; + + return; + } +} + +// Adds data to the back of a queue: +void pushQueue(queue * queue, void * data, queueDataType type) +{ + // Create a new node: + queueNode * newNode = malloc(sizeof(queueNode)); + + // Copy the data into the correct slot for the type: + switch (type) + { + case EVENT: + { + // TODO: Implement events. + break; + } + case COMMAND: + { + newNode->data.command = data; + break; + } + case INPUT_MESSAGE: + { + newNode->data.inputMessage = data; + break; + } + case OUTPUT_MESSAGE: + { + newNode->data.outputMessage = data; + break; + } + + } + + // Check if the queue is locked: + while (queue->lock); + + // Lock the queue: + queue->lock = true; + + // Set the correct pointers: + newNode->next = NULL; + + if (queue->itemCount == 0) + { + queue->front = newNode; + queue->back = newNode; + } + else + { + queue->back->next = newNode; + queue->back = newNode; + } + + // Increase the queue item count: + queue->itemCount++; + + // Unlock the queue: + queue->lock = false; +} diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000..6bc6afe --- /dev/null +++ b/src/queue.h @@ -0,0 +1,62 @@ +// queue.h: Defines the queue data type and associated function prototypes for SilverMUD. +// Barry Kane, 2022 +#ifndef QUEUE_H +#define QUEUE_H +#include "gamelogic.h" +#include "inputoutput.h" + +// ======================== +// -=[ Data Structures ]=-: +// ======================== + +typedef enum queueDataType +{ + EVENT, + COMMAND, + INPUT_MESSAGE, + OUTPUT_MESSAGE +} queueDataType; + +typedef union queueData +{ + outputMessage * outputMessage; + inputMessage * inputMessage; + commandEvent * command; +} queueData; + +typedef struct queueNode queueNode; +typedef struct queueNode +{ + queueDataType type; + queueData data; + queueNode * next; +} queueNode; + +typedef struct queue +{ + bool lock; + size_t itemCount; + queueNode * front; + queueNode * back; +} queue; + +// ======================== +// -=[ Functions ]=-: +// ======================== + +// Allocates and instantiates a queue: +queue * createQueue(void); + +// Destroys a queue and all of it's members: +void destroyQueue(queue ** queue); + +// Returns the node at the front of the given queue: +queueNode * peekQueue(queue * queue); + +// Removes the current node from the front of the queue: +void popQueue(queue * queue); + +// Adds data to the back of a queue: +void pushQueue(queue * queue, void * data, queueDataType type); + +#endif diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index eddec7d..92e54f6 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -29,7 +29,7 @@ typedef struct sockaddr sockaddr; void sigintHandler(int signal) { printf("Caught signal %d.\n", signal); - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); } int main(int argc, char ** argv) diff --git a/src/texteffects.h b/src/texteffects.h index 060c99c..0d0305b 100644 --- a/src/texteffects.h +++ b/src/texteffects.h @@ -33,4 +33,5 @@ char * logostring = " # # ### ##### ##### ### # # # # #### ### /\n"; void wrapString(char * stringToWrap, int stringLength, int screenWidth); + #endif diff --git a/tests/queue-test.c b/tests/queue-test.c new file mode 100644 index 0000000..d248f9c --- /dev/null +++ b/tests/queue-test.c @@ -0,0 +1,132 @@ +// Test for the queue type in SilverMUD: +#include +#include +#include +#include "../src/queue.h" + +#define formatBoolean(b) ((b) ? "true" : "false") +#define formatEquality(b) ((b) ? "equal" : "not equal") + +int main(void) +{ + // Create a queue: + printf("-=[ Creating queue ]=-:\n"); + queue * testQueue = createQueue(); + + // Check that the queue has the correct values: + printf("-=[ Checking initial values ]=-:\n"); + + printf("- Item count should be 0:\n"); + assert(testQueue->itemCount == 0); + printf("Item count is %d.\n\n", testQueue->itemCount); + + printf("- Lock should be false:\n"); + assert(testQueue->lock == false); + printf("Lock is %s.\n\n", formatBoolean(testQueue->lock)); + + printf("- Front should be (nil):\n"); + assert(testQueue->front == NULL); + printf("Front is %p.\n\n", testQueue->front); + + printf("- Back should be (nil):\n"); + assert(testQueue->back == NULL); + printf("Back is %p.\n\n", testQueue->front); + + // Create some items for the queue: + inputMessage * testInputMessage = malloc(sizeof(inputMessage)); + testInputMessage->sender = NULL; + testInputMessage->next = NULL; + testInputMessage->content = malloc(sizeof(userMessage)); + strcpy(testInputMessage->content->senderName,"Queue Test Input Sender"); + strcpy(testInputMessage->content->messageContent, "Queue Test Input Content - Hello!"); + + outputMessage * testOutputMessage = malloc(sizeof(outputMessage)); + for(int index = 0; index < PLAYERCOUNT; index++) + { + testOutputMessage->targets[index] = NULL; + } + testOutputMessage->next = NULL; + testOutputMessage->content = malloc(sizeof(userMessage)); + strcpy(testOutputMessage->content->senderName, "Queue Test Output Sender"); + strcpy(testOutputMessage->content->messageContent, "Queue Test Output Content - World!"); + + commandEvent * testCommandEvent = malloc(sizeof(commandEvent)); + testCommandEvent->next = NULL; + testCommandEvent->caller = NULL; + testCommandEvent->command = malloc(5 * sizeof(char)); + testCommandEvent->arguments = malloc(15 * sizeof(char)); + strcpy(testCommandEvent->command, "Test"); + strcpy(testCommandEvent->arguments, "Test Arguments"); + + // Add them to the queue: + printf("-=[ Adding items to the queue ]=-:\n"); + printf("- First item, Item count should be 1. Front and Back should be equal.\n"); + pushQueue(testQueue, testInputMessage, INPUT_MESSAGE); + assert(testQueue->itemCount == 1); + assert(testQueue->front == testQueue->back); + printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount, + formatEquality(testQueue->front == testQueue->back)); + + printf("- Second item, Item count should be 2. Front and Back should be not equal.\n"); + pushQueue(testQueue, testOutputMessage, OUTPUT_MESSAGE); + assert(testQueue->itemCount == 2); + assert(testQueue->front != testQueue->back); + printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount, + formatEquality(testQueue->front == testQueue->back)); + + printf("- Third item, Item count should be 3. Front and Back should be not equal.\n"); + pushQueue(testQueue, testCommandEvent, COMMAND); + assert(testQueue->itemCount == 3); + assert(testQueue->front != testQueue->back); + printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount, + formatEquality(testQueue->front == testQueue->back)); + + printf("-=[ Checking items and popping from queue ]=-:\n"); + printf("- First item peeked should point to testInputMessage.\n"); + assert(peekQueue(testQueue)->data.inputMessage == testInputMessage); + printf("Peeked data is located at: %p, testInputMessage is located at: %p.\n\n", + peekQueue(testQueue)->data.inputMessage, testInputMessage); + + printf("- Popping first item, Item count should be 2, Front and Back should not be equal.\n"); + popQueue(testQueue); + assert(testQueue->itemCount == 2); + assert(testQueue->front != testQueue->back); + printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount, + formatEquality(testQueue->front == testQueue->back)); + + printf("- Second item peeked should point to testOutputMessage.\n"); + assert(peekQueue(testQueue)->data.outputMessage == testOutputMessage); + printf("Peeked data is located at: %p, testOutputMessage is located at: %p.\n\n", + peekQueue(testQueue)->data.outputMessage, testOutputMessage); + + printf("- Popping second item, Item count should be 1, Front and Back should be equal.\n"); + popQueue(testQueue); + assert(testQueue->itemCount == 1); + assert(testQueue->front == testQueue->back); + printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount, + formatEquality(testQueue->front == testQueue->back)); + + printf("- Third item peeked should point to testCommandEvent.\n"); + assert(peekQueue(testQueue)->data.command == testCommandEvent); + printf("Peeked data is located at: %p, testCommandEvent is located at: %p.\n\n", + peekQueue(testQueue)->data.command, testCommandEvent); + + printf("- Popping third item:\n"); + popQueue(testQueue); + + printf("- Item count should be 0:\n"); + assert(testQueue->itemCount == 0); + printf("Item count is %d.\n\n", testQueue->itemCount); + + printf("- Lock should be false:\n"); + assert(testQueue->lock == false); + printf("Lock is %s.\n\n", formatBoolean(testQueue->lock)); + + printf("- Front should be (nil):\n"); + assert(testQueue->front == NULL); + printf("Front is %p.\n\n", testQueue->front); + + printf("- Back should be (nil):\n"); + assert(testQueue->back == NULL); + printf("Back is %p.\n\n", testQueue->front); +}