Implemented proper thread sleeping and additional output thread
- Replaced previous inefficient "spin-waiting" with proper thread sleeping. - Added threading primitives to the queue type to enable this. - Added additional thread for output management. - Miscellanous cleanup and restructuring.
This commit is contained in:
parent
15d82f59ee
commit
6a653c75b9
|
@ -14,6 +14,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../queue.h"
|
||||
#include "../constants.h"
|
||||
#include "../playerdata.h"
|
||||
#include "../texteffects.h"
|
||||
|
@ -225,7 +226,7 @@ int main(int argc, char ** argv)
|
|||
serverAddress.sin_port = htons(port);
|
||||
|
||||
// Connect the server and client sockets, Kronk:
|
||||
if (connect(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress)) != 0)
|
||||
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);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
// =======================
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicLoop(void * parameters)
|
||||
void * gameLogicHandler(void * parameters)
|
||||
{
|
||||
gameLogicParameters * threadParameters = parameters;
|
||||
inputMessage * currentInput = NULL;
|
||||
|
@ -25,11 +25,17 @@ void * gameLogicLoop(void * parameters)
|
|||
while(true)
|
||||
{
|
||||
// Evaluate remaining commands:
|
||||
if(commandQueue->currentLength != 0)
|
||||
while(commandQueue->currentLength != 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)
|
||||
{
|
||||
|
|
|
@ -8,35 +8,10 @@
|
|||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct queue queue;
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// =======================
|
||||
// -=[ Main Game Loop ]=-:
|
||||
// =======================
|
||||
|
||||
// A data-structure containing the needed parameters for a main game loop:
|
||||
typedef struct gameLogicParameters
|
||||
{
|
||||
// Players:
|
||||
int * playerCount;
|
||||
playerInfo * connectedPlayers;
|
||||
|
||||
// Queues:
|
||||
queue * inputQueue;
|
||||
queue * outputQueue;
|
||||
|
||||
// Lists:
|
||||
list * areaList;
|
||||
list * globalSkillList;
|
||||
} gameLogicParameters;
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicLoop(void * parameters);
|
||||
|
||||
// ======================
|
||||
// -=[ Command Queue ]=-:
|
||||
// ======================
|
||||
typedef struct commandEvent commandEvent;
|
||||
typedef struct commandEvent
|
||||
{
|
||||
|
@ -56,6 +31,32 @@ typedef struct commandQueue
|
|||
commandEvent * front;
|
||||
} commandQueue;
|
||||
|
||||
// A data-structure containing the needed parameters for a main game loop:
|
||||
typedef struct gameLogicParameters
|
||||
{
|
||||
// Players:
|
||||
int * playerCount;
|
||||
playerInfo * connectedPlayers;
|
||||
|
||||
// Queues:
|
||||
queue * inputQueue;
|
||||
queue * outputQueue;
|
||||
|
||||
// Lists:
|
||||
list * areaList;
|
||||
list * globalSkillList;
|
||||
} gameLogicParameters;
|
||||
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Player movement:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicHandler(void * parameters);
|
||||
|
||||
// Create a commandQueue:
|
||||
commandQueue * createCommandQueue(void);
|
||||
|
||||
|
@ -82,9 +83,6 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue);
|
|||
// -=[ Gameplay Primitives ]=-:
|
||||
// ============================
|
||||
|
||||
// Player movement:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
||||
|
||||
typedef enum outcome
|
||||
{
|
||||
CRITICAL_FAILURE,
|
||||
|
@ -94,6 +92,9 @@ typedef enum outcome
|
|||
ERROR
|
||||
} outcome;
|
||||
|
||||
// Player movement:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
||||
|
||||
// Run a stat check:
|
||||
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck);
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
@ -46,6 +49,7 @@ int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToM
|
|||
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:
|
||||
|
@ -64,10 +68,72 @@ outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, player
|
|||
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(outputQueue->itemCount == 0)
|
||||
{
|
||||
pthread_cond_wait(&outputQueue->condition, &outputQueue->mutex);
|
||||
}
|
||||
// Run through the output queue and send all unused 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 targetIndex = 0;
|
||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
if(targetIndex == message->recipientsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(&connectedPlayers[index] == message->recipients[targetIndex])
|
||||
{
|
||||
targetIndex++;
|
||||
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 buisness being here:
|
||||
if(!isprint(inputString[index]))
|
||||
{
|
||||
inputString[index] = '\n';
|
||||
|
@ -75,18 +141,22 @@ void userInputSanatize(char * inputString, int length)
|
|||
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 buisness being here:
|
||||
if(!isprint(inputString[index]))
|
||||
{
|
||||
inputString[index] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Make sure it's null-terminated:
|
||||
inputString[length - 1] = '\0';
|
||||
}
|
||||
|
|
|
@ -4,33 +4,36 @@
|
|||
#ifndef INPUTOUTPUT_H
|
||||
#define INPUTOUTPUT_H
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include <stdbool.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
// A message datastructure containing a user/character name and the content:
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
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;
|
||||
|
||||
// ==================
|
||||
// -=[Message I/O]=-:
|
||||
// ==================
|
||||
// Contains a userMessage and a pointer to the playerInfo of the connection which sent it:
|
||||
typedef struct inputMessage
|
||||
{
|
||||
playerInfo * sender;
|
||||
userMessage * content;
|
||||
} inputMessage;
|
||||
|
||||
// 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;
|
||||
// Contains a userMessage and pointers to the playerInfo of the recipients and the number of them:
|
||||
typedef struct outputMessage
|
||||
{
|
||||
int recipientsCount;
|
||||
|
@ -38,27 +41,34 @@ typedef struct outputMessage
|
|||
playerInfo ** recipients;
|
||||
} outputMessage;
|
||||
|
||||
// Contains the relevant parameters for the outputThreadLoop 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);
|
||||
|
||||
// ==================
|
||||
// -=[Input Queue]=-:
|
||||
// ==================
|
||||
typedef struct inputMessage inputMessage;
|
||||
typedef struct inputMessage
|
||||
{
|
||||
playerInfo * sender;
|
||||
userMessage * content;
|
||||
} inputMessage;
|
||||
// A function for the output thread, which sends queued messages:
|
||||
void * outputThreadHandler(void * parameters);
|
||||
|
||||
// =======================
|
||||
// -=[Input Sanitation]=-:
|
||||
// =======================
|
||||
|
||||
// Sanatize user input to ensure it's okay to send to the server:
|
||||
// Sanatize user input to ensure it's okay to process:
|
||||
void userInputSanatize(char * inputString, int length);
|
||||
|
||||
// Sanatize user names so they display correctly;
|
||||
// Sanatize user names so they display correctly:
|
||||
void userNameSanatize(char * inputString, int length);
|
||||
|
||||
#endif
|
||||
|
|
11
src/queue.c
11
src/queue.c
|
@ -1,5 +1,6 @@
|
|||
// queue.c: Implements the queue data type and associated functions for SilverMUD.
|
||||
// Barry Kane, 2022
|
||||
#include <pthread.h>
|
||||
#include "queue.h"
|
||||
|
||||
// Allocates and instantiates a queue:
|
||||
|
@ -13,6 +14,10 @@ queue * createQueue(void)
|
|||
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;
|
||||
}
|
||||
|
@ -28,6 +33,9 @@ void destroyQueue(queue ** queue)
|
|||
|
||||
// Deallocate the queue:
|
||||
free(*queue);
|
||||
|
||||
// Point the queue pointer to NULL;
|
||||
*queue = NULL;
|
||||
}
|
||||
|
||||
// Returns the data at the front of the given queue:
|
||||
|
@ -203,4 +211,7 @@ void pushQueue(queue * queue, void * data, queueDataType type)
|
|||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
// Flag that the queue was modified:
|
||||
pthread_cond_broadcast(&queue->condition);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct queue queue;
|
||||
|
||||
typedef enum queueDataType
|
||||
{
|
||||
EVENT,
|
||||
|
@ -38,6 +41,8 @@ typedef struct queue
|
|||
size_t itemCount;
|
||||
queueNode * front;
|
||||
queueNode * back;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t condition;
|
||||
} queue;
|
||||
|
||||
// ========================
|
||||
|
|
|
@ -40,7 +40,7 @@ int main(int argc, char ** argv)
|
|||
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
||||
socketCheck, activityCheck, returnVal;
|
||||
fd_set connectedClients;
|
||||
pthread_t gameLogicThread;
|
||||
pthread_t gameLogicThread, outputThread;
|
||||
int clientSockets[PLAYERCOUNT];
|
||||
userMessage sendBuffer, receiveBuffer;
|
||||
playerInfo connectedPlayers[PLAYERCOUNT];
|
||||
|
@ -54,11 +54,11 @@ int main(int argc, char ** argv)
|
|||
{
|
||||
switch(currentopt)
|
||||
{
|
||||
case 'd':
|
||||
{
|
||||
delay = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
delay = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,6 @@ int main(int argc, char ** argv)
|
|||
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
}
|
||||
|
||||
//
|
||||
bzero(&serverAddress, sizeof(serverAddress));
|
||||
|
||||
// Assign IP and port:
|
||||
|
@ -207,11 +206,19 @@ int main(int argc, char ** argv)
|
|||
gameLogicThreadParameters->outputQueue = outputQueue;
|
||||
gameLogicThreadParameters->inputQueue = inputQueue;
|
||||
gameLogicThreadParameters->areaList = areas;
|
||||
pthread_create(&gameLogicThread, NULL, &gameLogicLoop, gameLogicThreadParameters);
|
||||
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);
|
||||
slowPrint("=====\n", delay);
|
||||
struct timeval timeout = {0, 500};
|
||||
|
||||
while(true)
|
||||
{
|
||||
|
@ -239,7 +246,7 @@ int main(int argc, char ** argv)
|
|||
}
|
||||
|
||||
// See if a connection is ready to be interacted with:
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, &timeout);
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL);
|
||||
|
||||
// Check if select() worked:
|
||||
if ((activityCheck < 0) && (errno != EINTR))
|
||||
|
@ -286,7 +293,6 @@ int main(int argc, char ** argv)
|
|||
|
||||
// Push the new message onto the queue:
|
||||
pushQueue(inputQueue, newMessage, INPUT_MESSAGE);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -340,52 +346,8 @@ int main(int argc, char ** argv)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through the output queue and send all unused 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 targetIndex = 0;
|
||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
if(targetIndex == message->recipientsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(&connectedPlayers[index] == message->recipients[targetIndex])
|
||||
{
|
||||
targetIndex++;
|
||||
messageSend(tlssessions[index], message->content);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove the output message from the queue:
|
||||
popQueue(outputQueue);
|
||||
}
|
||||
}
|
||||
pthread_cancel(gameLogicThread);
|
||||
pthread_cancel(outputThread);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue