Basic message queuing implemented

- Messages are now queued on reception by the server.
- Message queue datastructures are now added.
This commit is contained in:
Barry 2022-04-07 01:38:36 +01:00
parent e4b8693037
commit 4ddb80b8b2
9 changed files with 438 additions and 34 deletions

View File

@ -6,7 +6,7 @@ serversrc = $(wildcard src/misc/*.c) \
src/SilverMUDServer.c
serverobj = $(serversrc:.c=.o)
CLIENTLDFLAGS= -lpthread -lncurses -lgnutls
SERVERLDFLAGS= -lncurses -lgnutls
SERVERLDFLAGS= -lpthread -lncurses -lgnutls
SilverMUDClient: $(clientobj)
gcc -s -O3 -o $@ $^ $(CLIENTLDFLAGS)

View File

@ -1,4 +1,4 @@
* SilverMUD: An extensible terminal-top role playing game engine
* 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

View File

@ -13,11 +13,10 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <gnutls/gnutls.h>
#include "misc/constants.h"
#include "misc/playerdata.h"
#include "misc/texteffects.h"
#include "misc/inputoutput.h"
#include "misc/inputhandling.h"
static int MAX = 2048;
// A struct for passing arguments to our threads containing a file descriptor and a window pointer:
typedef struct threadparameters

View File

@ -14,31 +14,32 @@
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include "misc/lists.h"
#include "misc/constants.h"
#include "misc/playerdata.h"
#include "misc/texteffects.h"
#include "misc/inputoutput.h"
#include "misc/inputhandling.h"
const int PORT = 5000;
const int PLAYERCOUNT = 64;
typedef struct sockaddr sockaddr;
int main()
{
bool keepRunning = true;
int socketFileDesc, connectionFileDesc, length, clientsAmount,
socketCheck, activityCheck, readLength, returnVal;
socketCheck, activityCheck, returnVal;
fd_set connectedClients;
int clientSockets[PLAYERCOUNT];
userMessage sendBuffer, receiveBuffer;
playerInfo connectedPlayers[PLAYERCOUNT];
char testString[32] = "Hehe.";
struct sockaddr_in serverAddress, clientAddress;
inputMessageQueue * inputQueue = createInputMessageQueue();
outputMessageQueue * outputQueue = createOutputMessageQueue();
// Initialize playerdata:
for (int index = 0; index < PLAYERCOUNT; index++)
{
strcpy(connectedPlayers[index].playerName, "UNNAMED");
sprintf(testString, "UNNAMED %d", index);
strcpy(connectedPlayers[index].playerName, testString);
}
// Give an intro: Display the Silverkin Industries logo and splash text.
@ -187,18 +188,47 @@ int main()
if(FD_ISSET(socketCheck, &connectedClients))
{
messageReceive(tlssessions[index], &receiveBuffer);
sprintf(testString, "User %d", index);
strcpy(sendBuffer.senderName, testString);
userInputSanatize(receiveBuffer.messageContent, sizeof(receiveBuffer.messageContent));
strcpy(sendBuffer.messageContent, receiveBuffer.messageContent);
for (int sendIndex = 0; sendIndex < clientsAmount; sendIndex++)
if(messageReceive(tlssessions[index], &receiveBuffer) == -10)
{
messageSend(tlssessions[sendIndex], &sendBuffer);
gnutls_bye(tlssessions[index], GNUTLS_SHUT_RDWR);
gnutls_deinit(tlssessions[index]);
shutdown(clientSockets[index], 2);
close(clientSockets[index]);
clientSockets[index] = 0;
tlssessions[index] = NULL;
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);
}
else
{
queueInputMessage(inputQueue, receiveBuffer, &connectedPlayers[index]);
}
}
}
}
// TEMPORARY: MOVE INPUT MESSAGES TO OUTPUT MESSAGES:
while(inputQueue->currentLength > 0)
{
inputMessage * message = peekInputMessage(inputQueue);
strncpy(message->content->senderName, message->sender->playerName, 32);
userInputSanatize(message->content->messageContent, MAX);
if(message->content->messageContent[0] != '\n')
{
queueOutputMessage(outputQueue, *message->content);
}
dequeueInputMessage(inputQueue);
}
while(outputQueue->currentLength > 0)
{
outputMessage * message = peekOutputMessage(outputQueue);
for (int index = 0; index < PLAYERCOUNT; index++)
{
messageSend(tlssessions[index], message->content);
}
dequeueOutputMessage(outputQueue);
}
}
return 0;
}

9
src/misc/constants.h Normal file
View File

@ -0,0 +1,9 @@
// 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

View File

@ -1,33 +1,282 @@
// inputoutput.c: Implementation of input output library for SilverMUD.
// inputoutput.c: Implementation of input/output library for SilverMUD.
// Barry Kane, 2022.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "constants.h"
#include "playerdata.h"
#include "inputoutput.h"
#include <gnutls/gnutls.h>
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
void messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
{
int returnValue = 0;
do
{
returnValue = gnutls_record_send(receivingSession, messageToSend->senderName, sizeof(((userMessage*)0)->senderName));
returnValue = gnutls_record_send(receivingSession, messageToSend->senderName,
sizeof(((userMessage*)0)->senderName));
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
do
{
returnValue = gnutls_record_send(receivingSession, messageToSend->messageContent, sizeof(((userMessage*)0)->messageContent));
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:
void messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage)
int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage)
{
int returnValue = 0;
do
{
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName, sizeof(((userMessage*)0)->senderName));
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName,
sizeof(((userMessage*)0)->senderName));
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
do
{
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->messageContent, sizeof(((userMessage*)0)->messageContent));
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->messageContent,
sizeof(((userMessage*)0)->messageContent));
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
return returnValue;
}
outputMessageQueue * createOutputMessageQueue(void)
{
outputMessageQueue * newQueue = malloc(sizeof(outputMessageQueue));
newQueue->front = NULL;
newQueue->back = NULL;
newQueue->currentLength = 0;
newQueue->lock = false;
return newQueue;
}
int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue)
{
// Copy the message into a new output message:
outputMessage * outputMessage = malloc(sizeof(outputMessage));
// Allocate the internal userMessage to store the message:
outputMessage->content = malloc(sizeof(userMessage));
// Copy the userMessage to the internal userMessage:
strncpy(outputMessage->content->senderName, messageToQueue.senderName, 32);
strncpy(outputMessage->content->messageContent, messageToQueue.messageContent, MAX);
// We have no targets, NULL sends to all players in an area:
outputMessage->targets[0] = NULL;
// Wait for the queue to unlock:
while (queue->lock);
// Lock the queue:
queue->lock = true;
// Check that we're not overflowing the queue:
if ((queue->currentLength + 1) > MAXQUEUELENGTH)
{
// Unlock the queue:
queue->lock = false;
return -1;
}
else
{
// If the queue is empty, set the first message as both the front and back of the queue:
if(queue->front == NULL)
{
queue->front = outputMessage;
queue->back = outputMessage;
queue->currentLength++;
// Unlock the queue:
queue->lock = false;
return 0;
}
else
{
queue->back->next = outputMessage;
queue->back = outputMessage;
queue->currentLength++;
// Unlock the queue:
queue->lock = false;
return 0;
}
}
}
int dequeueOutputMessage(outputMessageQueue * queue)
{
// Wait for the queue to unlock:
while (queue->lock);
// Lock the queue:
queue->lock = true;
// Check the list isn't empty:
if(queue->front == NULL)
{
queue->lock = false;
return -1;
}
// If there is only one item in the queue:
else if(queue->front == queue->back)
{
free(queue->front->content);
free(queue->front);
queue->front = NULL;
queue->back = NULL;
queue->currentLength--;
queue->lock = false;
return 0;
}
// Remove the front item:
else
{
outputMessage * messageToDelete = queue->front;
queue->front = queue->front->next;
free(messageToDelete->content);
free(messageToDelete);
queue->currentLength--;
queue->lock = false;
return 0;
}
}
inputMessageQueue * createInputMessageQueue(void)
{
inputMessageQueue * newQueue = malloc(sizeof(inputMessageQueue));
newQueue->front = NULL;
newQueue->back = NULL;
newQueue->currentLength = 0;
newQueue->lock = false;
return newQueue;
}
int dequeueInputMessage(inputMessageQueue * queue)
{
// Wait for the queue to unlock:
while (queue->lock);
// Lock the queue:
queue->lock = true;
// Check the list isn't empty:
if(queue->front == NULL)
{
queue->lock = false;
return -1;
}
// If there is only one item in the queue:
else if(queue->front == queue->back)
{
free(queue->front->content);
free(queue->front);
queue->front = NULL;
queue->back = NULL;
queue->currentLength--;
queue->lock = false;
return 0;
}
// Remove the front item:
else
{
inputMessage * messageToDelete = queue->front;
queue->front = queue->front->next;
free(messageToDelete->content);
free(messageToDelete);
queue->currentLength--;
queue->lock = false;
return 0;
}
}
int queueInputMessage(inputMessageQueue * queue, userMessage messageToQueue, playerInfo * sendingPlayer)
{
// Copy the message into a new input message:
inputMessage * inputMessage = malloc(sizeof(inputMessage));
// Allocate the internal userMessage to store the message:
inputMessage->content = malloc(sizeof(userMessage));
// Copy the userMessage to the internal userMessage:
strncpy(inputMessage->content->senderName, messageToQueue.senderName, 32);
strncpy(inputMessage->content->messageContent, messageToQueue.messageContent, MAX);
// We have no targets, NULL sends to all players in an area:
inputMessage->sender = sendingPlayer;
// Wait for the queue to unlock:
while (queue->lock);
// Lock the queue:
queue->lock = true;
// Check that we're not overflowing the queue:
if ((queue->currentLength + 1) > MAXQUEUELENGTH)
{
// Unlock the queue:
queue->lock = false;
return -1;
}
else
{
// If the queue is empty, set the first message as both the front and back of the queue:
if(queue->front == NULL)
{
queue->front = inputMessage;
queue->back = inputMessage;
queue->currentLength++;
// Unlock the queue:
queue->lock = false;
return 0;
}
else
{
queue->back->next = inputMessage;
queue->back = inputMessage;
queue->currentLength++;
// Unlock the queue:
queue->lock = false;
return 0;
}
}
}
void userInputSanatize(char * inputString, int length)
{
for(int index = 0; index <= length; index++)
{
if(!isprint(inputString[index]))
{
inputString[index] = '\n';
inputString[index + 1] = '\0';
break;
}
}
inputString[length - 1] = '\0';
}
// Return the front inputMessage from an inputMessageQueue:
inputMessage * peekInputMessage(inputMessageQueue * queue)
{
return queue->front;
}
outputMessage * peekOutputMessage(outputMessageQueue * queue)
{
return queue->front;
}

View File

@ -1,21 +1,104 @@
// inputoutput.h: Header file contatning function prototypes and datastructures
// for dealing with input and output.
// Barry Kane, 2022.
#ifndef INPUTOUTPUt_H
#ifndef INPUTOUTPUT_H
#define INPUTOUTPUT_H
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include "constants.h"
#include "playerdata.h"
#include <gnutls/gnutls.h>
// A message datastructure containing a user/character name and the content:
typedef struct userMessage
{
char senderName[32];
char messageContent[2048];
char messageContent[MAX];
} userMessage;
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
void messageSend(gnutls_session_t receivingSession, userMessage * messageToSend);
// =================
// -=[Message I/O]=-
// =================
// Recieves a message from a given TLS session, wraps the calls to gnutls_read:
void messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage);
// 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);
// ===================
// -=[Output Queue]=-:
// ===================
typedef struct outputMessage outputMessage;
typedef struct outputMessage
{
outputMessage * next;
playerInfo * targets[PLAYERCOUNT];
userMessage * content;
} outputMessage;
// A first-in first-out queue for message output to players:
typedef struct outputMessageQueue
{
bool lock;
int currentLength;
outputMessage * back;
outputMessage * front;
} outputMessageQueue;
// Creates and initializes a outputMessageQueue:
outputMessageQueue * createOutputMessageQueue(void);
// Enqueue a userMessage to an outputMessageQueue:
int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue);
// int queueOutputMessage(outputMessageQueue * queue, userMessage * messageToQueue,
// playerInfo * targets, int numberOfTargets);
// Dequeue the front outputMessage from an outputMessageQueue:
int dequeueOutputMessage(outputMessageQueue * queue);
// Return the front outputMessage from an outputMessageQueue:
outputMessage * peekOutputMessage(outputMessageQueue * queue);
// ==================
// -=[Input Queue]=-:
// ==================
typedef struct inputMessage inputMessage;
typedef struct inputMessage
{
inputMessage * next;
playerInfo * sender;
userMessage * content;
} inputMessage;
// A first-in first-out queue for message input from players:
typedef struct inputMessageQueue
{
bool lock;
int currentLength;
inputMessage * back;
inputMessage * front;
} inputMessageQueue;
// Create a inputMessageQueue:
inputMessageQueue * createInputMessageQueue(void);
// Enqueue a userMessage to an inputMessageQueue:
int queueInputMessage(inputMessageQueue * queue, userMessage messageToQueue, playerInfo * sendingPlayer);
// Dequeue the front inputMessage from an inputMessageQueue:
int dequeueInputMessage(inputMessageQueue * queue);
// Return the front inputMessage from an inputMessageQueue:
inputMessage * peekInputMessage(inputMessageQueue * queue);
// ======================
// -=[Input Sanitation]=-
// ======================
// Sanatize user input to ensure it's okay to send to the server:
void userInputSanatize(char * inputString, int length);
#endif

View File

@ -1,6 +1,5 @@
// playerdata.h: Header file containing data structures for player data and function
// prototypes for interacting with said data.
// Barry Kane, 2021.
#ifndef PLAYERDATA_H
#define PLAYERDATA_H
#include <stdlib.h>
@ -33,7 +32,7 @@ int movePlayerToArea(playerInfo * player, char * requestedPath);
// Create an area given a name and description:
playerArea * createArea(char * nameString, char * descriptionString);
// Create a path between two areas given two areas and a string:
int createPath(playerArea * fromArea, playerArea * toArea, char * pathFromString, char * pathToString);
// Create a path between two areas given two areas and two strings:
int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription);
#endif

View File

@ -0,0 +1,35 @@
#include "../misc/inputoutput.h"
#include <stdio.h>
#include <string.h>
int main()
{
userMessage A, B, C;
strncpy(A.senderName, "Bob\0", 32 -1);
strncpy(A.messageContent, "joins the fray!\0", MAX-1);
strncpy(B.senderName, "Alice\0", 32 -1);
strncpy(B.messageContent, "challenges the unknown!\0", MAX -1);
strncpy(C.senderName, "Tom\0", 32 -1);
strncpy(C.messageContent, "Attacks them all!\0", MAX -1);
outputMessageQueue * testQueue = createOutputMessageQueue();
printf("Queue Created.\n");
printf("%d", queueOutputMessage(testQueue, A));
printf("Queued A.\n");
printf("%d", queueOutputMessage(testQueue, B));
printf("Queued B.\n");
printf("%d", queueOutputMessage(testQueue, C));
printf("Queued C.\n");
printf("%s\n", testQueue->front->content->senderName);
printf("%s\n", testQueue->front->content->messageContent);
printf("%s\n", testQueue->front->next->content->senderName);
printf("%s\n", testQueue->front->next->content->messageContent);
printf("%s\n", testQueue->front->next->next->content->senderName);
printf("%s\n", testQueue->front->next->next->content->messageContent);
printf("%s\n", testQueue->front->content->senderName);
dequeueOutputMessage(testQueue);
printf("%s\n", testQueue->front->content->senderName);
dequeueOutputMessage(testQueue);
printf("%s\n", testQueue->front->content->senderName);
// dequeueOutputMessage(testQueue);
// printf("%s\n", testQueue->front->content->senderName);
return 0;
}