Began implementing game logic and re-implementing commands
- Reimplemented /MOVE and /EXIT - The server is now multi-threaded - Input and output is now queued
This commit is contained in:
parent
0b3a72beff
commit
151f3002b8
|
@ -279,7 +279,7 @@ int main(int argc, char **argv)
|
|||
pthread_cancel(sendingThread);
|
||||
|
||||
// Close the session and socket:
|
||||
gnutls_bye(tlsSession, GNUTLS_SHUT_RDWR);
|
||||
gnutls_bye(tlsSession, GNUTLS_SHUT_WR);
|
||||
close(socketFileDesc);
|
||||
|
||||
// Free the structs:
|
||||
|
@ -302,3 +302,4 @@ int main(int argc, char **argv)
|
|||
// Say goodbye:
|
||||
slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", characterDelay);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ncurses.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include "misc/lists.h"
|
||||
#include "misc/gamelogic.h"
|
||||
#include "misc/constants.h"
|
||||
#include "misc/playerdata.h"
|
||||
#include "misc/texteffects.h"
|
||||
|
@ -27,6 +29,7 @@ int main()
|
|||
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
||||
socketCheck, activityCheck, returnVal;
|
||||
fd_set connectedClients;
|
||||
pthread_t gameLogicThread;
|
||||
int clientSockets[PLAYERCOUNT];
|
||||
userMessage sendBuffer, receiveBuffer;
|
||||
playerInfo connectedPlayers[PLAYERCOUNT];
|
||||
|
@ -35,11 +38,19 @@ int main()
|
|||
inputMessageQueue * inputQueue = createInputMessageQueue();
|
||||
outputMessageQueue * outputQueue = createOutputMessageQueue();
|
||||
|
||||
// Initialize test areas:
|
||||
areaNode * areas = createAreaList(createArea("Spawn - North", "A large area, mostly empty, as if the designer hadn't bothered to put anything in it, just yet."));
|
||||
addAreaNodeToList(areas, createArea("Spawn - South", "A strange, white void. You feel rather uncomfortable."));
|
||||
addAreaNodeToList(areas, createArea("Temple of Emacs", "A beautifully ornate statue of GNU is above you on a pedestal. Inscribed into the pillar, over and over, is the phrase \"M-x exalt\", in delicate gold letters. You can't help but be awestruck."));
|
||||
createPath(getAreaFromList(areas, 0), getAreaFromList(areas, 1), "To South Spawn", "To North Spawn");
|
||||
createPath(getAreaFromList(areas, 2), getAreaFromList(areas, 1), "Back to South Spawn", "Path to Enlightenment.");
|
||||
|
||||
// Initialize playerdata:
|
||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
sprintf(testString, "UNNAMED %d", index);
|
||||
strcpy(connectedPlayers[index].playerName, testString);
|
||||
connectedPlayers[index].currentArea = getAreaFromList(areas, 0);
|
||||
}
|
||||
|
||||
// Give an intro: Display the Silverkin Industries logo and splash text.
|
||||
|
@ -116,6 +127,16 @@ int main()
|
|||
}
|
||||
slowPrint("\tTLS Sessions Initialization is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
|
||||
// Prepare the game logic thread:
|
||||
gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters));
|
||||
gameLogicThreadParameters->connectedPlayers = connectedPlayers;
|
||||
gameLogicThreadParameters->playerCount = &clientsAmount;
|
||||
gameLogicThreadParameters->outputQueue = outputQueue;
|
||||
gameLogicThreadParameters->inputQueue = inputQueue;
|
||||
pthread_create(&gameLogicThread, NULL, &gameLogicLoop, gameLogicThreadParameters);
|
||||
|
||||
struct timeval timeout = {0, 500};
|
||||
|
||||
while(keepRunning)
|
||||
{
|
||||
// Clear the set of file descriptors and add the master socket:
|
||||
|
@ -142,7 +163,7 @@ int main()
|
|||
}
|
||||
|
||||
// See if a connection is ready to be interacted with:
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL);
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, &timeout);
|
||||
|
||||
// Check if select() worked:
|
||||
if ((activityCheck < 0) && (errno != EINTR))
|
||||
|
@ -188,9 +209,10 @@ int main()
|
|||
|
||||
if(FD_ISSET(socketCheck, &connectedClients))
|
||||
{
|
||||
if(messageReceive(tlssessions[index], &receiveBuffer) == -10)
|
||||
int returnVal = messageReceive(tlssessions[index], &receiveBuffer);
|
||||
if(returnVal == -10 || returnVal == 0)
|
||||
{
|
||||
gnutls_bye(tlssessions[index], GNUTLS_SHUT_RDWR);
|
||||
gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR);
|
||||
gnutls_deinit(tlssessions[index]);
|
||||
shutdown(clientSockets[index], 2);
|
||||
close(clientSockets[index]);
|
||||
|
@ -209,24 +231,47 @@ int main()
|
|||
}
|
||||
}
|
||||
// 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)
|
||||
/* 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)
|
||||
{
|
||||
while(outputQueue->lock);
|
||||
outputQueue->lock = true;
|
||||
outputMessage * message = peekOutputMessage(outputQueue);
|
||||
outputQueue->lock = false;
|
||||
if(message->targets[0] == NULL)
|
||||
{
|
||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
messageSend(tlssessions[index], message->content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int targetIndex = 0;
|
||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
if(message->targets[targetIndex] == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(&connectedPlayers[index] == message->targets[targetIndex])
|
||||
{
|
||||
targetIndex++;
|
||||
messageSend(tlssessions[index], message->content);
|
||||
}
|
||||
}
|
||||
}
|
||||
dequeueOutputMessage(outputQueue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// gamelogic.c: Contains function definitons for dealing with the game's logic.
|
||||
// Barry Kane, 2022.
|
||||
#include <string.h>
|
||||
#include "constants.h"
|
||||
#include "gamelogic.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// =======================
|
||||
// -=[ Main Game Loop ]=-:
|
||||
// =======================
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicLoop(void * parameters)
|
||||
{
|
||||
gameLogicParameters * threadParameters = parameters;
|
||||
inputMessage * currentInput = NULL;
|
||||
bool keepRunning = true;
|
||||
while(keepRunning)
|
||||
{
|
||||
if(threadParameters->inputQueue->currentLength != 0)
|
||||
{
|
||||
while(threadParameters->inputQueue->lock == true)
|
||||
{
|
||||
threadParameters->inputQueue->lock = true;
|
||||
}
|
||||
currentInput = peekInputMessage(threadParameters->inputQueue);
|
||||
userInputSanatize(currentInput->content->messageContent, MAX);
|
||||
if(currentInput->content->messageContent[0] == '/')
|
||||
{
|
||||
// TODO: Implement Command Queue.
|
||||
// For now, basic intepretation will do.
|
||||
if(strncmp(¤tInput->content->messageContent[1], "EXIT", 4) == 0)
|
||||
{
|
||||
userMessage * exitMessage = malloc(sizeof(userMessage));
|
||||
exitMessage->senderName[0] = '\0';
|
||||
exitMessage->messageContent[0] = '\0';
|
||||
queueTargetedOutputMessage(threadParameters->outputQueue, exitMessage, ¤tInput->sender, 1);
|
||||
free(exitMessage);
|
||||
}
|
||||
if(strncmp(¤tInput->content->messageContent[1], "MOVE", 4) == 0)
|
||||
{
|
||||
userMessage * moveMessage = malloc(sizeof(userMessage));
|
||||
char requestedPath[32];
|
||||
strncpy(requestedPath, ¤tInput->content->messageContent[6], 32);
|
||||
userInputSanatize(requestedPath, 32);
|
||||
// Remove newlines:
|
||||
for (int index = 0; index < 32; index++)
|
||||
{
|
||||
if (requestedPath[index] == '\n')
|
||||
{
|
||||
requestedPath[index] = '\0';
|
||||
}
|
||||
}
|
||||
requestedPath[31] = '\0';
|
||||
if(movePlayerToArea(currentInput->sender, requestedPath) == 0)
|
||||
{
|
||||
strcpy(moveMessage->senderName, "\0");
|
||||
strcpy(moveMessage->messageContent, currentInput->sender->currentArea->areaDescription);
|
||||
queueTargetedOutputMessage(threadParameters->outputQueue, moveMessage, ¤tInput->sender, 1);
|
||||
}
|
||||
free(moveMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(currentInput->content->senderName, currentInput->sender->playerName, 32);
|
||||
// Create an array of players in the same area to receive the message:
|
||||
playerInfo ** recipients = malloc(sizeof(playerInfo*) * *threadParameters->playerCount);
|
||||
for(int index = 0; index < *threadParameters->playerCount; index++)
|
||||
{
|
||||
recipients[index] = NULL;
|
||||
}
|
||||
int recipientCount = 0;
|
||||
for(int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++)
|
||||
{
|
||||
if(threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea)
|
||||
{
|
||||
recipients[recipientCount] = &threadParameters->connectedPlayers[playerIndex];
|
||||
recipientCount++;
|
||||
}
|
||||
}
|
||||
if(currentInput->content->messageContent[0] != '\n')
|
||||
{
|
||||
queueTargetedOutputMessage(threadParameters->outputQueue, currentInput->content, recipients, recipientCount);
|
||||
}
|
||||
free(recipients);
|
||||
}
|
||||
currentInput = NULL;
|
||||
threadParameters->inputQueue->lock = false;
|
||||
dequeueInputMessage(threadParameters->inputQueue);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// gamelogic.h: Header file contatning function prototypes and datastructures
|
||||
// for dealing with the game's logic.
|
||||
// Barry Kane, 2022.
|
||||
#ifndef GAMELOGIC_H
|
||||
#define GAMELOGIC_H
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// =======================
|
||||
// -=[ Main Game Loop ]=-:
|
||||
// =======================
|
||||
|
||||
// A datastructure containing the needed parameters for a main game loop:
|
||||
typedef struct gameLogicParameters
|
||||
{
|
||||
int * playerCount;
|
||||
playerInfo * connectedPlayers;
|
||||
inputMessageQueue * inputQueue;
|
||||
outputMessageQueue * outputQueue;
|
||||
} gameLogicParameters;
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicLoop(void * parameters);
|
||||
|
||||
#endif
|
|
@ -57,17 +57,21 @@ outputMessageQueue * createOutputMessageQueue(void)
|
|||
int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue)
|
||||
{
|
||||
// Copy the message into a new output message:
|
||||
outputMessage * outputMessage = malloc(sizeof(outputMessage));
|
||||
outputMessage * newOutputMessage = malloc(sizeof(outputMessage));
|
||||
|
||||
// Allocate the internal userMessage to store the message:
|
||||
outputMessage->content = malloc(sizeof(userMessage));
|
||||
newOutputMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Allocate the internal strings to store the message:
|
||||
//outputMessage->content->senderName = malloc(sizeof(char)*32);
|
||||
//outputMessage->content->messageContent = malloc(sizeof(char)*MAX);
|
||||
|
||||
// Copy the userMessage to the internal userMessage:
|
||||
strncpy(outputMessage->content->senderName, messageToQueue.senderName, 32);
|
||||
strncpy(outputMessage->content->messageContent, messageToQueue.messageContent, MAX);
|
||||
strncpy(newOutputMessage->content->senderName, messageToQueue.senderName, 32);
|
||||
strncpy(newOutputMessage->content->messageContent, messageToQueue.messageContent, MAX);
|
||||
|
||||
// We have no targets, NULL sends to all players in an area:
|
||||
outputMessage->targets[0] = NULL;
|
||||
newOutputMessage->targets[0] = NULL;
|
||||
|
||||
// Wait for the queue to unlock:
|
||||
while (queue->lock);
|
||||
|
@ -87,24 +91,85 @@ int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue)
|
|||
// 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->front = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = outputMessage;
|
||||
queue->back = outputMessage;
|
||||
queue->back->next = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int queueTargetedOutputMessage(outputMessageQueue * queue,
|
||||
userMessage * messageToQueue, playerInfo ** targets, int numberOfTargets)
|
||||
{
|
||||
// Copy the message into a new output message:
|
||||
outputMessage * newOutputMessage = malloc(sizeof(outputMessage));
|
||||
|
||||
// Allocate the internal userMessage to store the message:
|
||||
newOutputMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Set the appropriate recipients:
|
||||
for(int index = 0; index < numberOfTargets && index < PLAYERCOUNT; index++)
|
||||
{
|
||||
newOutputMessage->targets[index] = targets[index];
|
||||
}
|
||||
for(int index = numberOfTargets; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
newOutputMessage->targets[index] = NULL;
|
||||
}
|
||||
|
||||
// Copy the userMessage to the internal userMessage:
|
||||
strncpy(newOutputMessage->content->senderName, messageToQueue->senderName, 32);
|
||||
strncpy(newOutputMessage->content->messageContent, messageToQueue->messageContent, MAX);
|
||||
|
||||
|
||||
// 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 = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ typedef struct userMessage
|
|||
char messageContent[MAX];
|
||||
} userMessage;
|
||||
|
||||
// =================
|
||||
// -=[Message I/O]=-
|
||||
// =================
|
||||
// ==================
|
||||
// -=[Message I/O]=-:
|
||||
// ==================
|
||||
|
||||
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
|
||||
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend);
|
||||
|
@ -52,8 +52,8 @@ 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);
|
||||
int queueTargetedOutputMessage(outputMessageQueue * queue, userMessage * messageToQueue,
|
||||
playerInfo ** targets, int numberOfTargets);
|
||||
|
||||
// Dequeue the front outputMessage from an outputMessageQueue:
|
||||
int dequeueOutputMessage(outputMessageQueue * queue);
|
||||
|
@ -93,9 +93,9 @@ int dequeueInputMessage(inputMessageQueue * queue);
|
|||
// Return the front inputMessage from an inputMessageQueue:
|
||||
inputMessage * peekInputMessage(inputMessageQueue * queue);
|
||||
|
||||
// ======================
|
||||
// -=[Input Sanitation]=-
|
||||
// ======================
|
||||
// =======================
|
||||
// -=[Input Sanitation]=-:
|
||||
// =======================
|
||||
|
||||
// Sanatize user input to ensure it's okay to send to the server:
|
||||
void userInputSanatize(char * inputString, int length);
|
||||
|
|
Loading…
Reference in New Issue