From b3a0345b199369dc64c5c522177b6a7ee1ac0372 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sun, 19 Feb 2023 22:13:06 +0000 Subject: [PATCH 01/39] Added initial support for talking. - /talk now allows you to begin a conversation, given a player name, or end one, given no arguments. - The client now properly clears previous messages when sending to the server. - Added talkingWith to playerInfo. --- src/client/SilverMUDClient.c | 3 +- src/gamelogic.c | 122 ++++++++++++++++++++++++++--------- src/playerdata.h | 4 +- 3 files changed, 96 insertions(+), 33 deletions(-) diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index 2822f68..ea6155c 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -72,7 +72,8 @@ void * messageSender(void * parameters) } // Send the message off to the server: - messageSend(tlsSession, &sendBuffer); + messageSend(tlsSession, &sendBuffer); + bzero(&sendBuffer, sizeof(char) * MAX); } // Rejoin the main thread: diff --git a/src/gamelogic.c b/src/gamelogic.c index a612ea9..82e5ddf 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -56,35 +56,59 @@ void * gameLogicHandler(void * parameters) strncpy(currentInput->content->senderName, currentInput->sender->playerName, 32); currentInput->content->senderName[31] = '\0'; - // Allocate an array of playerInfo to store the current players in the area for the output message: - playerInfo ** recipients = malloc(sizeof(playerInfo*) * PLAYERCOUNT); - - // Initialize them all to NULL: - for (int index = 0; index < PLAYERCOUNT; index++) + if(currentInput->sender->talkingWith == NULL) { - recipients[index] = NULL; - } + // Allocate an array of playerInfo to store the current players in the area for the output message: + playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*)); - // Get the players in the current area and add them to our array: - int recipientIndex = 0; - for (int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++) - { - if (threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea) + // Initialize them all to NULL: + for (int index = 0; index < PLAYERCOUNT; index++) { - recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex]; - recipientIndex++; + recipients[index] = NULL; } - } - // Create the outputMessage for the queue: - outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); + // Get the players in the current area and add them to our array: + int recipientIndex = 0; + for (int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++) + { + if (threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea) + { + recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex]; + recipientIndex++; + } + } - // Push the message onto the queue: - pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); + // Create the outputMessage for the queue: + outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); + + // Push the message onto the queue: + pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); - // Free the array; - free(recipients); + // Free the array; + free(recipients); + } + else + { + // Allocate an array of one playerInfo to store the pointer to the other player in the conversation: + playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); + + // Set the talkingWith player as the recipient of the message: + recipients[0] = currentInput->sender->talkingWith; + + // There's only one recipient: + int recipientIndex = 1; + + // Create the outputMessage for the queue: + outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); + + // Push the message onto the queue: + pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); + + // Free the array; + free(recipients); + } } + bzero(currentInput, sizeof(inputMessage)); currentInput = NULL; threadParameters->inputQueue->lock = false; popQueue(threadParameters->inputQueue); @@ -104,19 +128,20 @@ void queueMessagedCommand(queue * queue, inputMessage * messageToQueue) // Seperate the command from it's arguments: strtok(messageToQueue->content->messageContent, " "); - + // Copy the command and arguments to the new commandEvent: memcpy(newCommand->command, &messageToQueue->content->messageContent[1], 16); memcpy(newCommand->arguments, &messageToQueue->content->messageContent[strlen(newCommand->command) + 2], MAX - (strlen(newCommand->command) + 2)); + // Ensure the arguments are safe to parse, without adding newlines: userNameSanatize(newCommand->command, 16); newCommand->command[15] = '\0'; userNameSanatize(newCommand->arguments, MAX); newCommand->arguments[MAX - 1] = '\0'; - + // Lowercase the command for easier comparison: for (char * character = newCommand->command; *character; ++character) { @@ -159,7 +184,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) return -1; } - // Hash the command and execute the relevant functionality: + // Switch to the relevant command based on the hash: switch (hashCommand(currentCommand->command, strlen(currentCommand->command))) { // Look command: Returns the description of the current area and paths: @@ -390,7 +415,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) case 163143: { // Allocate the userMessage to send: - userMessage * tryMessage = malloc(sizeof(userMessage)); + userMessage * tryMessage = calloc(1, (sizeof(userMessage))); tryMessage->senderName[0] = '\0'; // Temporary message until we can implement objects, events, and challenges. @@ -521,14 +546,47 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) } // Talk command: Allows the player to begin a chat session with another player: - case 601264: + case 6012644: { - userMessage * talkMessage = malloc(sizeof(userMessage)); + userMessage * talkMessage = calloc(1, sizeof(userMessage)); talkMessage->senderName[0] = '\0'; - // Temporary message until we can implement objects, events, and challenges. - strcpy(talkMessage->messageContent, "The talk command is currently not implemented. Implement it if you want to use it.\n"); - + // If there's no name specified, end the current chat sessions. + if (currentCommand->arguments[0] == '\0' || currentCommand->arguments == NULL) + { + currentCommand->caller->talkingWith = NULL; + strcpy(talkMessage->messageContent, "Conversation ended.\n"); + } + else + { + for(int playerIndex = 0; playerIndex < *parameters->playerCount; playerIndex++) + { + if(strncmp(currentCommand->arguments, parameters->connectedPlayers[playerIndex].playerName, 31) == 0) + { + currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]); + + // Fill out the message to inform the receiving user what is happening: + strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 31); + strcat(talkMessage->messageContent, " is talking to you. \n"); + + playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); + recipients[0] = &(parameters->connectedPlayers[playerIndex]); + + // Allocate an outputMessage for the receiving user: + outputMessage * talkReceiverMessage = createTargetedOutputMessage(talkMessage, recipients, 1); + + // Queue the outputMessage: + pushQueue(parameters->outputQueue, talkReceiverMessage, OUTPUT_MESSAGE); + + // Prep the message to the calling user. + strcpy(talkMessage->messageContent, "Conversation begun with: "); + strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); + strcat(talkMessage->messageContent, ".\n"); + } + } + + } + // Allocate an outputMessage for the queue: outputMessage * talkOutputMessage = createTargetedOutputMessage(talkMessage, ¤tCommand->caller, 1); @@ -545,7 +603,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) case 5284234: { // Allocate a userMessage containing null characters as the first char in both fields: - userMessage * exitMessage = malloc(sizeof(userMessage)); + userMessage * exitMessage = calloc(1, (sizeof(userMessage))); exitMessage->senderName[0] = '\0'; exitMessage->messageContent[0] = '\0'; @@ -595,6 +653,8 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) } // Remove the current command and unlock the queue: + bzero(currentCommand->command, sizeof(char) * 16); + bzero(currentCommand->arguments, sizeof(char) * MAX); currentCommand = NULL; queue->lock = false; popQueue(queue); diff --git a/src/playerdata.h b/src/playerdata.h index f703682..b48bf80 100644 --- a/src/playerdata.h +++ b/src/playerdata.h @@ -9,6 +9,7 @@ #include "linkedlist.h" // Let the compiler know there will be structs defined elsewhere: +typedef struct playerInfo playerInfo; typedef struct playerArea playerArea; typedef struct playerPath playerPath; typedef struct listNode listNode; @@ -49,8 +50,9 @@ typedef struct playerSkill // Information about a single player's character: typedef struct playerInfo { - char playerName[32]; + playerInfo * talkingWith; playerArea * currentArea; + char playerName[32]; statBlock * stats; list * skills; } playerInfo; From 26a5496594e9f6df400c09b64841913e4f95e2b5 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Mon, 20 Feb 2023 23:30:41 +0000 Subject: [PATCH 02/39] Added shouting. - Players can now use /shout to send a message to the current room while talking. - No longer adding unnecessary newlines to /talk. --- src/gamelogic.c | 52 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 82e5ddf..36c5d55 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -80,7 +80,7 @@ void * gameLogicHandler(void * parameters) // Create the outputMessage for the queue: outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); - + // Push the message onto the queue: pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); @@ -544,7 +544,50 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) free(formattedString); break; } - + // Shout command: Allows the player to talk to everyone in the area if they are in a conversation. + case 220952831: + { + // Allocate an array of playerInfo to store the current players in the area for the output message: + playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*)); + + // Initialize them all to NULL: + for (int index = 0; index < PLAYERCOUNT; index++) + { + recipients[index] = NULL; + } + + // Get the players in the current area and add them to our array: + int recipientIndex = 0; + for (int playerIndex = 0; playerIndex < *parameters->playerCount; playerIndex++) + { + if (parameters->connectedPlayers[playerIndex].currentArea == currentCommand->caller->currentArea) + { + recipients[recipientIndex] = ¶meters->connectedPlayers[playerIndex]; + recipientIndex++; + } + } + + // Create a userMessage to be filled with the data from the command's arguments and caller: + userMessage * shoutMessage = calloc(1, sizeof(userMessage)); + + // Copy in the data and terminate it: + strncpy(shoutMessage->senderName, currentCommand->caller->playerName, 32); + shoutMessage->senderName[31] = '\0'; + strncpy(shoutMessage->messageContent, currentCommand->arguments, MAX); + shoutMessage->messageContent[MAX - 1] = '\0'; + strncat(shoutMessage->messageContent, "\n", MAX); + + // Create the outputMessage for the queue: + outputMessage * shoutOutputMessage = createTargetedOutputMessage(shoutMessage, recipients, recipientIndex); + + // Push the message onto the output queue: + pushQueue(parameters->outputQueue, shoutOutputMessage, OUTPUT_MESSAGE); + + // Free the array: + free(recipients); + + break; + } // Talk command: Allows the player to begin a chat session with another player: case 6012644: { @@ -555,7 +598,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) if (currentCommand->arguments[0] == '\0' || currentCommand->arguments == NULL) { currentCommand->caller->talkingWith = NULL; - strcpy(talkMessage->messageContent, "Conversation ended.\n"); + strcpy(talkMessage->messageContent, "Conversation ended."); } else { @@ -567,7 +610,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Fill out the message to inform the receiving user what is happening: strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 31); - strcat(talkMessage->messageContent, " is talking to you. \n"); + strcat(talkMessage->messageContent, " is talking to you."); playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); recipients[0] = &(parameters->connectedPlayers[playerIndex]); @@ -581,7 +624,6 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Prep the message to the calling user. strcpy(talkMessage->messageContent, "Conversation begun with: "); strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); - strcat(talkMessage->messageContent, ".\n"); } } From 0add9572243bbdc95effa08af3ed5843d7288fa6 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 25 Feb 2023 19:51:47 +0000 Subject: [PATCH 03/39] Changed from bzero to memset - All instances of bzero have been replaced in an effort to make SilverMUD slightly easier to port. --- src/client/SilverMUDClient.c | 3 ++- src/gamelogic.c | 20 ++++++++++---------- src/server/SilverMUDServer.c | 5 +++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index ea6155c..3c260d3 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,7 @@ void * messageSender(void * parameters) // Send the message off to the server: messageSend(tlsSession, &sendBuffer); - bzero(&sendBuffer, sizeof(char) * MAX); + memset(&sendBuffer, 0, sizeof(char) * MAX); } // Rejoin the main thread: diff --git a/src/gamelogic.c b/src/gamelogic.c index 36c5d55..5c24d0f 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -108,7 +108,7 @@ void * gameLogicHandler(void * parameters) free(recipients); } } - bzero(currentInput, sizeof(inputMessage)); + memset(currentInput, 0, sizeof(inputMessage)); currentInput = NULL; threadParameters->inputQueue->lock = false; popQueue(threadParameters->inputQueue); @@ -204,7 +204,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); //queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1); - bzero(lookMessage, sizeof(userMessage)); + memset(lookMessage, 0, sizeof(userMessage)); // Loop through the paths and send the appropriate amount of messages: int charCount = 13; @@ -221,7 +221,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Queue the outputMessage: pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - bzero(lookMessage, sizeof(userMessage)); + memset(lookMessage, 0, sizeof(userMessage)); charCount = 0; } snprintf(formattedString, 64, "\n\t%ld. %s", index + 1, @@ -278,7 +278,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Queue the outputMessage: pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - bzero(statMessage->messageContent, sizeof(char) * MAX); + memset(statMessage->messageContent, 0, sizeof(char) * MAX); if (currentCommand->caller->skills->head != NULL) { size_t skillIndex = 0; @@ -299,7 +299,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Queue the outputMessage: pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - bzero(statMessage, sizeof(userMessage)); + memset(statMessage, 0, sizeof(userMessage)); charCount = 0; break; } @@ -519,7 +519,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Queue the outputMessage: pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE); - bzero(listMessage, sizeof(userMessage)); + memset(listMessage, 0, sizeof(userMessage)); charCount = 0; addNewline = false; } @@ -695,8 +695,8 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) } // Remove the current command and unlock the queue: - bzero(currentCommand->command, sizeof(char) * 16); - bzero(currentCommand->arguments, sizeof(char) * MAX); + memset(currentCommand->command, 0, sizeof(char) * 16); + memset(currentCommand->arguments, 0, sizeof(char) * MAX); currentCommand = NULL; queue->lock = false; popQueue(queue); @@ -747,7 +747,7 @@ outcome statCheck(playerInfo * player, int chance, coreStat statToCheck) return ERROR; } } - int attempt = (random() % 100) + modifier; + int attempt = (rand() % 100) + modifier; if (attempt >= chance) { if (attempt >= 98) @@ -820,7 +820,7 @@ outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t ski } // Attempt the check: - int attempt = (random() % 100) + modifier; + int attempt = (rand() % 100) + modifier; if (attempt >= chance) { if (attempt >= 98) diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index a1dfe49..a18be2f 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -131,7 +132,7 @@ int main(int argc, char ** argv) slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.5\n", delay); // Seed random number generator from the current time: - srandom((unsigned)time(¤tTime)); + srand((unsigned)time(¤tTime)); // Initialize the sockets to 0, so we don't crash. for (int index = 0; index < PLAYERCOUNT; index++) @@ -152,7 +153,7 @@ int main(int argc, char ** argv) slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay); } - bzero(&serverAddress, sizeof(serverAddress)); + memset(&serverAddress, 0, sizeof(serverAddress)); // Assign IP and port: serverAddress.sin_family = AF_INET; From 8814a45c522bad68ea26315b0de7c88cedcdcdfc Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 25 Feb 2023 23:04:35 +0000 Subject: [PATCH 04/39] ALlowed for the server to change client prompt. - The server can now send some data in the normally unused "senderName" field for a server mesasge. - This will be set as the prompt for the client. --- src/client/SilverMUDClient.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index 3c260d3..0e22b63 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -29,6 +29,7 @@ typedef struct threadparameters bool loggingFlag; WINDOW * window; int characterDelay; + char * prompt; } threadparameters; // Use sockaddr as a type: @@ -45,13 +46,15 @@ void * messageSender(void * parameters) FILE * loggingStream = threadParameters->loggingStream; bool loggingFlag = threadParameters->loggingFlag; WINDOW * window = threadParameters->window; + char * prompt = threadParameters->prompt; userMessage sendBuffer; // Repeatedly get input from the user, place it in a userMessage, and send it to the server: while (!shouldExit) { // Print the prompt: - wprintw(window, "\n\n\nCOMM-LINK> "); + wprintw(window, "\n\n\n"); + wprintw(window, prompt); if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR) { // Quit if there's any funny business with getting input: @@ -111,8 +114,15 @@ void * messageReceiver(void * parameters) // Check if it's a server message: else if (receiveBuffer.senderName[0] == '\0') { + // Check if the server wants to change the prompt: + if (receiveBuffer.senderName[1] != '\0') + { + strncpy(threadParameters->prompt, &receiveBuffer.senderName[1], 63); + threadParameters->prompt[63] = '\0'; + } + // Check if it's a command to disconnect: - if (receiveBuffer.messageContent[0] == '\0') + if (receiveBuffer.messageContent[0] == '\0' && receiveBuffer.senderName[1] != '\0') { shouldExit = true; pthread_exit(NULL); @@ -307,6 +317,7 @@ int main(int argc, char ** argv) logArea->tlsSession = tlsSession; logArea->loggingFlag = chatLogging; logArea->characterDelay = characterDelay; + if (chatLog != NULL) { logArea->loggingStream = chatLog; @@ -314,13 +325,18 @@ int main(int argc, char ** argv) messageArea->window = newwin(3, COLS - 2, LINES - 4, 1); messageArea->tlsSession = tlsSession; messageArea->loggingFlag = gameLogging; - + // Set the appropriate log pointers: if (gameLog != NULL) { messageArea->loggingStream = gameLog; } - + + // Set up the string to hold the current "prompt" that the server has sent: + messageArea->prompt = calloc(64, sizeof(char)); + strcpy(messageArea->prompt, "> "); + logArea->prompt = messageArea->prompt; + // Set the two windows to scroll: scrollok(logArea->window, true); scrollok(messageArea->window, true); From 408033d48a0d85b5e458bd969a4c4eb3267edada Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sun, 26 Feb 2023 00:16:07 +0000 Subject: [PATCH 05/39] Talking now changes the prompt. - The server will now change the prompt for a user who begins a conversation with another player to that player's name. --- src/client/SilverMUDClient.c | 12 ++++++++---- src/gamelogic.c | 25 +++++++++++++++++++++++-- src/server/SilverMUDServer.c | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index 0e22b63..6209eff 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -52,9 +52,13 @@ void * messageSender(void * parameters) // Repeatedly get input from the user, place it in a userMessage, and send it to the server: while (!shouldExit) { - // Print the prompt: + usleep(100000); + // Clear the window: wprintw(window, "\n\n\n"); + + // Print the prompt: wprintw(window, prompt); + if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR) { // Quit if there's any funny business with getting input: @@ -122,7 +126,7 @@ void * messageReceiver(void * parameters) } // Check if it's a command to disconnect: - if (receiveBuffer.messageContent[0] == '\0' && receiveBuffer.senderName[1] != '\0') + if (receiveBuffer.messageContent[0] == '\0') { shouldExit = true; pthread_exit(NULL); @@ -333,8 +337,8 @@ int main(int argc, char ** argv) } // Set up the string to hold the current "prompt" that the server has sent: - messageArea->prompt = calloc(64, sizeof(char)); - strcpy(messageArea->prompt, "> "); + messageArea->prompt = calloc(32, sizeof(char)); + strcpy(messageArea->prompt, " Login > "); logArea->prompt = messageArea->prompt; // Set the two windows to scroll: diff --git a/src/gamelogic.c b/src/gamelogic.c index 5c24d0f..88782e8 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -599,6 +599,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) { currentCommand->caller->talkingWith = NULL; strcpy(talkMessage->messageContent, "Conversation ended."); + strncat(&talkMessage->senderName[1], " > ", 4); } else { @@ -609,6 +610,8 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]); // Fill out the message to inform the receiving user what is happening: + strncat(&talkMessage->senderName[1], currentCommand->caller->playerName, 27); + strncat(&talkMessage->senderName[1], " > ", 4); strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 31); strcat(talkMessage->messageContent, " is talking to you."); @@ -626,9 +629,12 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); } } - } - + if(talkMessage->messageContent[0] == '\0') + { + strcpy(talkMessage->messageContent, "There is no player by that name connected."); + } + // Allocate an outputMessage for the queue: outputMessage * talkOutputMessage = createTargetedOutputMessage(talkMessage, ¤tCommand->caller, 1); @@ -683,6 +689,21 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) { strncpy(currentCommand->caller->playerName, currentCommand->arguments, 16); currentCommand->caller->currentArea = getFromList(parameters->areaList, 1)->area; + + // Allocate a userMessage containing null characters as the first char in both fields: + userMessage * joinMessage = calloc(1, (sizeof(userMessage))); + memcpy(joinMessage->senderName, "\0 > \0", 5); + strcpy(joinMessage->messageContent, "Logged in successfully."); + + // Allocate an outputMessage for the queue: + outputMessage * joinOutputMessage = createTargetedOutputMessage(joinMessage, ¤tCommand->caller, 1); + + // Queue the outputMessage: + pushQueue(parameters->outputQueue, joinOutputMessage, OUTPUT_MESSAGE); + + // Free the userMessage + free(joinMessage); + // Call the look command after joining. It's fine to unlock, because the loop won't // continue until the command is queued: queue->lock = false; diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index a18be2f..5b18ccf 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -285,7 +285,7 @@ int main(int argc, char ** argv) while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0); // Send a greeting message: - strcpy(sendBuffer.senderName, ""); + memcpy(sendBuffer.senderName, "\0 Login > \0", 11); strcpy(sendBuffer.messageContent, "Welcome to the server!"); messageSend(tlssessions[index], &sendBuffer); strcpy(receiveBuffer.messageContent, "/look"); From a99296e31eeacd8e3b01ed3c3648c94d094b7799 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Mon, 27 Feb 2023 16:35:32 +0000 Subject: [PATCH 06/39] Added a list of players in the area to /look. - /look now lists the current players in the area. --- src/gamelogic.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 36c5d55..85c5fdb 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -235,8 +235,44 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) // Queue the outputMessage: pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); } - free(lookMessage); + // Clear the message: + memset(lookMessage, 0, sizeof(userMessage)); + if(currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area) + { + // Show the players in the area: + charCount = 23; + strncat(lookMessage->messageContent, "These players are here:", 24); + + int playerNumber = 1; + for(int index = 0; index < *(parameters->playerCount); index++) + { + if (parameters->connectedPlayers[index].currentArea == currentCommand->caller->currentArea) + { + if ((charCount + 38) >= MAX) + { + lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); + + // Queue the outputMessage: + pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); + memset(lookMessage, 0, sizeof(userMessage)); + charCount = 0; + } + snprintf(formattedString, 38, "\n%02d. %32s", playerNumber++, + parameters->connectedPlayers[index].playerName); + strncat(lookMessage->messageContent, formattedString, 37); + charCount += 38; + + // Allocate another outputMessage for the queue: + lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); + + // Queue the outputMessage: + pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); + } + } + } + + free(lookMessage); break; } From f83c03aadf8b55137a3d16ca35d1185bc564f4f4 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Mon, 27 Feb 2023 17:07:03 +0000 Subject: [PATCH 07/39] Fixed the name being sent to the wrong user's prompt while talking. - The talking prompt was being sent to the receiving user, not the sending user. --- src/gamelogic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 51f8a4f..7595bbf 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -646,8 +646,6 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]); // Fill out the message to inform the receiving user what is happening: - strncat(&talkMessage->senderName[1], currentCommand->caller->playerName, 27); - strncat(&talkMessage->senderName[1], " > ", 4); strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 31); strcat(talkMessage->messageContent, " is talking to you."); @@ -661,6 +659,8 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) pushQueue(parameters->outputQueue, talkReceiverMessage, OUTPUT_MESSAGE); // Prep the message to the calling user. + strncat(&talkMessage->senderName[1], currentCommand->arguments, 27); + strncat(&talkMessage->senderName[1], " > ", 4); strcpy(talkMessage->messageContent, "Conversation begun with: "); strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); } From 84c20dfdbf60e69b7cf69b167b19e3ec678c6cbf Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Mon, 27 Feb 2023 17:12:03 +0000 Subject: [PATCH 08/39] Actually fixed it this time, and squashed some warnings. --- src/gamelogic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 7595bbf..696cc0b 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -258,9 +258,9 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) memset(lookMessage, 0, sizeof(userMessage)); charCount = 0; } - snprintf(formattedString, 38, "\n%02d. %32s", playerNumber++, + snprintf(formattedString, 45, "\n%02d. %31s", playerNumber++, parameters->connectedPlayers[index].playerName); - strncat(lookMessage->messageContent, formattedString, 37); + strncat(lookMessage->messageContent, formattedString, 64); charCount += 38; // Allocate another outputMessage for the queue: @@ -646,7 +646,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]); // Fill out the message to inform the receiving user what is happening: - strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 31); + strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 32); strcat(talkMessage->messageContent, " is talking to you."); playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); @@ -659,7 +659,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) pushQueue(parameters->outputQueue, talkReceiverMessage, OUTPUT_MESSAGE); // Prep the message to the calling user. - strncat(&talkMessage->senderName[1], currentCommand->arguments, 27); + memcpy(&talkMessage->senderName[1], currentCommand->arguments, sizeof(char) * 27); strncat(&talkMessage->senderName[1], " > ", 4); strcpy(talkMessage->messageContent, "Conversation begun with: "); strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); From 8f08265c528ff224eda4249592d62964f3d772db Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Mon, 27 Feb 2023 20:00:50 +0000 Subject: [PATCH 09/39] Talking messages now appear in the chat log correctly. - Added logic to properly add the relevant users in the correct order to ensure message delivery. --- src/gamelogic.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 696cc0b..421ebcd 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -89,14 +89,32 @@ void * gameLogicHandler(void * parameters) } else { - // Allocate an array of one playerInfo to store the pointer to the other player in the conversation: - playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); + // Allocate an array of two playerInfo to store the pointers to the players in the conversation: + playerInfo ** recipients = calloc(2, (sizeof(playerInfo*))); - // Set the talkingWith player as the recipient of the message: - recipients[0] = currentInput->sender->talkingWith; + // Find which player is first in the player list: + bool senderIsFirst = false; + for(int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++) + { + if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender) + { + senderIsFirst = true; + break; + } + if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender->talkingWith) + { + senderIsFirst = false; + break; + } + } + + // Set the proper recipients: + recipients[0] = (senderIsFirst) ? currentInput->sender : currentInput->sender->talkingWith; + recipients[1] = (senderIsFirst) ? currentInput->sender->talkingWith : currentInput->sender; + // There's only one recipient: - int recipientIndex = 1; + int recipientIndex = 2; // Create the outputMessage for the queue: outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); @@ -261,15 +279,14 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) snprintf(formattedString, 45, "\n%02d. %31s", playerNumber++, parameters->connectedPlayers[index].playerName); strncat(lookMessage->messageContent, formattedString, 64); - charCount += 38; - - // Allocate another outputMessage for the queue: - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); + charCount += 38; } } + // Allocate another outputMessage for the queue: + lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); + + // Queue the outputMessage: + pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); } free(lookMessage); From 995a177c37c9dd21c922f59e80c01d662686cfe8 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Wed, 1 Mar 2023 23:14:37 +0000 Subject: [PATCH 10/39] Increased prompt reprint delay. - The client's prompt now delays for twice as long to ensure that an updated prompt is displayed next. --- src/client/SilverMUDClient.c | 2 +- src/linkedlist.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index 6209eff..a011f51 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -52,7 +52,7 @@ void * messageSender(void * parameters) // Repeatedly get input from the user, place it in a userMessage, and send it to the server: while (!shouldExit) { - usleep(100000); + usleep(200000); // Clear the window: wprintw(window, "\n\n\n"); diff --git a/src/linkedlist.c b/src/linkedlist.c index 59e7bab..c7ff81d 100644 --- a/src/linkedlist.c +++ b/src/linkedlist.c @@ -436,7 +436,8 @@ bool getIndexFromList(list * list, void * data, listDataType type, size_t * inde return false; } - for(*index = 0; *index < list->itemCount; *index += 1) + // Search through the list, one-by-one, comparing the list items: + for (*index = 0; *index < list->itemCount; *index += 1) { switch (type) { From 8dab8bfd06acd6c6a863c8406fd4dd86016be2d6 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 25 Mar 2023 21:38:00 +0000 Subject: [PATCH 11/39] Begin adding Scheme intepreretation. We now have a scheme thread and a interpreter. --- Makefile | 7 ++++--- src/areadata.h | 1 + src/schemeintegration.c | 11 +++++++++++ src/schemeintegration.h | 16 ++++++++++++++++ src/server/SilverMUDServer.c | 10 +++++++--- 5 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 src/schemeintegration.c create mode 100644 src/schemeintegration.h diff --git a/Makefile b/Makefile index e1478ab..f35ca9a 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,10 @@ clientsrc = $(wildcard src/*.c) \ clientobj = $(clientsrc:.c=.o) serversrc = $(wildcard src/*.c) \ src/server/SilverMUDServer.c -serverobj = $(serversrc:.c=.o) -CLIENTLDFLAGS= -lpthread -lncurses -lgnutls -SERVERLDFLAGS= -lpthread -lncurses -lgnutls +serverobj = $(serversrc:.c=.o) +CFLAGS = `pkg-config --cflags guile-3.0` +CLIENTLDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` +SERVERLDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` SilverMUDClient: $(clientobj) gcc -o $@ $^ $(CLIENTLDFLAGS) diff --git a/src/areadata.h b/src/areadata.h index 327db34..26356fe 100644 --- a/src/areadata.h +++ b/src/areadata.h @@ -12,6 +12,7 @@ // Let the compiler know that we're going to define these types: typedef struct playerPath playerPath; typedef struct playerArea playerArea; +typedef struct list list; // A path, which contains a name, and a pointer to the area which the player will travel to: struct playerPath diff --git a/src/schemeintegration.c b/src/schemeintegration.c new file mode 100644 index 0000000..cb72202 --- /dev/null +++ b/src/schemeintegration.c @@ -0,0 +1,11 @@ +// schemeintegration.h: Function definitions for SilverMUD's Scheme integration. +// Barra Ó Catháin, 2023. +#include +#include "schemeintegration.h" + +void * schemeHandler(void * parameters) +{ + scm_init_guile(); + scm_shell(0, NULL); +} + diff --git a/src/schemeintegration.h b/src/schemeintegration.h new file mode 100644 index 0000000..feef3f9 --- /dev/null +++ b/src/schemeintegration.h @@ -0,0 +1,16 @@ +// schemeintegration.h: Data-structures and function prototypes for SilverMUD's Scheme integration. +// Barra Ó Catháin, 2023. +#ifndef SCHEMEINTEGRATION_H +#define SCHEMEINTEGRATION_H +#include "linkedlist.h" + +typedef struct list list; + +typedef struct SchemeThreadParameters +{ + list * areaList, * skillList; +} SchemeThreadParameters; + +void * schemeHandler(void * parameters); + +#endif diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index 5b18ccf..a4bd6b2 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -26,6 +26,7 @@ #include "../linkedlist.h" #include "../texteffects.h" #include "../inputoutput.h" +#include "../schemeintegration.h" typedef struct sockaddr sockaddr; void sigintHandler(int signal) @@ -41,7 +42,7 @@ int main(int argc, char ** argv) int socketFileDesc, connectionFileDesc, length, clientsAmount, socketCheck, activityCheck, returnVal; fd_set connectedClients; - pthread_t gameLogicThread, outputThread; + pthread_t gameLogicThread, outputThread, schemeThread; int clientSockets[PLAYERCOUNT]; userMessage sendBuffer, receiveBuffer; playerInfo connectedPlayers[PLAYERCOUNT]; @@ -223,10 +224,13 @@ int main(int argc, char ** argv) outputParameters->tlssessions = tlssessions; outputParameters->connectedPlayers = connectedPlayers; pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters); - slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - slowPrint("=====\n", delay); + + pthread_create(&schemeThread, NULL, &schemeHandler, NULL); + slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); + + slowPrint("=====\n", delay); while(true) { // Clear the set of file descriptors and add the master socket: From adc036a1ae69f33bf442ea8ce694c14722c4e69f Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 25 Mar 2023 23:56:25 +0000 Subject: [PATCH 12/39] Added create-skill to the Scheme intepreter. --- src/schemeintegration.c | 19 ++++++++++++++++--- src/schemeintegration.h | 3 +++ src/server/SilverMUDServer.c | 9 +++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index cb72202..341bf2a 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -3,9 +3,22 @@ #include #include "schemeintegration.h" -void * schemeHandler(void * parameters) +SCM scheme_create_skill(SCM string, SCM skilllist) { - scm_init_guile(); - scm_shell(0, NULL); + size_t skillNameLength = 0; + char * skillName = scm_to_latin1_stringn(string, &skillNameLength); + createSkill(scm_to_pointer(skilllist), skillName, skillNameLength, false); + free(skillName); + return SCM_BOOL_T; } +void * schemeHandler(void * parameters) +{ + SchemeThreadParameters * schemeThreadParameters = parameters; + + scm_init_guile(); + scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); + scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); + scm_shell(0, NULL); +} + diff --git a/src/schemeintegration.h b/src/schemeintegration.h index feef3f9..9f20f25 100644 --- a/src/schemeintegration.h +++ b/src/schemeintegration.h @@ -2,13 +2,16 @@ // Barra Ó Catháin, 2023. #ifndef SCHEMEINTEGRATION_H #define SCHEMEINTEGRATION_H +#include "queue.h" #include "linkedlist.h" typedef struct list list; +typedef struct queue queue; typedef struct SchemeThreadParameters { list * areaList, * skillList; + queue * inputQueue, * outputQueue; } SchemeThreadParameters; void * schemeHandler(void * parameters); diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index a4bd6b2..85c1d0e 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -226,11 +226,12 @@ int main(int argc, char ** argv) pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters); slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - - pthread_create(&schemeThread, NULL, &schemeHandler, NULL); + SchemeThreadParameters * schemeParameters = malloc(sizeof(SchemeThreadParameters)); + schemeParameters->skillList = globalSkillList; slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - slowPrint("=====\n", delay); + pthread_create(&schemeThread, NULL, &schemeHandler, schemeParameters); + while(true) { // Clear the set of file descriptors and add the master socket: @@ -280,7 +281,7 @@ int main(int argc, char ** argv) if (clientSockets[index] == 0) { clientSockets[index] = connectionFileDesc; - printf("Adding to list of sockets as %d.\n", index); + //printf("Adding to list of sockets as %d.\n", index); gnutls_transport_set_int(tlssessions[index], clientSockets[index]); do { From 6b01917c1122c5960326bd843c5349d80eabbc4b Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Wed, 5 Apr 2023 21:34:57 +0100 Subject: [PATCH 13/39] Added a message-everyone scheme function. --- src/schemeintegration.c | 20 ++++++++++++++++++++ src/server/SilverMUDServer.c | 1 + 2 files changed, 21 insertions(+) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 341bf2a..91742fe 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -12,13 +12,33 @@ SCM scheme_create_skill(SCM string, SCM skilllist) return SCM_BOOL_T; } +SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) +{ + outputMessage * newOutputMessage = calloc(1, sizeof(userMessage)); + userMessage * newMessage = calloc(1, sizeof(userMessage)); + newOutputMessage->content = newMessage; + newOutputMessage->recipientsCount = 0; + newOutputMessage->recipients = NULL; + + scm_to_locale_stringbuf(sendername, newMessage->senderName, 31); + newMessage->senderName[31] = '\0'; + scm_to_locale_stringbuf(messagecontent, newMessage->messageContent, MAX - 1); + newMessage->messageContent[MAX - 1] = '\0'; + + userNameSanatize(newMessage->senderName, 32); + userInputSanatize(newMessage->messageContent, MAX); + pushQueue(scm_to_pointer(outputqueue), newOutputMessage, OUTPUT_MESSAGE); + return SCM_BOOL_T; +} void * schemeHandler(void * parameters) { SchemeThreadParameters * schemeThreadParameters = parameters; scm_init_guile(); scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); + scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); + scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL)); scm_shell(0, NULL); } diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index 85c1d0e..4f8f61f 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -228,6 +228,7 @@ int main(int argc, char ** argv) SchemeThreadParameters * schemeParameters = malloc(sizeof(SchemeThreadParameters)); schemeParameters->skillList = globalSkillList; + schemeParameters->outputQueue = outputQueue; slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); slowPrint("=====\n", delay); pthread_create(&schemeThread, NULL, &schemeHandler, schemeParameters); From 4696a1ec3281aeff3be586397c987620183ef803 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 6 Apr 2023 23:09:20 +0100 Subject: [PATCH 14/39] Adedd some comments to schemeintegration.c --- src/schemeintegration.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 91742fe..3c0a94d 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -3,8 +3,9 @@ #include #include "schemeintegration.h" +// Create a player skill and add it to a given skill list from Scheme: SCM scheme_create_skill(SCM string, SCM skilllist) -{ +{ size_t skillNameLength = 0; char * skillName = scm_to_latin1_stringn(string, &skillNameLength); createSkill(scm_to_pointer(skilllist), skillName, skillNameLength, false); @@ -12,33 +13,56 @@ SCM scheme_create_skill(SCM string, SCM skilllist) return SCM_BOOL_T; } +// Message every currently connected player from Scheme: SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) { + // Allocate the memory for the needed data structures: outputMessage * newOutputMessage = calloc(1, sizeof(userMessage)); userMessage * newMessage = calloc(1, sizeof(userMessage)); + + // Set some basic information for the output message, allowing it to be sent to everyone: newOutputMessage->content = newMessage; newOutputMessage->recipientsCount = 0; newOutputMessage->recipients = NULL; + // Convert the Scheme strings to C strings, and ensure they're NULL terminated: scm_to_locale_stringbuf(sendername, newMessage->senderName, 31); newMessage->senderName[31] = '\0'; scm_to_locale_stringbuf(messagecontent, newMessage->messageContent, MAX - 1); newMessage->messageContent[MAX - 1] = '\0'; + // Clean up the message contents to ensure they're safe to send and display correctly: userNameSanatize(newMessage->senderName, 32); userInputSanatize(newMessage->messageContent, MAX); + + // Push it to the queue, where it will be handled and de-allocated: pushQueue(scm_to_pointer(outputqueue), newOutputMessage, OUTPUT_MESSAGE); + + // Return a Scheme #t value: return SCM_BOOL_T; } + +// The function ran by the Scheme thread which handles all game-master and interpreter interaction: void * schemeHandler(void * parameters) { + // Take in the needed values from the main thread and make it back into the struct: SchemeThreadParameters * schemeThreadParameters = parameters; - + + // Initialize GNU Guile: scm_init_guile(); + + // Register the various functions: scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); + + // Define the various game state pointers as Scheme objects: scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL)); + + // Drop into the Scheme interpreter: scm_shell(0, NULL); + + // Return NULL. This should be unreachable. + return NULL; } From fd846c77443751a0ec8c1743b644ef444a7cb4c7 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 7 Apr 2023 22:13:26 +0100 Subject: [PATCH 15/39] Added change-area-description to the Scheme functions. --- src/server/SilverMUDServer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index 4f8f61f..057a2cd 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -225,10 +225,12 @@ int main(int argc, char ** argv) outputParameters->connectedPlayers = connectedPlayers; pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters); slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - + + // Prepare the Scheme handler thread: SchemeThreadParameters * schemeParameters = malloc(sizeof(SchemeThreadParameters)); schemeParameters->skillList = globalSkillList; schemeParameters->outputQueue = outputQueue; + schemeParameters->areaList = areas; slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); slowPrint("=====\n", delay); pthread_create(&schemeThread, NULL, &schemeHandler, schemeParameters); From 8d4758bea1766f00657fab3c289ee9d274fa5ec2 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 7 Apr 2023 22:13:45 +0100 Subject: [PATCH 16/39] Forgot to add to previous commit. --- src/schemeintegration.c | 42 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 3c0a94d..3aae258 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -1,5 +1,6 @@ // schemeintegration.h: Function definitions for SilverMUD's Scheme integration. // Barra Ó Catháin, 2023. +#include #include #include "schemeintegration.h" @@ -13,6 +14,43 @@ SCM scheme_create_skill(SCM string, SCM skilllist) return SCM_BOOL_T; } +// Change the description of an existing area in a list, given the number of the area in the list, from Scheme: +SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM arealist) +{ + // Check if the area exists: + list * areaList = scm_to_pointer(arealist); + size_t areaNumber = scm_to_size_t(areanumber); + + if (areaList->type != AREA) + { + return SCM_BOOL_F; + } + + playerArea * area = getFromList(areaList, areaNumber)->area; + + if (area == NULL) + { + return SCM_BOOL_F; + } + + // Create a string from the Scheme string and copy it into the area: + size_t newDescriptionLength = 0; + char * newDescription = scm_to_locale_stringn(newdescription, &newDescriptionLength); + memset(area->areaDescription, 0, MAX - 35); + if (newDescriptionLength > MAX - 35) + { + memcpy(area->areaDescription, newDescription, MAX - 35); + } + else + { + memcpy(area->areaDescription, newDescription, newDescriptionLength); + } + + free(newDescription); + + return SCM_BOOL_T; +} + // Message every currently connected player from Scheme: SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) { @@ -54,8 +92,10 @@ void * schemeHandler(void * parameters) // Register the various functions: scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); - + scm_c_define_gsubr("change-area-description", 3, 0, 0, &scheme_change_area_description); + // Define the various game state pointers as Scheme objects: + scm_c_define("area-list", scm_from_pointer(schemeThreadParameters->areaList, NULL)); scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL)); From ce54fb8033f672bc48ad5df50f8301727cc51909 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 7 Apr 2023 22:49:15 +0100 Subject: [PATCH 17/39] Added change-area-name to Scheme, and ensured strings are terminated --- src/schemeintegration.c | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 3aae258..d719828 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -14,6 +14,45 @@ SCM scheme_create_skill(SCM string, SCM skilllist) return SCM_BOOL_T; } +// Change the name of an existing area in a list, given the number of the area in the list, from Scheme: +SCM scheme_change_area_name(SCM newname, SCM areanumber, SCM arealist) +{ + // Check if the area exists: + list * areaList = scm_to_pointer(arealist); + size_t areaNumber = scm_to_size_t(areanumber); + + if (areaList->type != AREA) + { + return SCM_BOOL_F; + } + + playerArea * area = getFromList(areaList, areaNumber)->area; + + if (area == NULL) + { + return SCM_BOOL_F; + } + + // Create a string from the Scheme string and copy it into the area: + size_t newNameLength = 0; + char * newName = scm_to_locale_stringn(newname, &newNameLength); + memset(area->areaName, 0, 32); + if (newNameLength > 32) + { + memcpy(area->areaName, newName, 31); + area->areaName[31] = '\0'; + } + else + { + memcpy(area->areaName, newName, newNameLength); + area->areaName[31] = '\0'; + } + + free(newName); + + return SCM_BOOL_T; +} + // Change the description of an existing area in a list, given the number of the area in the list, from Scheme: SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM arealist) { @@ -40,10 +79,12 @@ SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM areal if (newDescriptionLength > MAX - 35) { memcpy(area->areaDescription, newDescription, MAX - 35); + area->areaDescription[MAX - 36] = '\0'; } else { memcpy(area->areaDescription, newDescription, newDescriptionLength); + area->areaDescription[MAX - 36] = '\0'; } free(newDescription); @@ -92,6 +133,7 @@ void * schemeHandler(void * parameters) // Register the various functions: scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); + scm_c_define_gsubr("change-area-name", 3, 0, 0, &scheme_change_area_name); scm_c_define_gsubr("change-area-description", 3, 0, 0, &scheme_change_area_description); // Define the various game state pointers as Scheme objects: From ce74a785761f83b03c003bed2bb409b0a6826a76 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 8 Apr 2023 23:23:24 +0100 Subject: [PATCH 18/39] Added /talk to the manual, and added some rules about casing. --- README.org | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.org b/README.org index 8fc4d46..f0f1dab 100644 --- a/README.org +++ b/README.org @@ -30,6 +30,7 @@ can be upper or lower-case. | STAT | None | Displays your current status and character sheet. | | SPEC | Core Stat Name | Allows you to apply spec points to a given stat. | | TRY | Core Stat Name/Skill Name, Object Number | Attempt to use the given stat or skill on the object. | +| TALK | Character Name | Begins a conversation with another player. | * Gamemaster's Guide ** Running the Server: @@ -48,6 +49,13 @@ some rules may be made based on my own personal tastes. - [] :: These are brackets. - {} :: These are braces. *** Formatting: +**** Casing: +- Variables :: Variables should be in camelCase, with the exception being + variables used to store SCM values, in which case, they should in be + snake_case. + +- Types :: User defined types should be in PascalCase. + **** Control Statements: - A space should be between the keyword and the condition. This is to make control statements visually distinct from function calls. From 6fd7f6532614130260c2dabb6b02ef687df3d381 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sun, 9 Apr 2023 23:52:14 +0100 Subject: [PATCH 19/39] Made SCM values use snake_case --- src/schemeintegration.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index d719828..047f3b3 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -15,11 +15,11 @@ SCM scheme_create_skill(SCM string, SCM skilllist) } // Change the name of an existing area in a list, given the number of the area in the list, from Scheme: -SCM scheme_change_area_name(SCM newname, SCM areanumber, SCM arealist) +SCM scheme_change_area_name(SCM new_name, SCM area_number, SCM area_list) { // Check if the area exists: - list * areaList = scm_to_pointer(arealist); - size_t areaNumber = scm_to_size_t(areanumber); + list * areaList = scm_to_pointer(area_list); + size_t areaNumber = scm_to_size_t(area_number); if (areaList->type != AREA) { @@ -35,7 +35,7 @@ SCM scheme_change_area_name(SCM newname, SCM areanumber, SCM arealist) // Create a string from the Scheme string and copy it into the area: size_t newNameLength = 0; - char * newName = scm_to_locale_stringn(newname, &newNameLength); + char * newName = scm_to_locale_stringn(new_name, &newNameLength); memset(area->areaName, 0, 32); if (newNameLength > 32) { @@ -54,11 +54,11 @@ SCM scheme_change_area_name(SCM newname, SCM areanumber, SCM arealist) } // Change the description of an existing area in a list, given the number of the area in the list, from Scheme: -SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM arealist) +SCM scheme_change_area_description(SCM new_description, SCM area_number, SCM area_list) { // Check if the area exists: - list * areaList = scm_to_pointer(arealist); - size_t areaNumber = scm_to_size_t(areanumber); + list * areaList = scm_to_pointer(area_list); + size_t areaNumber = scm_to_size_t(area_number); if (areaList->type != AREA) { @@ -74,7 +74,7 @@ SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM areal // Create a string from the Scheme string and copy it into the area: size_t newDescriptionLength = 0; - char * newDescription = scm_to_locale_stringn(newdescription, &newDescriptionLength); + char * newDescription = scm_to_locale_stringn(new_description, &newDescriptionLength); memset(area->areaDescription, 0, MAX - 35); if (newDescriptionLength > MAX - 35) { @@ -93,7 +93,7 @@ SCM scheme_change_area_description(SCM newdescription, SCM areanumber, SCM areal } // Message every currently connected player from Scheme: -SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) +SCM scheme_message_everyone(SCM sender_name, SCM message_content, SCM output_queue) { // Allocate the memory for the needed data structures: outputMessage * newOutputMessage = calloc(1, sizeof(userMessage)); @@ -105,9 +105,9 @@ SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) newOutputMessage->recipients = NULL; // Convert the Scheme strings to C strings, and ensure they're NULL terminated: - scm_to_locale_stringbuf(sendername, newMessage->senderName, 31); + scm_to_locale_stringbuf(sender_name, newMessage->senderName, 31); newMessage->senderName[31] = '\0'; - scm_to_locale_stringbuf(messagecontent, newMessage->messageContent, MAX - 1); + scm_to_locale_stringbuf(message_content, newMessage->messageContent, MAX - 1); newMessage->messageContent[MAX - 1] = '\0'; // Clean up the message contents to ensure they're safe to send and display correctly: @@ -115,7 +115,7 @@ SCM scheme_message_everyone(SCM sendername, SCM messagecontent, SCM outputqueue) userInputSanatize(newMessage->messageContent, MAX); // Push it to the queue, where it will be handled and de-allocated: - pushQueue(scm_to_pointer(outputqueue), newOutputMessage, OUTPUT_MESSAGE); + pushQueue(scm_to_pointer(output_queue), newOutputMessage, OUTPUT_MESSAGE); // Return a Scheme #t value: return SCM_BOOL_T; From 7e1a94820d6d34f71393464b79a3d8998ee5776a Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 11:16:29 +0100 Subject: [PATCH 20/39] Added gitignore - took a while! --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3652a69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Object files: +*.o + +# Binaries: +SilverMUDClient +SilverMUDServer +SilverMUDClientDebug +SilverMUDServerDebug + +# Profiling Artifacts: +gmon.out \ No newline at end of file From b5f2f44fcc7666a8ed355067653cc47bea4877da Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 11:33:36 +0100 Subject: [PATCH 21/39] Improved makefile. - Makefile now doesn't hardcode gcc. (Whether SilverMUD is compiler agnostic remains to be seen.) - The first target is now all, which compiles a "release" build. - General cleanup, removal of duplicate variables, and commenting. --- Makefile | 54 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index f35ca9a..a656190 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,38 @@ -CC = gcc -clientsrc = $(wildcard src/*.c) \ - src/client/SilverMUDClient.c -clientobj = $(clientsrc:.c=.o) -serversrc = $(wildcard src/*.c) \ - src/server/SilverMUDServer.c -serverobj = $(serversrc:.c=.o) +# Compiler and linker flags needed to link to the needed libraries: CFLAGS = `pkg-config --cflags guile-3.0` -CLIENTLDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` -SERVERLDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` -SilverMUDClient: $(clientobj) - gcc -o $@ $^ $(CLIENTLDFLAGS) +LDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` -SilverMUDServer: $(serverobj) - gcc -o $@ $^ $(SERVERLDFLAGS) +# Files needed to compile the client: +clientsrc = $(wildcard src/*.c) src/client/SilverMUDClient.c +clientobj = $(clientsrc:.c=.o) -SilverMUDClientDebug: $(clientobj) - gcc -pg $^ $(CLIENTLDFLAGS) -o $@ - -SilverMUDServerDebug: $(serverobj) - gcc -pg $^ $(SERVERLDFLAGS) -o $@ - -.PHONY: clean -clean: - rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug +# Files needed to compile the server: +serversrc = $(wildcard src/*.c) src/server/SilverMUDServer.c +serverobj = $(serversrc:.c=.o) +# Default target: Compile the client and server with aggressive optimizations and a big stack of warnings: all: clean SilverMUDClient SilverMUDServer -all: CFLAGS += -Wall -Wextra -Ofast +all: CFLAGS += -Wall -Wextra -Ofast + +# Debug target: Compile the client and server with profiling, debug information, debug optimization, and the +# preprocessor flag "debug" set. debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug debug: clean SilverMUDClientDebug SilverMUDServerDebug + +SilverMUDClient: $(clientobj) + cc $^ $(LDFLAGS) -o $@ + +SilverMUDServer: $(serverobj) + cc $^ $(LDFLAGS) -o $@ + +SilverMUDClientDebug: $(clientobj) + cc -pg $^ $(LDFLAGS) -o $@ + +SilverMUDServerDebug: $(serverobj) + cc -pg $^ $(LDFLAGS) -o $@ + +# Start from a clean slate: +.PHONY: clean +clean: + rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug gmon.out + From c9e19364d7ef1fbe0e743d90c590996059098778 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 11:56:57 +0100 Subject: [PATCH 22/39] Switched from _exit to exit in the sigintHandler. Not sure why I ended up with _exit. Perhaps an unlucky keypress, but it's the incorrect function to use in this case. --- src/server/SilverMUDServer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index 057a2cd..d67b181 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -32,7 +32,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) From 3419ef18049deaac81e26ac78eb2dc2fd68e2dbb Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 13:27:54 +0100 Subject: [PATCH 23/39] Added scheme_create_area and scheme_create_path. --- src/schemeintegration.c | 65 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 047f3b3..7643b97 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -14,13 +14,74 @@ SCM scheme_create_skill(SCM string, SCM skilllist) return SCM_BOOL_T; } +// Create a new area and add it to the list, given a name and area description, and an area list from Scheme. +// Returns the index of the new area: +SCM scheme_create_area(SCM area_name, SCM area_description, SCM area_list) +{ + // Check if the area list exists: + list * areaList = scm_to_pointer(area_list); + if (areaList == NULL || areaList->type != AREA) + { + return SCM_BOOL_F; + } + + // Turn the SCM values into C strings that we can use: + char * areaName = scm_to_locale_stringn(area_name, NULL); + char * areaDescription= scm_to_locale_stringn(area_description, NULL); + + // Create and add the area to the area list: + addToList(areaList, createArea(areaName, areaDescription), AREA); + + // The new index of the area will be at the end of the list. Consider returning a pointer to the area: + size_t areaIndex = areaList->itemCount - 1; + + // Free the strings: + free(areaName); + free(areaDescription); + + // Return the area index to Scheme for further lispy hacking: + return scm_from_size_t(areaIndex); +} + +// Create a one way path between two areas from scheme: +SCM scheme_create_path(SCM path_name, SCM from_area_index, SCM to_area_index, SCM area_list) +{ + // Check if the area list exists: + list * areaList = scm_to_pointer(area_list); + if (areaList == NULL || areaList->type != AREA) + { + return SCM_BOOL_F; + } + + // Check if the areas exist: + playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; + playerArea * toArea = getFromList(areaList, scm_to_size_t(to_area_index))->area; + + if (fromArea == NULL || toArea == NULL) + { + return SCM_BOOL_F; + } + + // Turn the SCM value into a C string that we can use: + char * pathName = scm_to_locale_stringn(path_name, NULL); + + // Create the path: + createOneWayPath(fromArea, toArea, pathName); + + // Free the string: + free(pathName); + + // Return true to Scheme: + return SCM_BOOL_T; +} + // Change the name of an existing area in a list, given the number of the area in the list, from Scheme: SCM scheme_change_area_name(SCM new_name, SCM area_number, SCM area_list) { // Check if the area exists: list * areaList = scm_to_pointer(area_list); size_t areaNumber = scm_to_size_t(area_number); - + if (areaList->type != AREA) { return SCM_BOOL_F; @@ -131,6 +192,8 @@ void * schemeHandler(void * parameters) scm_init_guile(); // Register the various functions: + scm_c_define_gsubr("create-area", 3, 0, 0, &scheme_create_area); + scm_c_define_gsubr("create-path", 4, 0, 0, &scheme_create_path); scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); scm_c_define_gsubr("change-area-name", 3, 0, 0, &scheme_change_area_name); From 701335c2360815a35fe9969e54687d8d7da6cdac Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 14:41:44 +0100 Subject: [PATCH 24/39] Squashed version of: Altered some header files to prevent cycles confusing LSP Switched to correct ncurses function for just printing a string. Make cleaning manual in the makefile. It's getting bigger. Squash the warning about strncpy. Added LSP shtuff to the gitignore --- .gitignore | 6 +++++- Makefile | 7 ++++--- src/areadata.h | 1 - src/client/SilverMUDClient.c | 4 ++-- src/gamelogic.h | 6 ++++-- src/inputoutput.h | 4 +--- src/linkedlist.h | 1 - src/queue.h | 6 +++--- src/server/SilverMUDServer.c | 3 ++- 9 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 3652a69..e49f673 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,8 @@ SilverMUDClientDebug SilverMUDServerDebug # Profiling Artifacts: -gmon.out \ No newline at end of file +gmon.out + +# LSP whatnot: +.cache +compile_commands.json \ No newline at end of file diff --git a/Makefile b/Makefile index a656190..3ae32bf 100644 --- a/Makefile +++ b/Makefile @@ -11,13 +11,14 @@ serversrc = $(wildcard src/*.c) src/server/SilverMUDServer.c serverobj = $(serversrc:.c=.o) # Default target: Compile the client and server with aggressive optimizations and a big stack of warnings: -all: clean SilverMUDClient SilverMUDServer all: CFLAGS += -Wall -Wextra -Ofast +all: SilverMUDClient SilverMUDServer + # Debug target: Compile the client and server with profiling, debug information, debug optimization, and the # preprocessor flag "debug" set. debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug -debug: clean SilverMUDClientDebug SilverMUDServerDebug +debug: SilverMUDClientDebug SilverMUDServerDebug SilverMUDClient: $(clientobj) cc $^ $(LDFLAGS) -o $@ @@ -29,7 +30,7 @@ SilverMUDClientDebug: $(clientobj) cc -pg $^ $(LDFLAGS) -o $@ SilverMUDServerDebug: $(serverobj) - cc -pg $^ $(LDFLAGS) -o $@ + cc -pg $^ $(LDFLAGS) -o @$ # Start from a clean slate: .PHONY: clean diff --git a/src/areadata.h b/src/areadata.h index 26356fe..ef58b36 100644 --- a/src/areadata.h +++ b/src/areadata.h @@ -3,7 +3,6 @@ #ifndef AREADATA_H #define AREADATA_H #include "constants.h" -#include "linkedlist.h" // ==================== // -=[ Area/Paths: ]=-: diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c index a011f51..c2cf663 100644 --- a/src/client/SilverMUDClient.c +++ b/src/client/SilverMUDClient.c @@ -54,10 +54,10 @@ void * messageSender(void * parameters) { usleep(200000); // Clear the window: - wprintw(window, "\n\n\n"); + waddstr(window, "\n\n\n"); // Print the prompt: - wprintw(window, prompt); + waddstr(window, prompt); if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR) { diff --git a/src/gamelogic.h b/src/gamelogic.h index 2fd9b8d..f36b749 100644 --- a/src/gamelogic.h +++ b/src/gamelogic.h @@ -2,11 +2,13 @@ // Barry Kane, 2022. #ifndef GAMELOGIC_H #define GAMELOGIC_H -#include "queue.h" #include "areadata.h" #include "constants.h" #include "playerdata.h" -#include "inputoutput.h" + +// Forward-declare some data structures to prevent cyclic dependencies: +typedef struct queue queue; +typedef struct inputMessage inputMessage; // ======================== // -=[ Data Structures ]=-: diff --git a/src/inputoutput.h b/src/inputoutput.h index c7d6fc0..93a1cbb 100644 --- a/src/inputoutput.h +++ b/src/inputoutput.h @@ -7,12 +7,10 @@ #include #include #include - -#include "queue.h" #include "constants.h" #include "playerdata.h" -// Let the compiler know there will be structs defined elsewhere: +// Forward-declare some data structures to prevent cyclic dependencies: typedef struct queue queue; // ======================== diff --git a/src/linkedlist.h b/src/linkedlist.h index b43e9ba..3ac3940 100644 --- a/src/linkedlist.h +++ b/src/linkedlist.h @@ -3,7 +3,6 @@ #ifndef LINKEDLIST_H #define LINKEDLIST_H #include "areadata.h" -#include "playerdata.h" // Let the compiler know there will be structs defined elsewhere: typedef struct playerPath playerPath; diff --git a/src/queue.h b/src/queue.h index 3217698..616794c 100644 --- a/src/queue.h +++ b/src/queue.h @@ -5,13 +5,13 @@ #include "gamelogic.h" #include "inputoutput.h" +// Forward-declare some data structures to prevent cyclic dependencies: +typedef struct queue queue; + // ======================== // -=[ Data Structures ]=-: // ======================== -// Let the compiler know there will be structs defined elsewhere: -typedef struct queue queue; - // An enum which is used to state what type of data is being stored in a queueNode: typedef enum queueDataType { diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index d67b181..c921de2 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -64,7 +64,8 @@ int main(int argc, char ** argv) } case 'm': { - strncpy(motd, optarg, strlen(optarg) + 1); + strncpy(motd, optarg, 2047); + motd[2047] = '\0'; break; } } From 7b266bfc51bd89164fc59f14e3403aa69ab0f060 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Tue, 11 Apr 2023 23:41:28 +0100 Subject: [PATCH 25/39] Enable readline for Guile and enable Guile for the game logic thread --- src/gamelogic.c | 2 ++ src/schemeintegration.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gamelogic.c b/src/gamelogic.c index 421ebcd..3195147 100644 --- a/src/gamelogic.c +++ b/src/gamelogic.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "queue.h" #include "constants.h" #include "gamelogic.h" @@ -22,6 +23,7 @@ void * gameLogicHandler(void * parameters) gameLogicParameters * threadParameters = parameters; inputMessage * currentInput = NULL; queue * commandQueue = createQueue(); + scm_init_guile(); while (true) { // Evaluate remaining commands: diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 7643b97..63c9312 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -54,7 +54,7 @@ SCM scheme_create_path(SCM path_name, SCM from_area_index, SCM to_area_index, SC } // Check if the areas exist: - playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; +1 playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; playerArea * toArea = getFromList(areaList, scm_to_size_t(to_area_index))->area; if (fromArea == NULL || toArea == NULL) @@ -204,6 +204,9 @@ void * schemeHandler(void * parameters) scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL)); + // Enable readline support: + scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); + // Drop into the Scheme interpreter: scm_shell(0, NULL); From 35b3e939f1ffd79a36bd47d56a338e9050f83f0d Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Wed, 12 Apr 2023 22:24:38 +0100 Subject: [PATCH 26/39] Fixed typo that causes the compile to fail. --- src/schemeintegration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 63c9312..6a87308 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -54,7 +54,7 @@ SCM scheme_create_path(SCM path_name, SCM from_area_index, SCM to_area_index, SC } // Check if the areas exist: -1 playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; + playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; playerArea * toArea = getFromList(areaList, scm_to_size_t(to_area_index))->area; if (fromArea == NULL || toArea == NULL) From b3ef4c1bb9a2b6fa5c925aa904d278b77c1ee291 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Wed, 12 Apr 2023 23:29:57 +0100 Subject: [PATCH 27/39] Added a basic-functions scheme file. I'll need a consistent way to load these files. Perhaps I actually need a proper build system now. Autotools, here we come? --- scheme/basic-functions.scm | 4 ++++ src/schemeintegration.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 scheme/basic-functions.scm diff --git a/scheme/basic-functions.scm b/scheme/basic-functions.scm new file mode 100644 index 0000000..27f3c7c --- /dev/null +++ b/scheme/basic-functions.scm @@ -0,0 +1,4 @@ +(define (create-two-way-path from-path-description to-path-description from-area to-area arealist) + (begin + (create-path from-path-description from-area to-area arealist) + (create-path to-path-description to-area from-area arealist))) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 6a87308..30bcac8 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -206,7 +206,7 @@ void * schemeHandler(void * parameters) // Enable readline support: scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - + // Drop into the Scheme interpreter: scm_shell(0, NULL); From c032aa7dad8080dc0af08ccbdb99155b37d5c7d7 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 02:25:43 +0100 Subject: [PATCH 28/39] Fix a segfault that happens when a client fails a handshake. --- src/server/SilverMUDServer.c | 58 +++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index c921de2..27dec85 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -40,7 +40,7 @@ int main(int argc, char ** argv) time_t currentTime; unsigned delay = 800; int socketFileDesc, connectionFileDesc, length, clientsAmount, - socketCheck, activityCheck, returnVal; + socketCheck, activityCheck; fd_set connectedClients; pthread_t gameLogicThread, outputThread, schemeThread; int clientSockets[PLAYERCOUNT]; @@ -280,36 +280,46 @@ int main(int argc, char ** argv) } // See if we can put in the client: for (int index = 0; index < PLAYERCOUNT; index++) - { + { + printf("Checking for slot: %d\n", index); // When there is an empty slot, pop it in: if (clientSockets[index] == 0) - { - clientSockets[index] = connectionFileDesc; - //printf("Adding to list of sockets as %d.\n", index); + { + volatile int handshakeReturnValue = 0; + clientSockets[index] = connectionFileDesc; gnutls_transport_set_int(tlssessions[index], clientSockets[index]); do { - returnVal = gnutls_handshake(tlssessions[index]); + handshakeReturnValue = gnutls_handshake(tlssessions[index]); + } while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0); + + // If it's good, send them the welcome message: + if(handshakeReturnValue == 0) + { + // Send a greeting message: + memcpy(sendBuffer.senderName, "\0 Login > \0", 11); + strcpy(sendBuffer.messageContent, "Welcome to the server!"); + messageSend(tlssessions[index], &sendBuffer); + strcpy(receiveBuffer.messageContent, "/look"); + + // Allocate the memory for a new input message: + inputMessage * newMessage = malloc(sizeof(inputMessage)); + newMessage->content = malloc(sizeof(userMessage)); + + // Copy in the correct data: + memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage)); + newMessage->sender = &connectedPlayers[index]; + + // Push the new message onto the queue: + pushQueue(inputQueue, newMessage, INPUT_MESSAGE); + break; } - while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0); - - // Send a greeting message: - memcpy(sendBuffer.senderName, "\0 Login > \0", 11); - strcpy(sendBuffer.messageContent, "Welcome to the server!"); - messageSend(tlssessions[index], &sendBuffer); - strcpy(receiveBuffer.messageContent, "/look"); - // Allocate the memory for a new input message: - inputMessage * newMessage = malloc(sizeof(inputMessage)); - newMessage->content = malloc(sizeof(userMessage)); - - // Copy in the correct data: - memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage)); - newMessage->sender = &connectedPlayers[index]; - - // Push the new message onto the queue: - pushQueue(inputQueue, newMessage, INPUT_MESSAGE); - break; + // If it's not good, close it, we don't want it: + shutdown(clientSockets[index], 2); + close(clientSockets[index]); + clientSockets[index] = 0; + break; } } } From f1673754bf0bc73aef16240e087cbbb4c51ac830 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 02:26:12 +0100 Subject: [PATCH 29/39] Fix a typo in the makefile. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3ae32bf..c3323ca 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ SilverMUDClientDebug: $(clientobj) cc -pg $^ $(LDFLAGS) -o $@ SilverMUDServerDebug: $(serverobj) - cc -pg $^ $(LDFLAGS) -o @$ + cc -pg $^ $(LDFLAGS) -o $@ # Start from a clean slate: .PHONY: clean From 7118d8e3d780ceae93f3e67bc5e5e7b15d7965c3 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 02:26:36 +0100 Subject: [PATCH 30/39] Add "shout" as an example of a scheme procedure. --- scheme/basic-functions.scm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scheme/basic-functions.scm b/scheme/basic-functions.scm index 27f3c7c..9b8a8d4 100644 --- a/scheme/basic-functions.scm +++ b/scheme/basic-functions.scm @@ -2,3 +2,6 @@ (begin (create-path from-path-description from-area to-area arealist) (create-path to-path-description to-area from-area arealist))) + +(define (shout message outputqueue) + (message-everyone "SERVER" message outputqueue)) From a9c600219fb99d23fc7f51e75c41d4c6ba90e7d4 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 02:42:22 +0100 Subject: [PATCH 31/39] Replaced the Guile REPL with a Guile REPL server for Emacs glory. --- src/schemeintegration.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index 30bcac8..fe62496 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -206,9 +206,9 @@ void * schemeHandler(void * parameters) // Enable readline support: scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - + scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))(run-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); // Drop into the Scheme interpreter: - scm_shell(0, NULL); +// scm_shell(0, NULL); // Return NULL. This should be unreachable. return NULL; From 70bdb879bf8769be7b3ee4477f7aef0f4aec6645 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 02:51:06 +0100 Subject: [PATCH 32/39] Remove debugging message. --- src/server/SilverMUDServer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c index 27dec85..c861cf7 100644 --- a/src/server/SilverMUDServer.c +++ b/src/server/SilverMUDServer.c @@ -281,7 +281,6 @@ int main(int argc, char ** argv) // See if we can put in the client: for (int index = 0; index < PLAYERCOUNT; index++) { - printf("Checking for slot: %d\n", index); // When there is an empty slot, pop it in: if (clientSockets[index] == 0) { From db00fc94a8f90866848ac4678d31339f49fa957a Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 13 Apr 2023 03:00:28 +0100 Subject: [PATCH 33/39] Why not both a REPL and a REPL server? --- src/schemeintegration.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index fe62496..ed95306 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -206,9 +206,10 @@ void * schemeHandler(void * parameters) // Enable readline support: scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))(run-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); - // Drop into the Scheme interpreter: -// scm_shell(0, NULL); + scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); + +// Drop into the Scheme interpreter: + scm_shell(0, NULL); // Return NULL. This should be unreachable. return NULL; From adfba9afe435c2bf9485bba697d94ed542f4ecb2 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 14 Apr 2023 21:34:27 +0100 Subject: [PATCH 34/39] Documented /shout in Player's Guide --- README.org | 1 + 1 file changed, 1 insertion(+) diff --git a/README.org b/README.org index f0f1dab..ca7c25c 100644 --- a/README.org +++ b/README.org @@ -31,6 +31,7 @@ can be upper or lower-case. | SPEC | Core Stat Name | Allows you to apply spec points to a given stat. | | TRY | Core Stat Name/Skill Name, Object Number | Attempt to use the given stat or skill on the object. | | TALK | Character Name | Begins a conversation with another player. | +| SHOUT | None | Messages the current area, ignoring any conversations. | * Gamemaster's Guide ** Running the Server: From fde867d25be15d342d2084c122c4a19b20f83623 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 14 Apr 2023 21:35:38 +0100 Subject: [PATCH 35/39] Noted that GNU Guile is now required to build SilverMUD. --- README.org | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index ca7c25c..e3e006a 100644 --- a/README.org +++ b/README.org @@ -41,7 +41,8 @@ can be upper or lower-case. SilverMUD has the following dependencies: - GnuTLS - ncurses - +- GNU Guile + ** C Style Guide: These rules attempt to make the program as visually clear as possible, while some rules may be made based on my own personal tastes. From 191cf8fcdb10b6f18f93340c967b1214a4f529dc Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 14 Apr 2023 21:53:29 +0100 Subject: [PATCH 36/39] Improved connection tutorial --- README.org | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.org b/README.org index e3e006a..a22c9a8 100644 --- a/README.org +++ b/README.org @@ -8,10 +8,18 @@ easy-to-understand structures. * Player's Guide ** Running The Client *** How To Connect To A Server: +#+begin_example +SilverMUDClient -i +#+end_example To connect to a server, use the command-line option =-i=, and the IP address of -the server. If the server admin is hosting the server on a port other than the -default port of 5000, you can use the =-p= option with the number of the port. If -the connection is successful, you will be placed in the server's login +the server +#+begin_example +SilverMUDClient -i -p +#+end_example +If the server admin is hosting the server on a port other than the +default port of 5000, you can use the =-p= option with the number of the port. + +If the connection is successful, you will be placed in the server's login area. Type =/join =, where player name is a name of your choosing, and you will be placed in the spawn area, ready to play. @@ -47,9 +55,11 @@ SilverMUD has the following dependencies: These rules attempt to make the program as visually clear as possible, while some rules may be made based on my own personal tastes. +For the purposes of this guide, we will use this terminology: - () :: These are parentheses. - [] :: These are brackets. - {} :: These are braces. + *** Formatting: **** Casing: - Variables :: Variables should be in camelCase, with the exception being From f488a4ac7c641d7cf4e47a015ac116720eb69df2 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 15 Apr 2023 23:46:06 +0100 Subject: [PATCH 37/39] Add a comment explaining what shout does. --- scheme/basic-functions.scm | 5 +++-- src/schemeintegration.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scheme/basic-functions.scm b/scheme/basic-functions.scm index 9b8a8d4..fcdd45c 100644 --- a/scheme/basic-functions.scm +++ b/scheme/basic-functions.scm @@ -3,5 +3,6 @@ (create-path from-path-description from-area to-area arealist) (create-path to-path-description to-area from-area arealist))) -(define (shout message outputqueue) - (message-everyone "SERVER" message outputqueue)) +;; Send a quick message to everyone in the game as the server: +(define (shout message) + (message-everyone "SERVER" message output-queue)) diff --git a/src/schemeintegration.c b/src/schemeintegration.c index ed95306..f915eca 100644 --- a/src/schemeintegration.c +++ b/src/schemeintegration.c @@ -206,9 +206,9 @@ void * schemeHandler(void * parameters) // Enable readline support: scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); + scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\")) (spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); -// Drop into the Scheme interpreter: + // Drop into the Scheme interpreter: scm_shell(0, NULL); // Return NULL. This should be unreachable. From fe444ac8c8cbba18830dcf59cf01ac3b4b449806 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 15 Apr 2023 23:46:56 +0100 Subject: [PATCH 38/39] Added a docstring explaining what shout does. --- scheme/basic-functions.scm | 1 + 1 file changed, 1 insertion(+) diff --git a/scheme/basic-functions.scm b/scheme/basic-functions.scm index fcdd45c..74923d3 100644 --- a/scheme/basic-functions.scm +++ b/scheme/basic-functions.scm @@ -5,4 +5,5 @@ ;; Send a quick message to everyone in the game as the server: (define (shout message) + "Send a quick message to everyone in the game as the server." (message-everyone "SERVER" message output-queue)) From 2059aa0b21cba64006f83a5bf544011a3b3e8394 Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sun, 30 Jul 2023 22:18:23 +0100 Subject: [PATCH 39/39] Starting again, see old branch for old codebase. --- .gitignore | 15 - LICENSE | 661 ------------------------ Makefile | 39 -- README.org | 111 ---- scheme/basic-functions.scm | 9 - src/areadata.c | 72 --- src/areadata.h | 49 -- src/client/SilverMUDClient.c | 382 -------------- src/constants.h | 9 - src/gamelogic.c | 968 ----------------------------------- src/gamelogic.h | 86 ---- src/inputoutput.c | 173 ------- src/inputoutput.h | 73 --- src/linkedlist.c | 480 ----------------- src/linkedlist.h | 84 --- src/playerdata.c | 225 -------- src/playerdata.h | 84 --- src/queue.c | 220 -------- src/queue.h | 71 --- src/schemeintegration.c | 217 -------- src/schemeintegration.h | 19 - src/server/SilverMUDServer.c | 378 -------------- src/texteffects.c | 121 ----- src/texteffects.h | 38 -- tests/list-test.c | 46 -- tests/queue-test.c | 132 ----- 26 files changed, 4762 deletions(-) delete mode 100644 .gitignore delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 README.org delete mode 100644 scheme/basic-functions.scm delete mode 100644 src/areadata.c delete mode 100644 src/areadata.h delete mode 100644 src/client/SilverMUDClient.c delete mode 100644 src/constants.h delete mode 100644 src/gamelogic.c delete mode 100644 src/gamelogic.h delete mode 100644 src/inputoutput.c delete mode 100644 src/inputoutput.h delete mode 100644 src/linkedlist.c delete mode 100644 src/linkedlist.h delete mode 100644 src/playerdata.c delete mode 100644 src/playerdata.h delete mode 100644 src/queue.c delete mode 100644 src/queue.h delete mode 100644 src/schemeintegration.c delete mode 100644 src/schemeintegration.h delete mode 100644 src/server/SilverMUDServer.c delete mode 100644 src/texteffects.c delete mode 100644 src/texteffects.h delete mode 100644 tests/list-test.c delete mode 100644 tests/queue-test.c diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e49f673..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# Object files: -*.o - -# Binaries: -SilverMUDClient -SilverMUDServer -SilverMUDClientDebug -SilverMUDServerDebug - -# Profiling Artifacts: -gmon.out - -# LSP whatnot: -.cache -compile_commands.json \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index be3f7b2..0000000 --- a/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/Makefile b/Makefile deleted file mode 100644 index c3323ca..0000000 --- a/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# Compiler and linker flags needed to link to the needed libraries: -CFLAGS = `pkg-config --cflags guile-3.0` -LDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0` - -# Files needed to compile the client: -clientsrc = $(wildcard src/*.c) src/client/SilverMUDClient.c -clientobj = $(clientsrc:.c=.o) - -# Files needed to compile the server: -serversrc = $(wildcard src/*.c) src/server/SilverMUDServer.c -serverobj = $(serversrc:.c=.o) - -# Default target: Compile the client and server with aggressive optimizations and a big stack of warnings: -all: CFLAGS += -Wall -Wextra -Ofast -all: SilverMUDClient SilverMUDServer - - -# Debug target: Compile the client and server with profiling, debug information, debug optimization, and the -# preprocessor flag "debug" set. -debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug -debug: SilverMUDClientDebug SilverMUDServerDebug - -SilverMUDClient: $(clientobj) - cc $^ $(LDFLAGS) -o $@ - -SilverMUDServer: $(serverobj) - cc $^ $(LDFLAGS) -o $@ - -SilverMUDClientDebug: $(clientobj) - cc -pg $^ $(LDFLAGS) -o $@ - -SilverMUDServerDebug: $(serverobj) - cc -pg $^ $(LDFLAGS) -o $@ - -# Start from a clean slate: -.PHONY: clean -clean: - rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug gmon.out - diff --git a/README.org b/README.org deleted file mode 100644 index a22c9a8..0000000 --- a/README.org +++ /dev/null @@ -1,111 +0,0 @@ -#+LATEX_HEADER: \RequirePackage[left=0.3in,top=0.3in,right=0.3in,bottom=0.3in, a4paper]{geometry} -* SilverMUD: The Hackable Terminal-Top Roleplaying Game. -SilverMUD is a tool for creating engaging and communal stories, all over the -world through the internet. It's designed to give a gamemaster the same power -to improvise that they have at the table, through simple programming and -easy-to-understand structures. - -* Player's Guide -** Running The Client -*** How To Connect To A Server: -#+begin_example -SilverMUDClient -i -#+end_example -To connect to a server, use the command-line option =-i=, and the IP address of -the server -#+begin_example -SilverMUDClient -i -p -#+end_example -If the server admin is hosting the server on a port other than the -default port of 5000, you can use the =-p= option with the number of the port. - -If the connection is successful, you will be placed in the server's login -area. Type =/join =, where player name is a name of your choosing, -and you will be placed in the spawn area, ready to play. - -*** Other Launch Options: - -** The Basic Commands -SilverMUD is played through a set of very simple commands. To use a command, -type a forward-slash (/) followed immediately by the command name. The command -can be upper or lower-case. - -| Command | Arguments | Effect | -|---------+------------------------------------------+---------------------------------------------------------| -| JOIN | Character Name | Logs you into the server with the given character name. | -| MOVE | Path Name/Path Number | Moves you down the given path. | -| LOOK | None | Describes the current area. | -| STAT | None | Displays your current status and character sheet. | -| SPEC | Core Stat Name | Allows you to apply spec points to a given stat. | -| TRY | Core Stat Name/Skill Name, Object Number | Attempt to use the given stat or skill on the object. | -| TALK | Character Name | Begins a conversation with another player. | -| SHOUT | None | Messages the current area, ignoring any conversations. | - -* Gamemaster's Guide -** Running the Server: - -* Developer's Guide -** Build Prerequisites: -SilverMUD has the following dependencies: -- GnuTLS -- ncurses -- GNU Guile - -** C Style Guide: -These rules attempt to make the program as visually clear as possible, while -some rules may be made based on my own personal tastes. - -For the purposes of this guide, we will use this terminology: -- () :: These are parentheses. -- [] :: These are brackets. -- {} :: These are braces. - -*** Formatting: -**** Casing: -- Variables :: Variables should be in camelCase, with the exception being - variables used to store SCM values, in which case, they should in be - snake_case. - -- Types :: User defined types should be in PascalCase. - -**** Control Statements: -- A space should be between the keyword and the condition. This is to make - control statements visually distinct from function calls. - -- Opening braces should be on the line after the control statement, and closing - braces on the line after the last statement, on it's own. This is to make the - scope of the control statement easily identifiable. - -- else and else if should always be on a new line, not the same line as an if - statement's closing brace. This is to more easily distinguish the seperate - blocks. - -- Control statements should never omit braces and do single statements. This is - mostly personal preference, but I do think it makes things more clear. - -*** Naming: -**** Rule 0: NEVER USE i AND j! -Never use the variable names i and j. These are easy to confuse, and often make -nested loops awful to read. Name these more descriptively. -For example: -- If you are using a variable to index an array, name the variable index. -- If you are indexing multiple arrays, name it "array name + Index". -- If you are using it to count something, call it count, or "name of the - thing you are counting + count". - -**** Rule 1: No one letter variable names, unless in a mathematical function. -You should never use one letter variable names. They're needlessly obtuse and -you will not remember their meaning upon re-reading of the source code. The -exception to this is when you are writing a function which replicates a -mathematical formula or function with commonly accepted notation. However, you -should consider if it would be better to break mathematical convention for -clarity inside the program, such as when the variable names are the first letter -of a word or the mathematical notation uses many similar looking variables. - -**** Rule 2: Prefer to use full words in variable and function names: -You should always prefer to use full words in variable and function names. It -makes the source code much easier to read, like a sentence. Ideally, if you want -to shorten the name, use synonyms or rephrasing before you resort to removing -letters. - -*** Comments: diff --git a/scheme/basic-functions.scm b/scheme/basic-functions.scm deleted file mode 100644 index 74923d3..0000000 --- a/scheme/basic-functions.scm +++ /dev/null @@ -1,9 +0,0 @@ -(define (create-two-way-path from-path-description to-path-description from-area to-area arealist) - (begin - (create-path from-path-description from-area to-area arealist) - (create-path to-path-description to-area from-area arealist))) - -;; Send a quick message to everyone in the game as the server: -(define (shout message) - "Send a quick message to everyone in the game as the server." - (message-everyone "SERVER" message output-queue)) diff --git a/src/areadata.c b/src/areadata.c deleted file mode 100644 index 50a61b9..0000000 --- a/src/areadata.c +++ /dev/null @@ -1,72 +0,0 @@ -// areadata.c: Implements functions for playerAreas and playerPaths in SilverMUD: -// Barra Ó Catháin, 2022. -#include -#include "areadata.h" -#include "playerdata.h" -#include "linkedlist.h" - -// ==================== -// -=[ Area/Paths: ]=-: -// ==================== - -// Create an area given a name and description: -playerArea * createArea(char * nameString, char * descriptionString) -{ - // Allocate and zero memory for the new area: - playerArea * createdArea = calloc(1, sizeof(playerArea)); - - // Copy the strings into the newly created area: - strncpy(createdArea->areaName, nameString, 32 - 1); - strncpy(createdArea->areaDescription, descriptionString, MAX - 36); - - // Properly null-terminate the strings: - createdArea->areaName[31] = '\0'; - createdArea->areaDescription[MAX - 36] = '\0'; - - // Create a list for the paths in the area: - createdArea->pathList = createList(PATH); - - // Return the pointer: - return createdArea; -} - -// Create a path between two areas given two areas and two strings: -int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription) -{ - // Allocate the new paths: - playerPath * fromPath = malloc(sizeof(playerPath)); - playerPath * toPath = malloc(sizeof(playerPath)); - - // Setup the from path: - strncpy(fromPath->pathName, fromDescription, 32 - 1); - fromPath->pathName[31] = '\0'; - fromPath->areaToJoin = toArea; - - // Setup the to path: - strncpy(toPath->pathName, toDescription, 32 - 1); - toPath->pathName[31] = '\0'; - toPath->areaToJoin = fromArea; - - // Add to the lists: - addToList(fromArea->pathList, fromPath, PATH); - addToList(toArea->pathList, toPath, PATH); - - return 0; -} - -// Create a one-way path between two areas given two areas and a string: -int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description) -{ - // Allocate the new paths: - playerPath * path = calloc(1, sizeof(playerPath)); - - // Setup the path: - strncpy(path->pathName, description, 32 - 1); - path->pathName[31] = '\0'; - path->areaToJoin = toArea; - - // Add to the list: - addToList(fromArea->pathList, path, PATH); - - return 0; -} diff --git a/src/areadata.h b/src/areadata.h deleted file mode 100644 index ef58b36..0000000 --- a/src/areadata.h +++ /dev/null @@ -1,49 +0,0 @@ -// areadata.h: Contains data structures and functions for playerAreas and playerPaths in SilverMUD: -// Barra Ó Catháin, 2022. -#ifndef AREADATA_H -#define AREADATA_H -#include "constants.h" - -// ==================== -// -=[ Area/Paths: ]=-: -// ==================== - -// Let the compiler know that we're going to define these types: -typedef struct playerPath playerPath; -typedef struct playerArea playerArea; -typedef struct list list; - -// A path, which contains a name, and a pointer to the area which the player will travel to: -struct playerPath -{ - char pathName[32]; - playerArea * areaToJoin; -}; - -// An area, containing a list of paths exiting from the area, and a name and description of the area: -struct playerArea -{ - list * pathList; - char areaName[32]; - char areaDescription[MAX - 35]; -}; - -// Create an area given a name and description, returning a pointer to the new area: -playerArea * createArea(char * nameString, char * descriptionString); - -// Create a path between two areas given two areas and two strings, adding it to the both area's list of paths: -int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription); - -// Create a one-way path between two areas given two areas and a string, adding it to the first area's list of paths: -int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description); - -// TO BE IMPLEMENTED: -/* int saveAreaList(areaNode * listToSave); */ - -/* int savePathList(pathNode * listToSave); */ - -/* int loadAreaList(areaNode * listToLoad); */ - -/* int loadPathList(pathNode * listToLoad); */ - -#endif diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c deleted file mode 100644 index c2cf663..0000000 --- a/src/client/SilverMUDClient.c +++ /dev/null @@ -1,382 +0,0 @@ -// Silverkin Industries Comm-Link Client, Public Demonstration Sample Alpha 0.5. -// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance. -// Barry Kane, 2021 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../queue.h" -#include "../constants.h" -#include "../playerdata.h" -#include "../texteffects.h" -#include "../inputoutput.h" - -// A struct for bundling all needed parameters for a thread so we can pass them using a void pointer: -typedef struct threadparameters -{ - gnutls_session_t tlsSession; - FILE * loggingStream; - bool loggingFlag; - WINDOW * window; - int characterDelay; - char * prompt; -} threadparameters; - -// Use sockaddr as a type: -typedef struct sockaddr sockaddr; - -// A globally available exit boolean: -bool shouldExit = false; - -// A function for managing the sending thread: -void * messageSender(void * parameters) -{ - threadparameters * threadParameters = parameters; - gnutls_session_t tlsSession = threadParameters->tlsSession; - FILE * loggingStream = threadParameters->loggingStream; - bool loggingFlag = threadParameters->loggingFlag; - WINDOW * window = threadParameters->window; - char * prompt = threadParameters->prompt; - userMessage sendBuffer; - - // Repeatedly get input from the user, place it in a userMessage, and send it to the server: - while (!shouldExit) - { - usleep(200000); - // Clear the window: - waddstr(window, "\n\n\n"); - - // Print the prompt: - waddstr(window, prompt); - - if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR) - { - // Quit if there's any funny business with getting input: - pthread_exit(NULL); - } - - // Ignore empty messages: - if (sendBuffer.messageContent[0] == '\n') - { - continue; - } - - // Send the message to the log if logging is enabled: - if (loggingFlag == true) - { - fputs(sendBuffer.messageContent, loggingStream); - fputs("\n", loggingStream); - fflush(loggingStream); - } - - // Send the message off to the server: - messageSend(tlsSession, &sendBuffer); - memset(&sendBuffer, 0, sizeof(char) * MAX); - } - - // Rejoin the main thread: - pthread_exit(NULL); -} - -// A function for managing the receiving thread: -void * messageReceiver(void * parameters) -{ - threadparameters * threadParameters = parameters; - gnutls_session_t tlsSession = threadParameters->tlsSession; - FILE * loggingStream = threadParameters->loggingStream; - int characterDelay = threadParameters->characterDelay; - bool loggingFlag = threadParameters->loggingFlag; - WINDOW * window = threadParameters->window; - - int returnValue = 0; - userMessage receiveBuffer; - bool serverMessage = false; - int screenWidth = getmaxx(threadParameters->window); - - // Repeatedly take messages from the server and print them to the chat log window: - while (!shouldExit) - { - // Get the next message: - returnValue = messageReceive(tlsSession, &receiveBuffer); - - // Check we haven't been disconnected: - if (returnValue == -10 || returnValue == 0) - { - shouldExit = true; - } - - // Check if it's a server message: - else if (receiveBuffer.senderName[0] == '\0') - { - // Check if the server wants to change the prompt: - if (receiveBuffer.senderName[1] != '\0') - { - strncpy(threadParameters->prompt, &receiveBuffer.senderName[1], 63); - threadParameters->prompt[63] = '\0'; - } - - // Check if it's a command to disconnect: - if (receiveBuffer.messageContent[0] == '\0') - { - shouldExit = true; - pthread_exit(NULL); - } - - // Fit the string to the screen: - wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1, screenWidth); - - // If it's the first server message in a block, begin a block of server messages: - if (serverMessage == false) - { - slowPrintNcurses("\n --====<>====--", characterDelay, window, true); - serverMessage = true; - } - - // Print the message: - slowPrintNcurses("\n", characterDelay, window, true); - slowPrintNcurses(receiveBuffer.messageContent, characterDelay, - window, false); - slowPrintNcurses("\n", characterDelay, window, true); - } - // It's a user message: - else - { - // Fit the string to the screen: - wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1, - screenWidth - strlen(receiveBuffer.senderName) - 2); - - // If the user has requested logging, insert the message into the file: - if (loggingFlag == true) - { - fputs(receiveBuffer.senderName, loggingStream); - fputs(": ", loggingStream); - fputs(receiveBuffer.messageContent, loggingStream); - fflush(loggingStream); - } - - // If we're in a block of server messages, end it: - if (serverMessage == true) - { - slowPrintNcurses("\n --====<>====-- \n", characterDelay, window, true); - serverMessage = false; - } - - // Print the message: - slowPrintNcurses(receiveBuffer.senderName, characterDelay, window, true); - slowPrintNcurses(": ", characterDelay, window, true); - slowPrintNcurses(receiveBuffer.messageContent, characterDelay, window, false); - } - } - // Exit the thread if shouldExit is true: - pthread_exit(NULL); -} - -int main(int argc, char ** argv) -{ - int socketFileDesc; - struct sockaddr_in serverAddress; - pthread_t sendingThread; - pthread_t receivingThread; - int port = 5000; - int currentopt = 0; - int characterDelay = 4000; - char chatLogPath[PATH_MAX + 1]; - char gameLogPath[PATH_MAX + 1]; - char ipAddress[32] = "127.0.0.1"; - FILE * chatLog = NULL, * gameLog = NULL; - bool chatLogging = false, gameLogging = false; - - // Print welcome message: - slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK CLIENT ====--\nVersion Alpha 0.5\n", 5000); - - // Parse command-line options: - while ((currentopt = getopt(argc, argv, "i:c:g:p:d:")) != -1) - { - switch (currentopt) - { - case 'i': - { - memcpy(ipAddress, optarg, 32); - break; - } - case 'c': - { - memcpy(chatLogPath, optarg, PATH_MAX + 1); - chatLog = fopen(chatLogPath, "a+"); - if (chatLog == NULL) - { - chatLogging = false; - } - else - { - chatLogging = true; - } - break; - } - case 'g': - { - memcpy(gameLogPath, optarg, PATH_MAX + 1); - gameLog = fopen(gameLogPath, "a+"); - if (gameLog == NULL) - { - gameLogging = false; - } - else - { - gameLogging = true; - } - break; - } - case 'p': - { - port = atoi(optarg); - break; - } - case 'd': - { - characterDelay = atoi(optarg); - break; - } - case '?': - { - return 1; - break; - } - } - } - - // Give me a socket, and make sure it's working: - socketFileDesc = socket(AF_INET, SOCK_STREAM, 0); - if (socketFileDesc == -1) - { - printf("Socket creation failed.\n"); - exit(EXIT_FAILURE); - } - else - { - slowPrint("Socket successfully created.\n", characterDelay); - } - - // Set our IP address and port. Default to localhost for testing: - serverAddress.sin_family = AF_INET; - serverAddress.sin_addr.s_addr = inet_addr(ipAddress); - serverAddress.sin_port = htons(port); - - // Connect the server and client sockets, Kronk: - if (connect(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress)) != 0) - { - slowPrint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", characterDelay); - exit(0); - } - - // Setup a GnuTLS session and initialize it: - gnutls_session_t tlsSession = NULL; - if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0) - { - exit(EXIT_FAILURE); - } - - // Setup the private credentials for our GnuTLS session: - gnutls_anon_client_credentials_t clientkey = NULL; - gnutls_anon_allocate_client_credentials(&clientkey); - gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientkey); - - // Bind the open socket to the TLS session: - gnutls_transport_set_int(tlsSession, socketFileDesc); - gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL); - - // Use the default for the GnuTLS handshake timeout: - gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - - // Repeatedly attempt to handshake unless we encounter a fatal error: - int returnValue = -1; - do - { - returnValue = gnutls_handshake(tlsSession); - } - while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0); - - // Setup Ncurses: - initscr(); - - // Create two pointers to structs to pass arguments to the threads: - threadparameters * logArea; - threadparameters * messageArea; - - logArea = malloc(sizeof(*logArea)); - messageArea = malloc(sizeof(*messageArea)); - - // Make the windows for the structs, and pass the socket descriptor: - logArea->window = newwin(LINES - 5, COLS - 2, 1, 1); - logArea->tlsSession = tlsSession; - logArea->loggingFlag = chatLogging; - logArea->characterDelay = characterDelay; - - if (chatLog != NULL) - { - logArea->loggingStream = chatLog; - } - messageArea->window = newwin(3, COLS - 2, LINES - 4, 1); - messageArea->tlsSession = tlsSession; - messageArea->loggingFlag = gameLogging; - - // Set the appropriate log pointers: - if (gameLog != NULL) - { - messageArea->loggingStream = gameLog; - } - - // Set up the string to hold the current "prompt" that the server has sent: - messageArea->prompt = calloc(32, sizeof(char)); - strcpy(messageArea->prompt, " Login > "); - logArea->prompt = messageArea->prompt; - - // Set the two windows to scroll: - scrollok(logArea->window, true); - scrollok(messageArea->window, true); - - // Run a thread to send messages, and use another to recieve: - pthread_create(&sendingThread, NULL, messageSender, messageArea); - pthread_create(&receivingThread, NULL, messageReceiver, logArea); - - // Wait for /EXIT: - pthread_join(receivingThread, NULL); - - // Close the threads: - pthread_cancel(sendingThread); - - // Close the session and socket: - gnutls_bye(tlsSession, GNUTLS_SHUT_WR); - close(socketFileDesc); - - // Free the structs: - free(logArea); - free(messageArea); - - // Close the log files: - if (gameLog != NULL) - { - fclose(gameLog); - } - if (chatLog != NULL) - { - fclose(chatLog); - } - - // Unsetup Ncurses: - endwin(); - - // Say goodbye: - slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", characterDelay); -} - diff --git a/src/constants.h b/src/constants.h deleted file mode 100644 index bb598d8..0000000 --- a/src/constants.h +++ /dev/null @@ -1,9 +0,0 @@ -// Constants.h: Contains configurable constants for SilverMUD. -// Barry Kane, 2022. -#ifndef CONSTANTS_H -#define CONSTANTS_H -#define PORT 5000 -#define MAX 2048 -#define PLAYERCOUNT 64 -#define MAXQUEUELENGTH 2048 -#endif diff --git a/src/gamelogic.c b/src/gamelogic.c deleted file mode 100644 index 3195147..0000000 --- a/src/gamelogic.c +++ /dev/null @@ -1,968 +0,0 @@ -// gamelogic.c: Contains function definitons for dealing with the game's logic. -// Barry Kane, 2022. -#include -#include -#include -#include -#include -#include -#include "queue.h" -#include "constants.h" -#include "gamelogic.h" -#include "playerdata.h" -#include "linkedlist.h" -#include "inputoutput.h" - -// ======================= -// -=[ Main Game Loop ]=-: -// ======================= - -// Thread function which runs the main game loop, given the needed parameters: -void * gameLogicHandler(void * parameters) -{ - gameLogicParameters * threadParameters = parameters; - inputMessage * currentInput = NULL; - queue * commandQueue = createQueue(); - scm_init_guile(); - while (true) - { - // Evaluate remaining commands: - while (commandQueue->itemCount != 0) - { - evaluateNextCommand(threadParameters, commandQueue); - } - - // Wait if there is nothing to do: - if (threadParameters->inputQueue->itemCount == 0) - { - pthread_cond_wait(&threadParameters->inputQueue->condition, &threadParameters->inputQueue->mutex); - } - - // Check for new messages and pop them off the queue: - if (threadParameters->inputQueue->itemCount != 0) - { - while (threadParameters->inputQueue->lock == true); - threadParameters->inputQueue->lock = true; - currentInput = peekQueue(threadParameters->inputQueue)->data.inputMessage; - userInputSanatize(currentInput->content->messageContent, MAX); - // A slash as the first character means the message is a user command: - if (currentInput->content->messageContent[0] == '/') - { - queueMessagedCommand(commandQueue, currentInput); - } - - else if (!(currentInput->sender->currentArea == getFromList(threadParameters->areaList, 0)->area) && - currentInput->content->messageContent[0] != '\n') - { - // Copy the correct name into the sender name field: - strncpy(currentInput->content->senderName, currentInput->sender->playerName, 32); - currentInput->content->senderName[31] = '\0'; - - if(currentInput->sender->talkingWith == NULL) - { - // Allocate an array of playerInfo to store the current players in the area for the output message: - playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*)); - - // Initialize them all to NULL: - for (int index = 0; index < PLAYERCOUNT; index++) - { - recipients[index] = NULL; - } - - // Get the players in the current area and add them to our array: - int recipientIndex = 0; - for (int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++) - { - if (threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea) - { - recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex]; - recipientIndex++; - } - } - - // Create the outputMessage for the queue: - outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); - - // Push the message onto the queue: - pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); - - // Free the array; - free(recipients); - } - else - { - // Allocate an array of two playerInfo to store the pointers to the players in the conversation: - playerInfo ** recipients = calloc(2, (sizeof(playerInfo*))); - - // Find which player is first in the player list: - bool senderIsFirst = false; - - for(int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++) - { - if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender) - { - senderIsFirst = true; - break; - } - if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender->talkingWith) - { - senderIsFirst = false; - break; - } - } - - // Set the proper recipients: - recipients[0] = (senderIsFirst) ? currentInput->sender : currentInput->sender->talkingWith; - recipients[1] = (senderIsFirst) ? currentInput->sender->talkingWith : currentInput->sender; - - // There's only one recipient: - int recipientIndex = 2; - - // Create the outputMessage for the queue: - outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex); - - // Push the message onto the queue: - pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE); - - // Free the array; - free(recipients); - } - } - memset(currentInput, 0, sizeof(inputMessage)); - currentInput = NULL; - threadParameters->inputQueue->lock = false; - popQueue(threadParameters->inputQueue); - } - } - pthread_exit(NULL); -} - -// Evaluate the next commandEvent in a queue: -void queueMessagedCommand(queue * queue, inputMessage * messageToQueue) -{ - // Prepare the new commandEvent: - commandEvent * newCommand = calloc(1, sizeof(commandEvent)); - newCommand->command = calloc(16, sizeof(char)); - newCommand->arguments = calloc(MAX, sizeof(char)); - newCommand->caller = messageToQueue->sender; - - // Seperate the command from it's arguments: - strtok(messageToQueue->content->messageContent, " "); - - // Copy the command and arguments to the new commandEvent: - memcpy(newCommand->command, &messageToQueue->content->messageContent[1], 16); - memcpy(newCommand->arguments, &messageToQueue->content->messageContent[strlen(newCommand->command) + 2], - MAX - (strlen(newCommand->command) + 2)); - - - // Ensure the arguments are safe to parse, without adding newlines: - userNameSanatize(newCommand->command, 16); - newCommand->command[15] = '\0'; - - userNameSanatize(newCommand->arguments, MAX); - newCommand->arguments[MAX - 1] = '\0'; - - // Lowercase the command for easier comparison: - for (char * character = newCommand->command; *character; ++character) - { - *character = tolower(*character); - } - - pushQueue(queue, newCommand, COMMAND); -} - -// Enqueue a command to a queue: -void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength, - playerInfo * callingPlayer) -{ - // Prepare the new commandEvent: - commandEvent * newCommand = calloc(1, sizeof(commandEvent)); - newCommand->command = calloc(16, sizeof(char)); - newCommand->arguments = calloc(MAX, sizeof(char)); - newCommand->caller = callingPlayer; - - // Copy the command and arguments: - strncpy(newCommand->command, command, commandLength); - if (argumentsLength > 0) - { - strncpy(newCommand->arguments, arguments, argumentsLength); - } - // Ensure the arguments are safe to parse, without adding newlines: - userNameSanatize(newCommand->command, 16); - - pushQueue(queue, newCommand, COMMAND); -} - -// Evaluate the next commandEvent in a queue: -int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) -{ - commandEvent * currentCommand = peekQueue(queue)->data.command; - while (queue->lock); - queue->lock = true; - if (currentCommand == NULL) - { - return -1; - } - - // Switch to the relevant command based on the hash: - switch (hashCommand(currentCommand->command, strlen(currentCommand->command))) - { - // Look command: Returns the description of the current area and paths: - case 5626697: - { - char formattedString[64]; - userMessage * lookMessage = calloc(1, sizeof(userMessage)); - lookMessage->senderName[0] = '\0'; - strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaName, 33); - strncat(lookMessage->messageContent, "\n", 2); - strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaDescription, MAX - 35); - - // Allocate an outputMessage for the queue: - outputMessage * lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - - //queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1); - memset(lookMessage, 0, sizeof(userMessage)); - - // Loop through the paths and send the appropriate amount of messages: - int charCount = 13; - strncat(lookMessage->messageContent, "You can go:", 13); - - if (currentCommand->caller->currentArea->pathList->itemCount > 0) - { - for(size_t index = 0; index < currentCommand->caller->currentArea->pathList->itemCount; index++) - { - if ((charCount + 64) >= MAX) - { - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - - memset(lookMessage, 0, sizeof(userMessage)); - charCount = 0; - } - snprintf(formattedString, 64, "\n\t%ld. %s", index + 1, - getFromList(currentCommand->caller->currentArea->pathList, index)->path->pathName); - strncat(lookMessage->messageContent, formattedString, 64); - charCount += 64; - } - // Allocate another outputMessage for the queue: - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - } - - // Clear the message: - memset(lookMessage, 0, sizeof(userMessage)); - if(currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area) - { - // Show the players in the area: - charCount = 23; - strncat(lookMessage->messageContent, "These players are here:", 24); - - int playerNumber = 1; - for(int index = 0; index < *(parameters->playerCount); index++) - { - if (parameters->connectedPlayers[index].currentArea == currentCommand->caller->currentArea) - { - if ((charCount + 38) >= MAX) - { - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - memset(lookMessage, 0, sizeof(userMessage)); - charCount = 0; - } - snprintf(formattedString, 45, "\n%02d. %31s", playerNumber++, - parameters->connectedPlayers[index].playerName); - strncat(lookMessage->messageContent, formattedString, 64); - charCount += 38; - } - } - // Allocate another outputMessage for the queue: - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - } - - free(lookMessage); - break; - } - - // Stat command: Displays the current character's sheet. - case 5987604: - { - char * formattedString = calloc(121, sizeof(char)); - userMessage * statMessage = calloc(1, sizeof(userMessage)); - statMessage->senderName[0] = '\0'; - // Basic status: Name, level, location. - snprintf(formattedString, 120, "%s, Level %d | %s\n", currentCommand->caller->playerName, - currentCommand->caller->stats->level, currentCommand->caller->currentArea->areaName); - strncat(statMessage->messageContent, formattedString, 120); - - // Current stats: Health and WISED. - snprintf(formattedString, 120, - "Health: %d/%d\nStats:\n\tWits: %2d | Intellect: %2d | Strength: %2d | Endurance: %2d | Dexerity: %2d \n", - currentCommand->caller->stats->currentHealth, currentCommand->caller->stats->maxHealth, - currentCommand->caller->stats->wits, currentCommand->caller->stats->intellect, - currentCommand->caller->stats->strength, currentCommand->caller->stats->endurance, - currentCommand->caller->stats->dexerity); - strncat(statMessage->messageContent, formattedString, 120); - - // Levelling stats: Current XP, and spec points. - if (currentCommand->caller->stats->specPoints > 0 || currentCommand->caller->stats->skillPoints > 0) - { - snprintf(formattedString, 120, "Current Experience: %ld | Spec Points Available: %d | Skill Points Available: %d", - currentCommand->caller->stats->experience, currentCommand->caller->stats->specPoints, currentCommand->caller->stats->skillPoints); - } - else - { - snprintf(formattedString, 120, "Current Experience: %ld", currentCommand->caller->stats->experience); - } - strncat(statMessage->messageContent, formattedString, 120); - - // Allocate an outputMessage for the queue: - outputMessage * statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - - memset(statMessage->messageContent, 0, sizeof(char) * MAX); - if (currentCommand->caller->skills->head != NULL) - { - size_t skillIndex = 0; - int charCount = 0; - bool addNewline = false; - playerSkill * skill; - while (skillIndex < currentCommand->caller->skills->itemCount) - { - skill = getFromList(currentCommand->caller->skills, skillIndex)->skill; - skillIndex++; - snprintf(formattedString, 120, "| %2d | %31s ", skill->skillPoints, skill->skillName); - charCount += 43; - strncat(statMessage->messageContent, formattedString, 120); - if ((charCount + 43) >= MAX) - { - // Allocate an outputMessage for the queue: - statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - memset(statMessage, 0, sizeof(userMessage)); - charCount = 0; - break; - } - else if (addNewline) - { - strncat(statMessage->messageContent, "|\n", 3); - charCount++; - addNewline = false; - } - else - { - addNewline = true; - } - } - // Allocate an outputMessage for the queue: - statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - } - free(statMessage); - free(formattedString); - - break; - } - - // Spec command: Assign spec points to stats: - case 5982259: - { - userMessage * specMessage = calloc(1, sizeof(userMessage)); - specMessage->senderName[0] = '\0'; - char * formattedString = calloc(121, sizeof(char)); - if (currentCommand->caller->stats->specPoints > 0) - { - int selectedAmount = 0; - strtok(currentCommand->arguments, " "); - selectedAmount = atoi(¤tCommand->arguments[strlen(currentCommand->arguments) + 1]); - coreStat selectedStat = getCoreStatFromString(currentCommand->arguments, 16); - if (selectedAmount > 0 && (currentCommand->caller->stats->specPoints - selectedAmount) >= 0) - { - switch (selectedStat) - { - case WITS: - { - currentCommand->caller->stats->wits += selectedAmount; - strncat(specMessage->messageContent, "Increased wits.", 16); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case INTELLECT: - { - currentCommand->caller->stats->intellect += selectedAmount; - strncat(specMessage->messageContent, "Increased intellect.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case STRENGTH: - { - currentCommand->caller->stats->strength += selectedAmount; - strncat(specMessage->messageContent, "Increased strength.", 20); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case ENDURANCE: - { - currentCommand->caller->stats->endurance += selectedAmount; - strncat(specMessage->messageContent, "Increased endurance.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case DEXERITY: - { - currentCommand->caller->stats->dexerity += selectedAmount; - strncat(specMessage->messageContent, "Increased dexerity.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case INVALID: - { - strncat(specMessage->messageContent, "Invalid stat.", 21); - } - } - } - else - { - strncat(specMessage->messageContent, "You have entered an invalid amount of spec points.", 51); - } - } - else - { - strncat(specMessage->messageContent, "You have no spec points available.", 35); - } - - // Allocate an outputMessage for the queue: - outputMessage * specOutputMessage = createTargetedOutputMessage(specMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, specOutputMessage, OUTPUT_MESSAGE); - - // Show the new stat sheet: - queue->lock = false; - queueCommand(queue, "stat", "", 5, 0, currentCommand->caller); - queue->lock = true; - - // Free the finished message: - free(specMessage); - free(formattedString); - - break; - } - - // Try command: Attempt to use a stat or skill on an object: - case 163143: - { - // Allocate the userMessage to send: - userMessage * tryMessage = calloc(1, (sizeof(userMessage))); - tryMessage->senderName[0] = '\0'; - - // Temporary message until we can implement objects, events, and challenges. - strcpy(tryMessage->messageContent, "The try command is currently not implemented. Implement it if you want to use it.\n"); - - // Allocate an outputMessage for the queue: - outputMessage * tryOutputMessage = createTargetedOutputMessage(tryMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, tryOutputMessage, OUTPUT_MESSAGE); - - // Free the userMessage: - free(tryMessage); - - break; - } - - // Move command: Moves the caller to a different area given a path name or number: - case 5677603: - { - char requestedPath[32]; - if (strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area) - { - memcpy(requestedPath, currentCommand->arguments, 32); - userNameSanatize(requestedPath, 32); - requestedPath[31] = '\0'; - if (movePlayerToArea(currentCommand->caller, requestedPath) == 0) - { - // Call the look command after moving. It's fine to unlock, because the loop won't - // continue until the command is queued: - queue->lock = false; - queueCommand(queue, "look", "", 5, 0, currentCommand->caller); - queue->lock = true; - } - } - - break; - } - - // Skill command: Allows you to put skill points into skills: - case 221096235: - { - userMessage * skillMessage = calloc(1, sizeof(userMessage)); - skillMessage->senderName[0] = '\0'; - if ((currentCommand->caller->stats->skillPoints - 1) >= 0) - { - int returnValue = takeSkill(parameters->globalSkillList, currentCommand->arguments, - strlen(currentCommand->arguments), currentCommand->caller); - switch(returnValue) - { - case -1: - { - strcpy(skillMessage->messageContent, "Not a valid skill."); - break; - } - case 0: - { - strcpy(skillMessage->messageContent, "Took "); - strcat(skillMessage->messageContent, currentCommand->arguments); - strcat(skillMessage->messageContent, "."); - currentCommand->caller->stats->skillPoints--; - break; - } - } - } - else - { - strcpy(skillMessage->messageContent, "You don't have enough skill points to take this skill.\n"); - } - - // Allocate an outputMessage for the queue: - outputMessage * skillOutputMessage = createTargetedOutputMessage(skillMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, skillOutputMessage, OUTPUT_MESSAGE); - - free(skillMessage); - break; - } - - // Listskills commands: List all available skills on the server: - case 2395990522: - { - userMessage * listMessage = calloc(1, sizeof(userMessage)); - char * formattedString = calloc(121, sizeof(char)); - int charCount = 0; - size_t skillIndex = 0; - bool addNewline = false; - playerSkill * currentSkill; - while (skillIndex < parameters->globalSkillList->itemCount) - { - currentSkill = getFromList(parameters->globalSkillList, skillIndex)->skill; - snprintf(formattedString, 120, "| %-31s ", currentSkill->skillName); - charCount += 43; - strncat(listMessage->messageContent, formattedString, 120); - if ((charCount + 46) >= MAX) - { - // Allocate an outputMessage for the queue: - outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE); - - memset(listMessage, 0, sizeof(userMessage)); - charCount = 0; - addNewline = false; - } - else if (addNewline) - { - strncat(listMessage->messageContent, "|\n", 3); - charCount++; - addNewline = false; - } - else - { - addNewline = true; - } - skillIndex++; - } - // Allocate an outputMessage for the queue: - outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE); - free(listMessage); - free(formattedString); - break; - } - // Shout command: Allows the player to talk to everyone in the area if they are in a conversation. - case 220952831: - { - // Allocate an array of playerInfo to store the current players in the area for the output message: - playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*)); - - // Initialize them all to NULL: - for (int index = 0; index < PLAYERCOUNT; index++) - { - recipients[index] = NULL; - } - - // Get the players in the current area and add them to our array: - int recipientIndex = 0; - for (int playerIndex = 0; playerIndex < *parameters->playerCount; playerIndex++) - { - if (parameters->connectedPlayers[playerIndex].currentArea == currentCommand->caller->currentArea) - { - recipients[recipientIndex] = ¶meters->connectedPlayers[playerIndex]; - recipientIndex++; - } - } - - // Create a userMessage to be filled with the data from the command's arguments and caller: - userMessage * shoutMessage = calloc(1, sizeof(userMessage)); - - // Copy in the data and terminate it: - strncpy(shoutMessage->senderName, currentCommand->caller->playerName, 32); - shoutMessage->senderName[31] = '\0'; - strncpy(shoutMessage->messageContent, currentCommand->arguments, MAX); - shoutMessage->messageContent[MAX - 1] = '\0'; - strncat(shoutMessage->messageContent, "\n", MAX); - - // Create the outputMessage for the queue: - outputMessage * shoutOutputMessage = createTargetedOutputMessage(shoutMessage, recipients, recipientIndex); - - // Push the message onto the output queue: - pushQueue(parameters->outputQueue, shoutOutputMessage, OUTPUT_MESSAGE); - - // Free the array: - free(recipients); - - break; - } - // Talk command: Allows the player to begin a chat session with another player: - case 6012644: - { - userMessage * talkMessage = calloc(1, sizeof(userMessage)); - talkMessage->senderName[0] = '\0'; - - // If there's no name specified, end the current chat sessions. - if (currentCommand->arguments[0] == '\0' || currentCommand->arguments == NULL) - { - currentCommand->caller->talkingWith = NULL; - strcpy(talkMessage->messageContent, "Conversation ended."); - strncat(&talkMessage->senderName[1], " > ", 4); - } - else - { - for(int playerIndex = 0; playerIndex < *parameters->playerCount; playerIndex++) - { - if(strncmp(currentCommand->arguments, parameters->connectedPlayers[playerIndex].playerName, 31) == 0) - { - currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]); - - // Fill out the message to inform the receiving user what is happening: - strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 32); - strcat(talkMessage->messageContent, " is talking to you."); - - playerInfo ** recipients = calloc(1, (sizeof(playerInfo*))); - recipients[0] = &(parameters->connectedPlayers[playerIndex]); - - // Allocate an outputMessage for the receiving user: - outputMessage * talkReceiverMessage = createTargetedOutputMessage(talkMessage, recipients, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, talkReceiverMessage, OUTPUT_MESSAGE); - - // Prep the message to the calling user. - memcpy(&talkMessage->senderName[1], currentCommand->arguments, sizeof(char) * 27); - strncat(&talkMessage->senderName[1], " > ", 4); - strcpy(talkMessage->messageContent, "Conversation begun with: "); - strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName); - } - } - } - if(talkMessage->messageContent[0] == '\0') - { - strcpy(talkMessage->messageContent, "There is no player by that name connected."); - } - - // Allocate an outputMessage for the queue: - outputMessage * talkOutputMessage = createTargetedOutputMessage(talkMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, talkOutputMessage, OUTPUT_MESSAGE); - - // Free the userMessage: - free(talkMessage); - - break; - } - - // Exit command: Sends an "empty" exit message to disconnect a client: - case 5284234: - { - // Allocate a userMessage containing null characters as the first char in both fields: - userMessage * exitMessage = calloc(1, (sizeof(userMessage))); - exitMessage->senderName[0] = '\0'; - exitMessage->messageContent[0] = '\0'; - - // Allocate an outputMessage for the queue: - outputMessage * exitOutputMessage = createTargetedOutputMessage(exitMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, exitOutputMessage, OUTPUT_MESSAGE); - - // Free the userMessage - free(exitMessage); - - break; - } - - // Join command: Allows the player to join the game given a name: - // TODO: Implement login/character creation. Will be a while: - case 5525172: - { - if (currentCommand->caller->currentArea == getFromList(parameters->areaList, 0)->area) - { - bool validName = true; - for(int index = 0; index < *parameters->playerCount; index++) - { - if (currentCommand->arguments[0] == '\0') - { - validName = false; - } - if (strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0) - { - validName = false; - } - } - if (validName) - { - strncpy(currentCommand->caller->playerName, currentCommand->arguments, 16); - currentCommand->caller->currentArea = getFromList(parameters->areaList, 1)->area; - - // Allocate a userMessage containing null characters as the first char in both fields: - userMessage * joinMessage = calloc(1, (sizeof(userMessage))); - memcpy(joinMessage->senderName, "\0 > \0", 5); - strcpy(joinMessage->messageContent, "Logged in successfully."); - - // Allocate an outputMessage for the queue: - outputMessage * joinOutputMessage = createTargetedOutputMessage(joinMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, joinOutputMessage, OUTPUT_MESSAGE); - - // Free the userMessage - free(joinMessage); - - // Call the look command after joining. It's fine to unlock, because the loop won't - // continue until the command is queued: - queue->lock = false; - queueCommand(queue, "look", "", 5, 0, currentCommand->caller); - queue->lock = true; - } - } - break; - } - } - - // Remove the current command and unlock the queue: - memset(currentCommand->command, 0, sizeof(char) * 16); - memset(currentCommand->arguments, 0, sizeof(char) * MAX); - currentCommand = NULL; - queue->lock = false; - popQueue(queue); - return 0; -} - -// Run a stat check for the given player, returning an outcome: -outcome statCheck(playerInfo * player, int chance, coreStat statToCheck) -{ - // Calculate the chance: - if (chance > 100 || chance < 0) - { - return ERROR; - } - chance = 100 - chance; - - // Calculate the modifier: - int modifier = 0; - switch(statToCheck) - { - case WITS: - { - modifier = player->stats->wits * 4; - break; - } - case INTELLECT: - { - modifier = player->stats->intellect * 4; - break; - } - case STRENGTH: - { - modifier = player->stats->strength * 4; - break; - } - case ENDURANCE: - { - modifier = player->stats->endurance * 4; - break; - } - case DEXERITY: - { - modifier = player->stats->dexerity * 4; - break; - } - default: - { - return ERROR; - } - } - int attempt = (rand() % 100) + modifier; - if (attempt >= chance) - { - if (attempt >= 98) - { - return CRITICAL_SUCCESS; - } - else - { - return SUCCESS; - } - } - else - { - if (attempt <= 2) - { - return CRITICAL_FAILURE; - } - else - { - return FAILURE; - } - } -} - -// Run a skill check for the given player, returning an outcome: -outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList) -{ - // Calculate the chance: - if (chance > 100 || chance < 0) - { - return ERROR; - } - chance = 100 - chance; - - // Check if the player has the given skill: - bool playerHasSkill = false; - size_t playerIndex = 0; - while (playerIndex < player->skills->itemCount) - { - if (strncmp(skillName, getFromList(player->skills, playerIndex)->skill->skillName, skillNameLength) != 0) - { - playerHasSkill = true; - break; - } - playerIndex++; - } - - // If the player doesn't have the skill, check if it's in the game and is trained: - bool trainedSkill = false; - size_t globalIndex = 0; - while (globalIndex < globalSkillList->itemCount) - { - if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) != 0) - { - trainedSkill = getFromList(globalSkillList, globalIndex)->skill->trainedSkill; - break; - } - globalIndex++; - } - - // Calculate the modifier: - int modifier = 0; - if (trainedSkill) - { - modifier = -100; - } - else if (playerHasSkill) - { - modifier = getFromList(player->skills, playerIndex)->skill->skillModifier * 4; - } - - // Attempt the check: - int attempt = (rand() % 100) + modifier; - if (attempt >= chance) - { - if (attempt >= 98) - { - return CRITICAL_SUCCESS; - } - else - { - return SUCCESS; - } - } - else - { - if (attempt <= 2) - { - return CRITICAL_FAILURE; - } - else - { - return FAILURE; - } - } -} - -// Move a player along a path in their current area: -int movePlayerToArea(playerInfo * player, char * requestedPath) -{ - // Check if a number was given first: - size_t selected = atoi(requestedPath); - if (selected != 0 && !(selected > player->currentArea->pathList->itemCount)) - { - if (getFromList(player->currentArea->pathList, selected - 1)->path != NULL && - getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin != NULL) - { - player->currentArea = getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin; - return 0; - } - else - { - return 1; - } - } - - // Otherwise search for the description: - for (size_t index = 0; index < player->currentArea->pathList->itemCount; index++) - { - if (strncmp(getFromList(player->currentArea->pathList, index)->path->pathName, - requestedPath, 32) == 0) - { - player->currentArea = getFromList(player->currentArea->pathList, index)->path->areaToJoin; - return 0; - } - } - return 1; -} - -// A hash function for distinguishing commands for the game logic handler: -unsigned int hashCommand(char * command, unsigned int commandLength) -{ - unsigned int hash = 0; - char * currentCharacter = command; - - for (unsigned int index = 0; index < commandLength && *currentCharacter != '\0'; currentCharacter++) - { - hash = 37 * hash + *currentCharacter; - } - - return hash; -} diff --git a/src/gamelogic.h b/src/gamelogic.h deleted file mode 100644 index f36b749..0000000 --- a/src/gamelogic.h +++ /dev/null @@ -1,86 +0,0 @@ -// gamelogic.h: Function prototypes and data-structures for dealing with game logic. -// Barry Kane, 2022. -#ifndef GAMELOGIC_H -#define GAMELOGIC_H -#include "areadata.h" -#include "constants.h" -#include "playerdata.h" - -// Forward-declare some data structures to prevent cyclic dependencies: -typedef struct queue queue; -typedef struct inputMessage inputMessage; - -// ======================== -// -=[ Data Structures ]=-: -// ======================== - -// An event for storing the information needed to evaluate a command: -typedef struct commandEvent commandEvent; -typedef struct commandEvent -{ - playerInfo * caller; - commandEvent * next; - char * command; - char * arguments; -} commandEvent; - -// A data-structure containing the needed parameters for the main game loop: -typedef struct gameLogicParameters -{ - // Players: - int * playerCount; - playerInfo * connectedPlayers; - - // Queues: - queue * inputQueue; - queue * outputQueue; - - // Lists: - list * areaList; - list * globalSkillList; -} gameLogicParameters; - -// ======================== -// -=[ Functions ]=-: -// ======================== - -// Thread function which runs the main game loop, given the needed parameters: -void * gameLogicHandler(void * parameters); - -// Enqueue a command that has been sent as a message from a user to a queue: -void queueMessagedCommand(queue * queue, inputMessage * messageToQueue); - -// Evaluate the next commandEvent in a queue: -int evaluateNextCommand(gameLogicParameters * parameters, queue * queue); - -// Enqueue a command to a queue: -void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength, - playerInfo * callingPlayer); - -// A hash function for distinguishing commands for the game logic handler: -unsigned int hashCommand(char * command, unsigned int commandLength); - -// ============================ -// -=[ Gameplay Primitives ]=-: -// ============================ - -// The possible outcomes of a check or challenge: -typedef enum outcome -{ - CRITICAL_FAILURE, - FAILURE, - SUCCESS, - CRITICAL_SUCCESS, - ERROR -} outcome; - -// Move a player along a path in their current area: -int movePlayerToArea(playerInfo * player, char * requestedPath); - -// Run a stat check for the given player, returning an outcome: -outcome statCheck(playerInfo * player, int chance, coreStat statToCheck); - -// Run a skill check for the given player, returning an outcome: -outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList); - -#endif diff --git a/src/inputoutput.c b/src/inputoutput.c deleted file mode 100644 index b8e4136..0000000 --- a/src/inputoutput.c +++ /dev/null @@ -1,173 +0,0 @@ -// inputoutput.c: Implementation of input/output library for SilverMUD. -// Barry Kane, 2022. -#include -#include -#include -#include -#include -#include -#include - -#include "queue.h" -#include "constants.h" -#include "playerdata.h" -#include "inputoutput.h" - -// Sends a message to a given TLS session, wraps the calls to gnutls_write: -int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend) -{ - int returnValue = 0; - // Continuously attempt to send the name field until it succeeds or fatally errors: - do - { - returnValue = gnutls_record_send(receivingSession, messageToSend->senderName, - sizeof(((userMessage*)0)->senderName)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - // Continuously attempt to send the message field until it succeeds or fatally errors: - do - { - returnValue = gnutls_record_send(receivingSession, messageToSend->messageContent, - sizeof(((userMessage*)0)->messageContent)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - return returnValue; -} - -// Recieves a message from a given TLS session, wraps the calls to gnutls_read: -int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage) -{ - int returnValue = 0; - // Continuously attempt to receive the name field until it succeeds or fatally errors: - do - { - returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName, - sizeof(((userMessage*)0)->senderName)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - // Continuously attempt to receive the message field until it succeeds or fatally errors: - do - { - returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->messageContent, - sizeof(((userMessage*)0)->messageContent)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - return returnValue; -} - -// Allocate and initialize an outputMessage targeted to a variable amount of players: -outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientsCount) -{ - // Allocate a new output message: - outputMessage * newOutputMessage = malloc(sizeof(outputMessage)); - newOutputMessage->content = malloc(sizeof(userMessage)); - - // Allocate an array of playerInfo for the output message recepients: - newOutputMessage->recipients = malloc(sizeof(playerInfo*) * recipientsCount); - - // Copy in the appropriate data: - memcpy(newOutputMessage->recipients, recipients, sizeof(playerInfo *) * recipientsCount); - memcpy(newOutputMessage->content, messageToQueue, sizeof(userMessage)); - newOutputMessage->recipientsCount = recipientsCount; - - // Return a pointer to the new outputMessage: - return newOutputMessage; -} - -// A function for the output thread, which sends queued messages: -void * outputThreadHandler(void * parameters) -{ - outputThreadParameters * variables = parameters; - queue * outputQueue = variables->outputQueue; - gnutls_session_t * tlssessions = variables->tlssessions; - playerInfo * connectedPlayers = variables->connectedPlayers; - - while (true) - { - // If there's nothing to do, put the thread to sleep: - if (outputQueue->itemCount == 0) - { - pthread_cond_wait(&outputQueue->condition, &outputQueue->mutex); - } - - // Run through the output queue and send all unsent messages: - while (outputQueue->itemCount != 0) - { - // Wait until the queue unlocks: - while (outputQueue->lock); - - // Lock the queue: - outputQueue->lock = true; - - // Get a message off the queue: - outputMessage * message = peekQueue(outputQueue)->data.outputMessage; - - // Unlock the queue: - outputQueue->lock = false; - - // If the first target is set to NULL, it's intended for all connected: - if (message->recipientsCount == 0) - { - for (int index = 0; index < PLAYERCOUNT; index++) - { - messageSend(tlssessions[index], message->content); - } - } - - // Otherwise, send it only to the targeted players: - else - { - int sentCount = 0; - for (int index = 0; index < PLAYERCOUNT; index++) - { - if (sentCount == message->recipientsCount) - { - break; - } - if (&connectedPlayers[index] == message->recipients[sentCount]) - { - sentCount++; - messageSend(tlssessions[index], message->content); - } - } - } - - // Remove the output message from the queue: - popQueue(outputQueue); - } - } -} - -// Sanatize user input to ensure it's okay to process: -void userInputSanatize(char * inputString, int length) -{ - for (int index = 0; index <= length; index++) - { - // If it's not a printable character, it has no business being here: - if(!isprint(inputString[index])) - { - inputString[index] = '\n'; - inputString[index + 1] = '\0'; - break; - } - } - - // Make sure it's null-terminated: - inputString[length - 1] = '\0'; -} - -// Sanatize user names so they display correctly: -void userNameSanatize(char * inputString, int length) -{ - for(int index = 0; index <= length; index++) - { - // If it's not a printable character, it has no business being here: - if(!isprint(inputString[index])) - { - inputString[index] = '\0'; - break; - } - } - // Make sure it's null-terminated: - inputString[length - 1] = '\0'; -} diff --git a/src/inputoutput.h b/src/inputoutput.h deleted file mode 100644 index 93a1cbb..0000000 --- a/src/inputoutput.h +++ /dev/null @@ -1,73 +0,0 @@ -// inputoutput.h: Header file contatning function prototypes and datastructures -// for dealing with input and output. -// Barry Kane, 2022. -#ifndef INPUTOUTPUT_H -#define INPUTOUTPUT_H -#include -#include -#include -#include -#include "constants.h" -#include "playerdata.h" - -// Forward-declare some data structures to prevent cyclic dependencies: -typedef struct queue queue; - -// ======================== -// -=[ Data Structures ]=-: -// ======================== - -// Contains a character/player name and the content of a message: -typedef struct userMessage -{ - char senderName[32]; - char messageContent[MAX]; -} userMessage; - -// Contains a message sent to the server and a pointer to the playerInfo of the connection which sent it: -typedef struct inputMessage -{ - playerInfo * sender; - userMessage * content; -} inputMessage; - -// Contains a message to be sent, the amount of recipients, and pointers to their playerInfo: -typedef struct outputMessage -{ - int recipientsCount; - userMessage * content; - playerInfo ** recipients; -} outputMessage; - -// Contains pointers to the necessary information to be shared outputThreadHandler function: -typedef struct outputThreadParameters -{ - queue * outputQueue; - gnutls_session_t * tlssessions; - playerInfo * connectedPlayers; -} outputThreadParameters; - -// ======================== -// -=[ Functions ]=-: -// ======================== - -// Sends a message to a given TLS session, wraps the calls to gnutls_write: -int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend); - -// Receives a message from a given TLS session, wraps the calls to gnutls_read: -int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage); - -// Create a targetedOutput message to be delivered to the players pointed to in recipients: -outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientCount); - -// A function for the output thread, which sends queued messages: -void * outputThreadHandler(void * parameters); - -// Sanatize user input to ensure it's okay to process: -void userInputSanatize(char * inputString, int length); - -// Sanatize user names so they display correctly: -void userNameSanatize(char * inputString, int length); - -#endif - diff --git a/src/linkedlist.c b/src/linkedlist.c deleted file mode 100644 index c7ff81d..0000000 --- a/src/linkedlist.c +++ /dev/null @@ -1,480 +0,0 @@ -// linkedlist.h: Function definitions for the list type for SilverMUD. -// Barry Kane, 2022. -#include -#include -#include -#include -#include "playerdata.h" -#include "linkedlist.h" - -// Deallocate a given list node, including it's data: -static inline void deallocateListNode(listNode * node, listDataType type) -{ - // Delete the node: - switch (type) - { - case PLAYER: - { - deallocatePlayer(node->data.player); - break; - } - case AREA: - { - destroyList(&(node->data.area->pathList)); - free(node->data.area); - free(node); - break; - } - case PATH: - { - free(node->data.path); - free(node); - break; - } - case SKILL: - { - free(node->data.skill); - free(node); - break; - } - } -} - -// Allocates and instantiates a list of the specified type: -list * createList(listDataType type) -{ - // Allocate and clear the memory for the list: - list * newList = calloc(sizeof(list), 1); - - // Set the appropriate values in the new list: - newList->type = type; - newList->itemCount = 0; - newList->head = NULL; - newList->tail = NULL; - - // Return the new list: - return newList; -} - -// Deallocates a list and all of it's members: -int destroyList(list ** list) -{ - // Check if the list is empty: - if ((*list)->itemCount == 0) - { - free(*list); - list = NULL; - return 0; - } - else - { - while ((*list)->itemCount > 0) - { - removeFromList((*list), (*list)->type, (*list)->itemCount - 1); - } - free(*list); - *list = NULL; - return 0; - } -} - -// Returns the data at a given index in a list: -listData * getFromList(list * list, size_t listIndex) -{ - // Check that we were given a valid index: - if (listIndex > (list->itemCount - 1)) - { - perror("Invalid index specified.\n"); - return NULL; - } - // Return the head if index is 0: - else if (listIndex == 0) - { - return &(list->head->data); - } - // Loop through the entries in the list until we get to the right one: - else - { - listNode * currentNode = list->head; - while (listIndex-- > 0) - { - currentNode = currentNode->next; - } - return &(currentNode->data); - } -} - -// Returns the node at a given index in a list: -listNode * getNodeFromList(list * list, size_t listIndex) -{ - // Check that we were given a valid index: - if (listIndex > (list->itemCount - 1)) - { - perror("Invalid index specified.\n"); - return NULL; - } - // Return the head if index is 0: - else if (listIndex == 0) - { - return list->head; - } - // Loop through the entries in the list until we get to the right one: - else - { - if ((list->itemCount / 2) < listIndex) - { - listNode * currentNode = list->tail; - while (listIndex-- > 0) - { - currentNode = currentNode->previous; - } - return currentNode; - } - else - { - listNode * currentNode = list->head; - while (listIndex-- > 0) - { - currentNode = currentNode->next; - } - return currentNode; - } - } -} - -// Adds the given data to the end of a list: -listNode * addToList(list * list, void * data, listDataType type) -{ - // Check the type: - if (type != list->type) - { - fprintf(stderr, "Not the correct type for this list.\n"); - return NULL; - } - - // If this is the first item in the list: - if (list->itemCount == 0) - { - // Allocate the new node for the list: - list->head = calloc(1, sizeof(listNode)); - - // Set the appropriate pointers for the list: - list->head->next = NULL; - list->head->previous = NULL; - list->tail = list->head; - - // Add the data to the new node: - switch (type) - { - case PATH: - { - list->head->data.path = (playerPath *)data; - break; - } - case AREA: - { - list->head->data.area = (playerArea *)data; - break; - } - case PLAYER: - { - list->head->data.player = (playerInfo *)data; - break; - } - case SKILL: - { - list->head->data.skill = (playerSkill *)data; - break; - } - } - } - else - { - // Allocate the new node at the end of the list: - list->tail->next = calloc(1, sizeof(listNode)); - - // Add the data to the new node: - switch (type) - { - case PATH: - { - list->tail->next->data.path = (playerPath *)data; - break; - } - case AREA: - { - list->tail->next->data.area = (playerArea *)data; - break; - } - case PLAYER: - { - list->tail->next->data.player = (playerInfo *)data; - break; - } - case SKILL: - { - list->tail->next->data.skill = (playerSkill *)data; - break; - } - } - - // Set the appropriate pointers in the new node: - list->tail->next->previous = list->tail; - - // Set the list's tail to the new tail: - list->tail = list->tail->next; - } - // Increase the count of items in the list: - list->itemCount++; - - // Return the new item in the list: - return list->tail; -} - -// Insert the given data at a given index in the list: -listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex) -{ - // Check that the types are correct: - if (list->type != type) - { - fprintf(stderr, "Types do not match.\n"); - return NULL; - } - - // Handle the special case of adding to the end of the list: - if (listIndex == (list->itemCount - 1)) - { - return addToList(list, data, type); - } - - // Handle the special case of adding to the beginning of the list: - if (listIndex == 0) - { - // Create the new node: - listNode * newNode = calloc(1, sizeof(listNode)); - - // Add the data to the node: - switch (type) - { - case PATH: - { - newNode->data.path = (playerPath *)data; - break; - } - case AREA: - { - newNode->data.area = (playerArea *)data; - break; - } - case PLAYER: - { - newNode->data.player = (playerInfo *)data; - break; - } - case SKILL: - { - newNode->data.skill = (playerSkill *)data; - break; - } - } - - // Place it in the list: - newNode->next = list->head; - newNode->previous = NULL; - list->head->previous = newNode; - list->head = newNode; - list->itemCount++; - - // Return the node: - return newNode; - } - - // Check that the index is valid: - if (listIndex > (list->itemCount - 1)) - { - fprintf(stderr, "Index is invalid for the list.\n"); - return NULL; - } - - // Get the current node at the index: - listNode * currentNode = list->head; - for(size_t index = 0; index < listIndex; index++) - { - currentNode = currentNode->next; - } - - // Get the node before the current node: - listNode * previousNode = currentNode->previous; - - // Create the new node: - previousNode->next = calloc(1, sizeof(listNode)); - currentNode->previous = previousNode->next; - previousNode->next->next = currentNode; - previousNode->next->previous = previousNode; - - // Add the data to the node: - switch (type) - { - case PATH: - { - previousNode->next->data.path = (playerPath *)data; - break; - } - case AREA: - { - previousNode->next->data.area = (playerArea *)data; - break; - } - case PLAYER: - { - previousNode->next->data.player = (playerInfo *)data; - break; - } - case SKILL: - { - previousNode->next->data.skill = (playerSkill *)data; - break; - } - } - list->itemCount++; - return previousNode->next; -} - -// Delete the given data from a list: -bool deleteFromList(list * list, void * data, listDataType type) -{ - size_t index = 0; - if (getIndexFromList(list, data, type, &index) == false) - { - return false; - } - else - { - removeFromList(list, type, index); - return true; - } -} - -// Delete the data from a given point in a list: -int removeFromList(list * list, listDataType type, size_t listIndex) -{ - // Check that we're removing the correct type: - if (list->type != type) - { - return -1; - } - - // Check the list index is valid: - if (listIndex > list->itemCount - 1) - { - return -2; - } - - // The first node in the list: - if (listIndex == 0) - { - // Get the current head and move the list's head on: - listNode * oldHead = list->head; - list->head = list->head->next; - - // If we haven't removed the last item, set the previous pointer - // in the new head to null. - if (list->head != NULL) - { - list->head->previous = NULL; - } - - // Delete the node: - deallocateListNode(oldHead, type); - - // Return the new amount of items in the list: - list->itemCount--; - return list->itemCount; - } - // The last node in the list: - else if (listIndex == (list->itemCount - 1)) - { - // Move the tail up by one: - list->tail = list->tail->previous; - - // Deallocate the former tail: - deallocateListNode(list->tail->next, type); - - // Set the appropriate pointer: - list->tail->next = NULL; - - // Return the new amount of items in the list: - list->itemCount--; - return list->itemCount; - } - // A node in the middle of the list: - else - { - // Get the needed node as a pointer: - listNode * nodeToDelete = getNodeFromList(list, listIndex); - - // Set the appropriate pointers for the surrounding nodes: - nodeToDelete->previous->next = nodeToDelete->next; - nodeToDelete->next->previous = nodeToDelete->previous; - - // Deallocate the node: - deallocateListNode(nodeToDelete, type); - - // Return the new amount of items in the list: - list->itemCount--; - return list->itemCount; - } -} - -// Get the index of a given piece of data in a list: -bool getIndexFromList(list * list, void * data, listDataType type, size_t * index) -{ - // Check the list types are the same: - if (list->type == type) - { - fprintf(stderr, "List types do not match.\n"); - return false; - } - - // Search through the list, one-by-one, comparing the list items: - for (*index = 0; *index < list->itemCount; *index += 1) - { - switch (type) - { - case AREA: - { - if (getFromList(list, *index)->area == data) - { - return true; - } - break; - } - case PLAYER: - { - if (getFromList(list, *index)->player == data) - { - return true; - } - break; - } - case PATH: - { - if (getFromList(list, *index)->path == data) - { - return true; - } - break; - } - case SKILL: - { - if (getFromList(list, *index)->skill == data) - { - return true; - } - break; - } - } - } - return false; -} - diff --git a/src/linkedlist.h b/src/linkedlist.h deleted file mode 100644 index 3ac3940..0000000 --- a/src/linkedlist.h +++ /dev/null @@ -1,84 +0,0 @@ -// linkedlist.h: Defines the linked list datatype for SilverMUD. -// Barry Kane, 2022. -#ifndef LINKEDLIST_H -#define LINKEDLIST_H -#include "areadata.h" - -// Let the compiler know there will be structs defined elsewhere: -typedef struct playerPath playerPath; -typedef struct playerArea playerArea; -typedef struct playerInfo playerInfo; -typedef struct playerSkill playerSkill; - -// ======================== -// -=[ Data Structures ]=-: -// ======================== - -// An enum of the possible data types that can be stored in a list: -typedef enum listDataType -{ - PATH, - AREA, - PLAYER, - SKILL -} listDataType; - -// A union containing a pointers to all data types that can be stored in a list: -typedef union listData -{ - playerPath * path; - playerArea * area; - playerInfo * player; - playerSkill * skill; -} listData; - -// A doubly linked node for the linked list type: -typedef struct listNode listNode; -typedef struct listNode -{ - listData data; - listNode * next; - listNode * previous; -} listNode; - -// A header structure for the list containing the length, head, tail, and type of the list. -typedef struct list -{ - listDataType type; - size_t itemCount; - listNode * head; - listNode * tail; -} list; - -// ======================== -// -=[ Functions ]=-: -// ======================== - -// Allocates and instantiates a list of the specified type: -list * createList(listDataType type); - -// Deallocates a list and all of it's members: -int destroyList(list ** list); - -// Returns the data at a given index in a list: -listData * getFromList(list * list, size_t listIndex); - -// Returns the node at a given index in a list: -listNode * getNodeFromList(list * list, size_t listIndex); - -// Adds the given data to the end of a list: -listNode * addToList(list * list, void * data, listDataType type); - -// Insert the given data at a given index in the list: -listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex); - -// Delete the given data from a list: -bool deleteFromList(list * list, void * data, listDataType type); - -// Delete the data from a given point in a list: -int removeFromList(list * list, listDataType type, size_t listIndex); - -// Get the index of a given piece of data in a list: -bool getIndexFromList(list * list, void * data, listDataType type, size_t * index); - -#endif diff --git a/src/playerdata.c b/src/playerdata.c deleted file mode 100644 index 56bb4ed..0000000 --- a/src/playerdata.c +++ /dev/null @@ -1,225 +0,0 @@ -// playerdata.c: Contains functions definitions for working with player data. -// Barry Kane, 2021 -#include -#include -#include -#include -#include -#include "constants.h" -#include "playerdata.h" - -// Create a new skill and add it to the global skill list: -listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill) -{ - if (skillNameLength >= 32) - { - fprintf(stderr, "Skill name is too long. Please shorten the name and try again.\n"); - return NULL; - } - playerSkill * newSkill = malloc(sizeof(playerSkill)); - - strncpy(newSkill->skillName, skillName, 31); - newSkill->skillName[31] = '\0'; - - newSkill->skillPoints = 0; - newSkill->skillModifier = 0; - newSkill->trainedSkill = trainedSkill; - - // Add the skill to a node in the list: - return(addToList(globalSkillList, newSkill, SKILL)); -} - -// Take a skill and add it to the player's skill list: -int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer) -{ - // Check if the skill exists in the game: - size_t globalIndex = 0; - bool skillExists = false; - while (globalIndex < globalSkillList->itemCount) - { - if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) == 0) - { - skillExists = true; - break; - } - globalIndex++; - } - - if (!skillExists) - { - fprintf(stderr, "Skill doesn't exist in skill list.\n"); - return -1; - } - - // Check if the player has the skill: - size_t playerIndex = 0; - bool playerHasSkill = false; - while (playerIndex < targetPlayer->skills->itemCount) - { - if (strncmp(skillName, getFromList(targetPlayer->skills, playerIndex)->skill->skillName, skillNameLength) == 0) - { - playerHasSkill = true; - break; - } - playerIndex++; - } - if (playerHasSkill) - { - getFromList(targetPlayer->skills, playerIndex)->skill->skillPoints++; - } - - // Copy the skill into the player's skill list: - else - { - playerSkill * newSkill = calloc(1, sizeof(playerSkill)); - strncpy(newSkill->skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, 32); - printf("%s ", newSkill->skillName); - newSkill->skillPoints = 1; - addToList(targetPlayer->skills, newSkill, SKILL); - } - return 0; -} - - -// Take a string containing a core stat name and return the core stat: -coreStat getCoreStatFromString(char * inputString, int stringLength) -{ - // Check we've got a long enough string to fit a stat: - if (stringLength < 4) - { - return INVALID; - } - - // Lowercase the string: - char * string = malloc(sizeof(char) * stringLength); - for(int index = 0; index < stringLength; index++) - { - string[index] = tolower(inputString[index]); - } - - // If we have a string that's at most just the stat name plus a null character, or - // a dirtier string, we can check in a better order and ignore impossibilites: - if (stringLength < 9) - { - if (stringLength <= 4) - { - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - // Hopefully one of the seven letter long ones: - else if (stringLength <= 7) - { - if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - // Hopefully one of the 8 letter long stats: - else - { - if (strncmp(string, "intellect", 8) == 0) - { - free(string); - return INTELLECT; - } - else if (strncmp(string, "endurance", 8) == 0) - { - free(string); - return ENDURANCE; - } - else if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - } - // Worst case, it's definitely a dirty string, compare them all: - else - { - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else if (strncmp(string, "intellect", 8) == 0) - { - free(string); - return INTELLECT; - } - else if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "endurance", 8) == 0) - { - free(string); - return ENDURANCE; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - else - { - free(string); - return INVALID; - } - } -} - -// Deallocate a player's information including the skill lists and stats: -int deallocatePlayer(playerInfo * playerToDeallocate) -{ - // Deallocate the skill list: - destroyList(&(playerToDeallocate->skills)); - - // Deallocate the stat block: - free(playerToDeallocate->stats); - - // Deallocate the player: - free(playerToDeallocate); - - return 0; -} diff --git a/src/playerdata.h b/src/playerdata.h deleted file mode 100644 index b48bf80..0000000 --- a/src/playerdata.h +++ /dev/null @@ -1,84 +0,0 @@ -// playerdata.h: Header file containing data structures for player data and function -// prototypes for interacting with said data. -#ifndef PLAYERDATA_H -#define PLAYERDATA_H -#include -#include -#include "areadata.h" -#include "constants.h" -#include "linkedlist.h" - -// Let the compiler know there will be structs defined elsewhere: -typedef struct playerInfo playerInfo; -typedef struct playerArea playerArea; -typedef struct playerPath playerPath; -typedef struct listNode listNode; -typedef struct list list; - -// The basic information that needs to be stored for a player or creature's stats: -typedef struct statBlock -{ - // Levelling: - int level; - long experience; - - // Health: - int currentHealth; - int maxHealth; - - // Core Stats: - int wits; - int intellect; - int strength; - int endurance; - int dexerity; - - // Character Building: - int specPoints; - int skillPoints; -} statBlock; - -// Information about a skill, including skill levels and modifiers for the player: -typedef struct playerSkill -{ - char skillName[32]; - int skillPoints; - int skillModifier; - bool trainedSkill; -} playerSkill; - -// Information about a single player's character: -typedef struct playerInfo -{ - playerInfo * talkingWith; - playerArea * currentArea; - char playerName[32]; - statBlock * stats; - list * skills; -} playerInfo; - -// An enum of the main stats of the game: -typedef enum coreStat -{ - WITS, - INTELLECT, - STRENGTH, - ENDURANCE, - DEXERITY, - INVALID -} coreStat; - -// Create a new skill and add it to the global skill list: -listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill); - -// Take a skill and add it to the player's skill list: -int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer); -int takeSkillbyID(list * globalSkillList, int skillID, playerInfo * targetPlayer); - -// Take a string containing a core stat name and return the core stat: -coreStat getCoreStatFromString(char * string, int stringLength); - -// Deallocate a player's information including the skill lists and stats: -int deallocatePlayer(playerInfo * playerToDeallocate); - -#endif diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index 74ba8e8..0000000 --- a/src/queue.c +++ /dev/null @@ -1,220 +0,0 @@ -// queue.c: Implements the queue data type and associated functions for SilverMUD. -// Barry Kane, 2022 -#include -#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; - - // Create the threading constructs: - newQueue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; - newQueue->condition = (pthread_cond_t)PTHREAD_COND_INITIALIZER; - - // 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); - - // Point the queue pointer to NULL; - *queue = NULL; -} - -// 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) -{ - // Check if the queue is locked: - while (queue->lock); - - // Create a new node: - queueNode * newNode = malloc(sizeof(queueNode)); - newNode->type = type; - // 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; - - // Flag that the queue was modified: - pthread_cond_broadcast(&queue->condition); -} diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index 616794c..0000000 --- a/src/queue.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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" - -// Forward-declare some data structures to prevent cyclic dependencies: -typedef struct queue queue; - -// ======================== -// -=[ Data Structures ]=-: -// ======================== - -// An enum which is used to state what type of data is being stored in a queueNode: -typedef enum queueDataType -{ - EVENT, - COMMAND, - INPUT_MESSAGE, - OUTPUT_MESSAGE -} queueDataType; - -// A union for storing a pointer to all the types of data a queueNode may hold: -typedef union queueData -{ - outputMessage * outputMessage; - inputMessage * inputMessage; - commandEvent * command; -} queueData; - -// A queue node, a singly-linked list node for our queue: -typedef struct queueNode queueNode; -typedef struct queueNode -{ - queueDataType type; - queueData data; - queueNode * next; -} queueNode; - -// A queue, with pointers to the head and tail of the linked list, and data for multi-threading, locking, and an item count. -typedef struct queue -{ - volatile bool lock; - size_t itemCount; - queueNode * front; - queueNode * back; - pthread_mutex_t mutex; - pthread_cond_t condition; -} 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/schemeintegration.c b/src/schemeintegration.c deleted file mode 100644 index f915eca..0000000 --- a/src/schemeintegration.c +++ /dev/null @@ -1,217 +0,0 @@ -// schemeintegration.h: Function definitions for SilverMUD's Scheme integration. -// Barra Ó Catháin, 2023. -#include -#include -#include "schemeintegration.h" - -// Create a player skill and add it to a given skill list from Scheme: -SCM scheme_create_skill(SCM string, SCM skilllist) -{ - size_t skillNameLength = 0; - char * skillName = scm_to_latin1_stringn(string, &skillNameLength); - createSkill(scm_to_pointer(skilllist), skillName, skillNameLength, false); - free(skillName); - return SCM_BOOL_T; -} - -// Create a new area and add it to the list, given a name and area description, and an area list from Scheme. -// Returns the index of the new area: -SCM scheme_create_area(SCM area_name, SCM area_description, SCM area_list) -{ - // Check if the area list exists: - list * areaList = scm_to_pointer(area_list); - if (areaList == NULL || areaList->type != AREA) - { - return SCM_BOOL_F; - } - - // Turn the SCM values into C strings that we can use: - char * areaName = scm_to_locale_stringn(area_name, NULL); - char * areaDescription= scm_to_locale_stringn(area_description, NULL); - - // Create and add the area to the area list: - addToList(areaList, createArea(areaName, areaDescription), AREA); - - // The new index of the area will be at the end of the list. Consider returning a pointer to the area: - size_t areaIndex = areaList->itemCount - 1; - - // Free the strings: - free(areaName); - free(areaDescription); - - // Return the area index to Scheme for further lispy hacking: - return scm_from_size_t(areaIndex); -} - -// Create a one way path between two areas from scheme: -SCM scheme_create_path(SCM path_name, SCM from_area_index, SCM to_area_index, SCM area_list) -{ - // Check if the area list exists: - list * areaList = scm_to_pointer(area_list); - if (areaList == NULL || areaList->type != AREA) - { - return SCM_BOOL_F; - } - - // Check if the areas exist: - playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area; - playerArea * toArea = getFromList(areaList, scm_to_size_t(to_area_index))->area; - - if (fromArea == NULL || toArea == NULL) - { - return SCM_BOOL_F; - } - - // Turn the SCM value into a C string that we can use: - char * pathName = scm_to_locale_stringn(path_name, NULL); - - // Create the path: - createOneWayPath(fromArea, toArea, pathName); - - // Free the string: - free(pathName); - - // Return true to Scheme: - return SCM_BOOL_T; -} - -// Change the name of an existing area in a list, given the number of the area in the list, from Scheme: -SCM scheme_change_area_name(SCM new_name, SCM area_number, SCM area_list) -{ - // Check if the area exists: - list * areaList = scm_to_pointer(area_list); - size_t areaNumber = scm_to_size_t(area_number); - - if (areaList->type != AREA) - { - return SCM_BOOL_F; - } - - playerArea * area = getFromList(areaList, areaNumber)->area; - - if (area == NULL) - { - return SCM_BOOL_F; - } - - // Create a string from the Scheme string and copy it into the area: - size_t newNameLength = 0; - char * newName = scm_to_locale_stringn(new_name, &newNameLength); - memset(area->areaName, 0, 32); - if (newNameLength > 32) - { - memcpy(area->areaName, newName, 31); - area->areaName[31] = '\0'; - } - else - { - memcpy(area->areaName, newName, newNameLength); - area->areaName[31] = '\0'; - } - - free(newName); - - return SCM_BOOL_T; -} - -// Change the description of an existing area in a list, given the number of the area in the list, from Scheme: -SCM scheme_change_area_description(SCM new_description, SCM area_number, SCM area_list) -{ - // Check if the area exists: - list * areaList = scm_to_pointer(area_list); - size_t areaNumber = scm_to_size_t(area_number); - - if (areaList->type != AREA) - { - return SCM_BOOL_F; - } - - playerArea * area = getFromList(areaList, areaNumber)->area; - - if (area == NULL) - { - return SCM_BOOL_F; - } - - // Create a string from the Scheme string and copy it into the area: - size_t newDescriptionLength = 0; - char * newDescription = scm_to_locale_stringn(new_description, &newDescriptionLength); - memset(area->areaDescription, 0, MAX - 35); - if (newDescriptionLength > MAX - 35) - { - memcpy(area->areaDescription, newDescription, MAX - 35); - area->areaDescription[MAX - 36] = '\0'; - } - else - { - memcpy(area->areaDescription, newDescription, newDescriptionLength); - area->areaDescription[MAX - 36] = '\0'; - } - - free(newDescription); - - return SCM_BOOL_T; -} - -// Message every currently connected player from Scheme: -SCM scheme_message_everyone(SCM sender_name, SCM message_content, SCM output_queue) -{ - // Allocate the memory for the needed data structures: - outputMessage * newOutputMessage = calloc(1, sizeof(userMessage)); - userMessage * newMessage = calloc(1, sizeof(userMessage)); - - // Set some basic information for the output message, allowing it to be sent to everyone: - newOutputMessage->content = newMessage; - newOutputMessage->recipientsCount = 0; - newOutputMessage->recipients = NULL; - - // Convert the Scheme strings to C strings, and ensure they're NULL terminated: - scm_to_locale_stringbuf(sender_name, newMessage->senderName, 31); - newMessage->senderName[31] = '\0'; - scm_to_locale_stringbuf(message_content, newMessage->messageContent, MAX - 1); - newMessage->messageContent[MAX - 1] = '\0'; - - // Clean up the message contents to ensure they're safe to send and display correctly: - userNameSanatize(newMessage->senderName, 32); - userInputSanatize(newMessage->messageContent, MAX); - - // Push it to the queue, where it will be handled and de-allocated: - pushQueue(scm_to_pointer(output_queue), newOutputMessage, OUTPUT_MESSAGE); - - // Return a Scheme #t value: - return SCM_BOOL_T; -} - -// The function ran by the Scheme thread which handles all game-master and interpreter interaction: -void * schemeHandler(void * parameters) -{ - // Take in the needed values from the main thread and make it back into the struct: - SchemeThreadParameters * schemeThreadParameters = parameters; - - // Initialize GNU Guile: - scm_init_guile(); - - // Register the various functions: - scm_c_define_gsubr("create-area", 3, 0, 0, &scheme_create_area); - scm_c_define_gsubr("create-path", 4, 0, 0, &scheme_create_path); - scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill); - scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone); - scm_c_define_gsubr("change-area-name", 3, 0, 0, &scheme_change_area_name); - scm_c_define_gsubr("change-area-description", 3, 0, 0, &scheme_change_area_description); - - // Define the various game state pointers as Scheme objects: - scm_c_define("area-list", scm_from_pointer(schemeThreadParameters->areaList, NULL)); - scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL)); - scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL)); - - // Enable readline support: - scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\")) (spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); - - // Drop into the Scheme interpreter: - scm_shell(0, NULL); - - // Return NULL. This should be unreachable. - return NULL; -} - diff --git a/src/schemeintegration.h b/src/schemeintegration.h deleted file mode 100644 index 9f20f25..0000000 --- a/src/schemeintegration.h +++ /dev/null @@ -1,19 +0,0 @@ -// schemeintegration.h: Data-structures and function prototypes for SilverMUD's Scheme integration. -// Barra Ó Catháin, 2023. -#ifndef SCHEMEINTEGRATION_H -#define SCHEMEINTEGRATION_H -#include "queue.h" -#include "linkedlist.h" - -typedef struct list list; -typedef struct queue queue; - -typedef struct SchemeThreadParameters -{ - list * areaList, * skillList; - queue * inputQueue, * outputQueue; -} SchemeThreadParameters; - -void * schemeHandler(void * parameters); - -#endif diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c deleted file mode 100644 index c861cf7..0000000 --- a/src/server/SilverMUDServer.c +++ /dev/null @@ -1,378 +0,0 @@ -// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.5 -// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance. -// Barry Kane, 2021 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../queue.h" -#include "../areadata.h" -#include "../gamelogic.h" -#include "../constants.h" -#include "../playerdata.h" -#include "../linkedlist.h" -#include "../texteffects.h" -#include "../inputoutput.h" -#include "../schemeintegration.h" - -typedef struct sockaddr sockaddr; -void sigintHandler(int signal) -{ - printf("Caught signal %d.\n", signal); - exit(EXIT_SUCCESS); -} - -int main(int argc, char ** argv) -{ - time_t currentTime; - unsigned delay = 800; - int socketFileDesc, connectionFileDesc, length, clientsAmount, - socketCheck, activityCheck; - fd_set connectedClients; - pthread_t gameLogicThread, outputThread, schemeThread; - int clientSockets[PLAYERCOUNT]; - userMessage sendBuffer, receiveBuffer; - playerInfo connectedPlayers[PLAYERCOUNT]; - char testString[32] = "Hehe."; - struct sockaddr_in serverAddress, clientAddress; - char motd[2048] = "Please login with the /join command."; - queue * inputQueue = createQueue(), * outputQueue = createQueue(); - - // Parse command-line options: - int currentopt = 0; - while ((currentopt = getopt(argc, argv, "d:m:")) != -1) - { - switch(currentopt) - { - case 'd': - { - delay = atoi(optarg); - break; - } - case 'm': - { - strncpy(motd, optarg, 2047); - motd[2047] = '\0'; - break; - } - } - } - - // Set the handler for SIGINT: - signal(2, sigintHandler); - - // -==[ TEST GAME-STATE INITIALIZATION ]==- - // Initialize test areas: - list * areas = createList(AREA); - addToList(areas, createArea("Login Area", motd), AREA); - - // Create the areas: - addToList(areas, createArea("Octal One - Docking Bay Alpha", - "You are standing in the main docking bay of the largest station in the Octal System. " - "The sheer size of the bay is awe-inpiring. The number of ships is endless. " - "The bay is curved along with the body of the station. A catwalk runs along the back wall of the bay. " - "Two large arches lie at each end, leading to the other bays, and in the center, a set of doors leading to the interior of the station."), AREA); - - addToList(areas, createArea("Octal One - Station Access Control", - "You enter into the hallway leading to the main interior of the station." - "The attendant informs you that due to a computer error, exits cannot be proccessed at the moment," - " so you will be unable to leave, until it is resolved. " - "He apologizes profusely for the inconvenience, and clears you for entry if you wish to continue."), AREA); - - addToList(areas, createArea("Octal One - Floor Zero", - "You've never quite seen so many people in one place. A large ring of shopfronts surrounds an area filled with benches and tables. " - "There's so many buisnesses in sight that you feel you could find everything you need, and this is only one of 25 main floors, " - "not to mention the 6 outer pylons which surround the main hull of the station. Staircases lead to an upper platform allowing access to the pylons. "), AREA); - - // Initialize test paths: - createPath(getFromList(areas, 1)->area, getFromList(areas, 2)->area, - "Enter the station interior.", "Return to Docking Bay Alpha."); - createOneWayPath(getFromList(areas, 2)->area, getFromList(areas, 3)->area, - "Continue to station interior. "); - - list * globalSkillList = createList(SKILL); - - // Create a few basic skills: - createSkill(globalSkillList, "Medicine", 8, true); - createSkill(globalSkillList, "Lockpicking", 12, true); - createSkill(globalSkillList, "Programming", 12, true); - createSkill(globalSkillList, "Sensor Reading", 14, false); - createSkill(globalSkillList, "Starship Piloting", 17, true); - createSkill(globalSkillList, "Mechanical Repair", 17, true); - - // Initialize playerdata: - for (int index = 0; index < PLAYERCOUNT; index++) - { - sprintf(testString, "UNNAMED %d", index); - // OH NO IT'S NOT MEMORY SAFE BETTER REWRITE IT IN RUST - // But wait, we know the string won't be too big, so it's fine. - strcpy(connectedPlayers[index].playerName, testString); - connectedPlayers[index].currentArea = getFromList(areas, 0)->area; - connectedPlayers[index].stats = calloc(1, sizeof(statBlock)); - connectedPlayers[index].stats->specPoints = 30; - connectedPlayers[index].stats->skillPoints = 30; - connectedPlayers[index].skills = createList(SKILL); - } - - // -==[ TEST GAME-STATE INITIALIZATION END ]==- - - // Give an intro: Display the Silverkin Industries logo and splash text. - slowPrint(logostring, delay); - slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.5\n", delay); - - // Seed random number generator from the current time: - srand((unsigned)time(¤tTime)); - - // Initialize the sockets to 0, so we don't crash. - for (int index = 0; index < PLAYERCOUNT; index++) - { - clientSockets[index] = 0; - } - - // Get a socket and make sure we actually get one. - socketFileDesc = socket(AF_INET, SOCK_STREAM, 0); - if (socketFileDesc == -1) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - - else - { - slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - - memset(&serverAddress, 0, sizeof(serverAddress)); - - // Assign IP and port: - serverAddress.sin_family = AF_INET; - serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - serverAddress.sin_port = htons(PORT); - - // Binding newly created socket to given IP, and checking it works: - if ((bind(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress))) != 0) - { - fprintf(stderr, "\tSocket Binding is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - - else - { - slowPrint("\tSocket Binding is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - - // Let's start listening: - if ((listen(socketFileDesc, PLAYERCOUNT)) != 0) - { - fprintf(stderr, "\tServer Listener is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(EXIT_FAILURE); - } - else - { - slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - length = sizeof(clientAddress); - - // Declare the needed variables for TLS sessions: - gnutls_session_t tlssessions[PLAYERCOUNT]; - gnutls_anon_server_credentials_t serverkey = NULL; - gnutls_anon_allocate_server_credentials(&serverkey); - gnutls_anon_set_server_known_dh_params(serverkey, GNUTLS_SEC_PARAM_MEDIUM); - - // Initialize all the TLS sessions to NULL: We use this to check if it's an "empty connection." - for (int index = 0; index < PLAYERCOUNT; index++) - { - tlssessions[index] = NULL; - if (gnutls_init(&tlssessions[index], GNUTLS_SERVER) < 0) - { - fprintf(stderr, "\tTLS Sessions Initialization is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(EXIT_FAILURE); - } - gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL); - gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey); - gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - } - slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", delay); - - // Prepare the game logic thread: - gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters)); - gameLogicThreadParameters->connectedPlayers = connectedPlayers; - gameLogicThreadParameters->playerCount = &clientsAmount; - gameLogicThreadParameters->globalSkillList = globalSkillList; - gameLogicThreadParameters->outputQueue = outputQueue; - gameLogicThreadParameters->inputQueue = inputQueue; - gameLogicThreadParameters->areaList = areas; - pthread_create(&gameLogicThread, NULL, &gameLogicHandler, gameLogicThreadParameters); - - slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - - // Prepare the output queue thread: - outputThreadParameters * outputParameters = malloc(sizeof(outputThreadParameters)); - outputParameters->outputQueue = outputQueue; - outputParameters->tlssessions = tlssessions; - outputParameters->connectedPlayers = connectedPlayers; - pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters); - slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - - // Prepare the Scheme handler thread: - SchemeThreadParameters * schemeParameters = malloc(sizeof(SchemeThreadParameters)); - schemeParameters->skillList = globalSkillList; - schemeParameters->outputQueue = outputQueue; - schemeParameters->areaList = areas; - slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - slowPrint("=====\n", delay); - pthread_create(&schemeThread, NULL, &schemeHandler, schemeParameters); - - while(true) - { - // Clear the set of file descriptors and add the master socket: - FD_ZERO(&connectedClients); - FD_SET(socketFileDesc, &connectedClients); - clientsAmount = socketFileDesc; - - // Find all sockets that are still working and place them in the set: - for(int index = 0; index < PLAYERCOUNT; index++) - { - // Just get the one we're working with to another name: - socketCheck = clientSockets[index]; - - // If it's working, bang it into the list: - if(socketCheck > 0) - { - FD_SET(socketCheck, &connectedClients); - } - // The amount of clients is needed for select(): - if(socketCheck > clientsAmount) - { - clientsAmount = socketCheck; - } - } - - // See if a connection is ready to be interacted with: - activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL); - - // Check if select() worked: - if ((activityCheck < 0) && (errno != EINTR)) - { - fprintf(stderr, "Error in select(), retrying.\n"); - } - - // If it's the master socket selected, there is a new connection: - if (FD_ISSET(socketFileDesc, &connectedClients)) - { - if ((connectionFileDesc = accept(socketFileDesc, (struct sockaddr *)&clientAddress, (socklen_t*)&length)) < 0) - { - fprintf(stderr, "Failed to accept connection. Aborting.\n"); - exit(EXIT_FAILURE); - } - // See if we can put in the client: - for (int index = 0; index < PLAYERCOUNT; index++) - { - // When there is an empty slot, pop it in: - if (clientSockets[index] == 0) - { - volatile int handshakeReturnValue = 0; - clientSockets[index] = connectionFileDesc; - gnutls_transport_set_int(tlssessions[index], clientSockets[index]); - do - { - handshakeReturnValue = gnutls_handshake(tlssessions[index]); - } while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0); - - // If it's good, send them the welcome message: - if(handshakeReturnValue == 0) - { - // Send a greeting message: - memcpy(sendBuffer.senderName, "\0 Login > \0", 11); - strcpy(sendBuffer.messageContent, "Welcome to the server!"); - messageSend(tlssessions[index], &sendBuffer); - strcpy(receiveBuffer.messageContent, "/look"); - - // Allocate the memory for a new input message: - inputMessage * newMessage = malloc(sizeof(inputMessage)); - newMessage->content = malloc(sizeof(userMessage)); - - // Copy in the correct data: - memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage)); - newMessage->sender = &connectedPlayers[index]; - - // Push the new message onto the queue: - pushQueue(inputQueue, newMessage, INPUT_MESSAGE); - break; - } - - // If it's not good, close it, we don't want it: - shutdown(clientSockets[index], 2); - close(clientSockets[index]); - clientSockets[index] = 0; - break; - } - } - } - // Otherwise, it's a client we need to interact with: - else - { - for (int index = 0; index < PLAYERCOUNT; index++) - { - socketCheck = clientSockets[index]; - - if(FD_ISSET(socketCheck, &connectedClients)) - { - int returnVal = messageReceive(tlssessions[index], &receiveBuffer); - // If player has disconnected: - if(returnVal == -10 || returnVal == 0) - { - // Close the session: - gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR); - gnutls_deinit(tlssessions[index]); - shutdown(clientSockets[index], 2); - close(clientSockets[index]); - clientSockets[index] = 0; - tlssessions[index] = NULL; - - // Clear out the old player state so that a new one may join: - sprintf(testString, "UNNAMED %d", index); - strcpy(connectedPlayers[index].playerName, testString); - connectedPlayers[index].currentArea = getFromList(areas, 0)->area; - - // Prepare a fresh SSL session for the next new player: - gnutls_init(&tlssessions[index], GNUTLS_SERVER); - gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL); - gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey); - gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - } - // Otherwise, they've sent a message: - else - { - // Allocate the memory for a new input message: - inputMessage * newMessage = malloc(sizeof(inputMessage)); - newMessage->content = malloc(sizeof(userMessage)); - - // Copy in the correct data: - memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage)); - newMessage->sender = &connectedPlayers[index]; - - // Push the new message onto the queue: - pushQueue(inputQueue, newMessage, INPUT_MESSAGE); - } - } - } - } - } - pthread_cancel(gameLogicThread); - pthread_cancel(outputThread); - exit(EXIT_SUCCESS); -} diff --git a/src/texteffects.c b/src/texteffects.c deleted file mode 100644 index 7cdaf57..0000000 --- a/src/texteffects.c +++ /dev/null @@ -1,121 +0,0 @@ -// texteffects.c: Implementation of text effect library for SilverMUD. -// Barry Kane, 2021. -#include -#include -#include -#include - -// A character by character print, similar to a serial terminal with lower baud rate: -void slowPrint(const char * stringToPrint, int delay) -{ - int characterIndex = 0; - while (stringToPrint[characterIndex] != '\0') - { - putchar(stringToPrint[characterIndex]); - // Flush the buffer so there's no line buffering. - fflush(stdout); - usleep(delay); - characterIndex++; - } -} - -// The same, altered to work with ncurses: -void slowPrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded) -{ - int characterIndex = 0; - if (bolded) - { - wattron(window, A_BOLD); - } - while (stringToPrint[characterIndex] != '\0') - { - waddch(window, stringToPrint[characterIndex]); - // Refresh the ncurses screen. - wrefresh(window); - usleep(delay); - characterIndex++; - } - if (bolded) - { - wattroff(window, A_BOLD); - } - wrefresh(window); -} - -// A character by character "brute-force" print, similar to Hollywood hacking scenes: -void bruteforcePrint(const char * stringToPrint, int delay) -{ - unsigned int characterIndex = 0; - while (stringToPrint[characterIndex] != '\0') - { - for(unsigned char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++) - { - putchar(stringToPrint[currentCharacter]); - fflush(stdout); - usleep(delay); - putchar(8); - fflush(stdout); - } - putchar(stringToPrint[characterIndex]); - characterIndex++; - } -} - -// The same, altered to work with ncurses: -void bruteforcePrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded) -{ - int characterIndex = 0; - if (bolded) - { - wattron(window, A_BOLD); - } - while (stringToPrint[characterIndex] != '\0') - { - for(char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++) - { - waddch(window, currentCharacter); - wrefresh(window); - usleep(delay); - waddch(window, 8); - wrefresh(window); - } - waddch(window, stringToPrint[characterIndex]); - characterIndex++; - } - if (bolded) - { - wattroff(window, A_BOLD); - } - wrefresh(window); -} - -// Word-wrap a string to a given width: -void wrapString(char * stringToWrap, int stringLength, int screenWidth) -{ - int characterCount = 0; - for(int index = 0; index < stringLength; index++) - { - if (stringToWrap[index] == '\n') - { - characterCount = 0; - } - else - { - characterCount++; - } - if (characterCount == screenWidth) - { - while (!isspace(stringToWrap[index]) && index > 0) - { - index--; - } - if (index == 0) - { - return; - } - stringToWrap[index] = '\n'; - index++; - characterCount = 0; - } - } -} diff --git a/src/texteffects.h b/src/texteffects.h deleted file mode 100644 index 37292c4..0000000 --- a/src/texteffects.h +++ /dev/null @@ -1,38 +0,0 @@ -// texteffects.h: Header file for the texteffects library for SilverMUD. -// Barry Kane, 2021. -#ifndef TEXTEFFECTS_H_ -#define TEXTEFFECTS_H_ -#include -#include - -// A character by character print, similar to a serial terminal with lower baud rate: -void slowPrint(const char * stringToPrint, int delay); - -// The same, altered to work with ncurses: -void slowPrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded); - -// A character by character "brute-force" print, similar to Hollywood hacking scenes: -void bruteforcePrint(const char * stringToPrint, int delay); - -// The same, altered to work with ncurses: -void bruteforcePrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded); - -// Word-wrap a string to a given width: -void wrapString(char * stringToWrap, int stringLength, int screenWidth); - -// A string containing an ASCII art version of the Silverkin Industries logo. -const char * logostring = - " ///////\n" - " //////////////////////////////////////////\n" - " ///////////////////////////////////////////////////////////\n" - " ////////// ////////////////////////////\n" - " ### # # # # ##### ### # # # # # /////////////////\n" - " ## # # # # ## # # ### # ## # //////////////\n" - " ## # # # # # ### # # # # # # /////////\n" - " ### # ### # ##### # # # # # # # ///////\n" - " # ## # ##### # # ### ### ### # ##### ### //////\n" - " # # # # # # # # ## # # # # ## ## ////\n" - " # # # # # # # # ## # ### # # ## //\n" - " # # ### ##### ##### ### # # # # #### ### /\n"; - -#endif diff --git a/tests/list-test.c b/tests/list-test.c deleted file mode 100644 index 94f3db1..0000000 --- a/tests/list-test.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../src/linkedlist.h" -#include - -static inline void printAreaList(list * areaList) -{ - listData * currentData; - for(int index = 0; index < areaList->itemCount; index++) - { - currentData = getFromList(areaList, index); - printf("%d\t| %s - %s\n", index, currentData->area->areaName, currentData->area->areaDescription); - } -} - -void main() -{ - list * areaList = createList(AREA); - char areaName[256]; - char areaDescription[256]; - - printf("\n--==[ Generating a list of ten items. ]==--\n\n"); - for(int count = 1; count <= 10; count++) - { - sprintf(areaName, "Area %d", count); - sprintf(areaDescription, "This is Area %d.", count); - - addToList(areaList, createArea(areaName, areaDescription) , AREA); - } - printAreaList(areaList); - - printf("\n--==[ Inserting items into specific indexes. ]==--\n\n"); - insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 0); - insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 6); - insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 11); - printAreaList(areaList); - - printf("\n--==[ Removing certain areas from the list. ]==--\n\n"); - removeFromList(areaList, AREA, 12); - removeFromList(areaList, AREA, 6); - removeFromList(areaList, AREA, 0); - - printAreaList(areaList); - - destroyList(&areaList); - printf(""); -} - diff --git a/tests/queue-test.c b/tests/queue-test.c deleted file mode 100644 index d248f9c..0000000 --- a/tests/queue-test.c +++ /dev/null @@ -1,132 +0,0 @@ -// 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); -}