Began implementation of skills and stats.
- Added text wrapping in client. - Implemented functions for managing skill data. - Rewrote some existing functionality to allow for variable length game messages over multiple userMessages. - Reorganized the code yet again. - Introduced enums for coreStats and outcomes. - Implemented core stat checks. - Added more example skills. - Rewrote test areas to have longer descriptions.
This commit is contained in:
parent
8673bb1ad5
commit
b8189ae2de
2
Makefile
2
Makefile
|
@ -25,5 +25,5 @@ clean:
|
||||||
|
|
||||||
all: SilverMUDClient SilverMUDServer
|
all: SilverMUDClient SilverMUDServer
|
||||||
|
|
||||||
debug: CFLAGS += -Wall -ggdb
|
debug: CFLAGS += -Wall -ggdb -Wextra
|
||||||
debug: SilverMUDClientDebug SilverMUDServerDebug
|
debug: SilverMUDClientDebug SilverMUDServerDebug
|
||||||
|
|
|
@ -4,5 +4,25 @@ 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
|
to improvise that they have at the table, through simple programming and
|
||||||
easy-to-understand structures.
|
easy-to-understand structures.
|
||||||
** Player's Guide
|
** Player's Guide
|
||||||
|
*** 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 | Takes a character name | Logs you into the server with the given character name. |
|
||||||
|
| MOVE | Takes a path name or a path number | Moves you down the given path. |
|
||||||
|
| LOOK | None | Gives you a description of what's around you, and what you can do. |
|
||||||
|
| 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 or skill name and an object number | Attempt to use the given stat or skill on the object. |
|
||||||
|
|
||||||
** Gamemaster's Guide
|
** Gamemaster's Guide
|
||||||
|
*** Running the Server:
|
||||||
|
|
||||||
** Developer's Guide
|
** Developer's Guide
|
||||||
|
*** Build Prerequisites:
|
||||||
|
SilverMUD has the following dependencies:
|
||||||
|
- GnuTLS
|
||||||
|
- ncurses
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
// 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
|
|
|
@ -1,38 +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 <stdlib.h>
|
|
||||||
|
|
||||||
typedef struct playerPath playerPath;
|
|
||||||
typedef struct playerArea playerArea;
|
|
||||||
|
|
||||||
struct playerPath
|
|
||||||
{
|
|
||||||
char pathName[32];
|
|
||||||
playerArea * areaToJoin;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct playerArea
|
|
||||||
{
|
|
||||||
char areaName[32];
|
|
||||||
char areaDescription[256];
|
|
||||||
playerPath * areaExits[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct playerInfo
|
|
||||||
{
|
|
||||||
char playerName[32];
|
|
||||||
playerArea * currentArea;
|
|
||||||
} playerInfo;
|
|
||||||
|
|
||||||
// Move a player to a different area given a path in the area:
|
|
||||||
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 two strings:
|
|
||||||
int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -13,12 +13,12 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include "../../include/constants.h"
|
#include "../constants.h"
|
||||||
#include "../../include/playerdata.h"
|
#include "../playerdata.h"
|
||||||
#include "../../include/texteffects.h"
|
#include "../texteffects.h"
|
||||||
#include "../../include/inputoutput.h"
|
#include "../inputoutput.h"
|
||||||
|
|
||||||
// A struct for passing arguments to our threads containing a file descriptor and a window pointer:
|
// A struct for bundling all needed paramaters for a thread so we can pass them using a void pointer:
|
||||||
typedef struct threadparameters
|
typedef struct threadparameters
|
||||||
{
|
{
|
||||||
gnutls_session_t tlsSession;
|
gnutls_session_t tlsSession;
|
||||||
|
@ -72,26 +72,37 @@ void * messageSender(void * parameters)
|
||||||
void * messageReceiver(void * parameters)
|
void * messageReceiver(void * parameters)
|
||||||
{
|
{
|
||||||
struct threadparameters *threadParameters = parameters;
|
struct threadparameters *threadParameters = parameters;
|
||||||
|
bool serverMessage = false;
|
||||||
userMessage receiveBuffer;
|
userMessage receiveBuffer;
|
||||||
|
int screenWidth = getmaxx(threadParameters->window);
|
||||||
|
|
||||||
// Repeatedly take messages from the server and print them to the chat log window:
|
// Repeatedly take messages from the server and print them to the chat log window:
|
||||||
while (!shouldExit)
|
while (!shouldExit)
|
||||||
{
|
{
|
||||||
messageReceive(threadParameters->tlsSession, &receiveBuffer);
|
messageReceive(threadParameters->tlsSession, &receiveBuffer);
|
||||||
if (receiveBuffer.senderName[0] == '\0')
|
if (receiveBuffer.senderName[0] == '\0')
|
||||||
{
|
{
|
||||||
|
wrapString(receiveBuffer.messageContent,
|
||||||
|
strlen(receiveBuffer.messageContent) - 1, screenWidth);
|
||||||
if (receiveBuffer.messageContent[0] == '\0')
|
if (receiveBuffer.messageContent[0] == '\0')
|
||||||
{
|
{
|
||||||
shouldExit = true;
|
shouldExit = true;
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
slowPrintNcurses("\n --====<", 8000, threadParameters->window, true);
|
if(serverMessage == false)
|
||||||
slowPrintNcurses(">====-- \n", 8000, threadParameters->window, true);
|
{
|
||||||
slowPrintNcurses(receiveBuffer.messageContent, 8000, threadParameters->window, false);
|
slowPrintNcurses("\n --====<>====--", 4000, threadParameters->window, true);
|
||||||
slowPrintNcurses("\n --====<>====-- \n", 8000, threadParameters->window, true);
|
serverMessage = true;
|
||||||
|
}
|
||||||
|
slowPrintNcurses("\n", 4000, threadParameters->window, true);
|
||||||
|
slowPrintNcurses(receiveBuffer.messageContent, 4000, threadParameters->window, false);
|
||||||
|
slowPrintNcurses("\n", 4000, threadParameters->window, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
wrapString(receiveBuffer.messageContent,
|
||||||
|
strlen(receiveBuffer.messageContent) - 1,
|
||||||
|
screenWidth - strlen(receiveBuffer.senderName) - 2);
|
||||||
if (threadParameters->loggingFlag == true)
|
if (threadParameters->loggingFlag == true)
|
||||||
{
|
{
|
||||||
fputs(receiveBuffer.senderName, threadParameters->loggingStream);
|
fputs(receiveBuffer.senderName, threadParameters->loggingStream);
|
||||||
|
@ -99,9 +110,14 @@ void * messageReceiver(void * parameters)
|
||||||
fputs(receiveBuffer.messageContent, threadParameters->loggingStream);
|
fputs(receiveBuffer.messageContent, threadParameters->loggingStream);
|
||||||
fflush(threadParameters->loggingStream);
|
fflush(threadParameters->loggingStream);
|
||||||
}
|
}
|
||||||
slowPrintNcurses(receiveBuffer.senderName, 8000, threadParameters->window, true);
|
if(serverMessage == true)
|
||||||
slowPrintNcurses(": ", 8000, threadParameters->window, true);
|
{
|
||||||
slowPrintNcurses(receiveBuffer.messageContent, 8000, threadParameters->window, false);
|
slowPrintNcurses("\n --====<>====-- \n", 4000, threadParameters->window, true);
|
||||||
|
serverMessage = false;
|
||||||
|
}
|
||||||
|
slowPrintNcurses(receiveBuffer.senderName, 4000, threadParameters->window, true);
|
||||||
|
slowPrintNcurses(": ", 4000, threadParameters->window, true);
|
||||||
|
slowPrintNcurses(receiveBuffer.messageContent, 4000, threadParameters->window, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
|
@ -115,7 +131,7 @@ int main(int argc, char **argv)
|
||||||
pthread_t receivingThread;
|
pthread_t receivingThread;
|
||||||
int port = 5000;
|
int port = 5000;
|
||||||
int currentopt = 0;
|
int currentopt = 0;
|
||||||
int characterDelay = 8000;
|
int characterDelay = 4000;
|
||||||
char chatLogPath[PATH_MAX + 1];
|
char chatLogPath[PATH_MAX + 1];
|
||||||
char gameLogPath[PATH_MAX + 1];
|
char gameLogPath[PATH_MAX + 1];
|
||||||
char ipAddress[32] = "127.0.0.1";
|
char ipAddress[32] = "127.0.0.1";
|
||||||
|
@ -248,7 +264,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
logArea = malloc(sizeof(*logArea));
|
logArea = malloc(sizeof(*logArea));
|
||||||
messageArea = malloc(sizeof(*messageArea));
|
messageArea = malloc(sizeof(*messageArea));
|
||||||
|
|
||||||
// Make the windows for the structs, and pass the socket descriptor:
|
// Make the windows for the structs, and pass the socket descriptor:
|
||||||
logArea->window = newwin(LINES - 5, COLS - 2, 1, 1);
|
logArea->window = newwin(LINES - 5, COLS - 2, 1, 1);
|
||||||
logArea->tlsSession = tlsSession;
|
logArea->tlsSession = tlsSession;
|
||||||
|
@ -257,7 +273,7 @@ int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
logArea->loggingStream = chatLog;
|
logArea->loggingStream = chatLog;
|
||||||
}
|
}
|
||||||
messageArea->window = newwin(3, COLS, LINES - 3, 0);
|
messageArea->window = newwin(3, COLS - 2, LINES - 4, 1);
|
||||||
messageArea->tlsSession = tlsSession;
|
messageArea->tlsSession = tlsSession;
|
||||||
messageArea->loggingFlag = gameLogging;
|
messageArea->loggingFlag = gameLogging;
|
||||||
if (gameLog != NULL)
|
if (gameLog != NULL)
|
||||||
|
@ -269,7 +285,7 @@ int main(int argc, char **argv)
|
||||||
scrollok(logArea->window, true);
|
scrollok(logArea->window, true);
|
||||||
scrollok(messageArea->window, true);
|
scrollok(messageArea->window, true);
|
||||||
|
|
||||||
// Run a thread to send messages, and use main to recieve:
|
// Run a thread to send messages, and use another to recieve:
|
||||||
pthread_create(&sendingThread, NULL, messageSender, messageArea);
|
pthread_create(&sendingThread, NULL, messageSender, messageArea);
|
||||||
pthread_create(&receivingThread, NULL, messageReceiver, logArea);
|
pthread_create(&receivingThread, NULL, messageReceiver, logArea);
|
||||||
|
|
||||||
|
|
741
src/gamelogic.c
741
src/gamelogic.c
|
@ -1,11 +1,13 @@
|
||||||
// gamelogic.c: Contains function definitons for dealing with the game's logic.
|
// gamelogic.c: Contains function definitons for dealing with the game's logic.
|
||||||
// Barry Kane, 2022.
|
// Barry Kane, 2022.
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../include/constants.h"
|
#include <stdbool.h>
|
||||||
#include "../include/gamelogic.h"
|
#include "constants.h"
|
||||||
#include "../include/playerdata.h"
|
#include "gamelogic.h"
|
||||||
#include "../include/inputoutput.h"
|
#include "playerdata.h"
|
||||||
|
#include "inputoutput.h"
|
||||||
|
|
||||||
// =======================
|
// =======================
|
||||||
// -=[ Main Game Loop ]=-:
|
// -=[ Main Game Loop ]=-:
|
||||||
|
@ -14,103 +16,34 @@
|
||||||
// Thread function which runs the main game loop, given the needed parameters:
|
// Thread function which runs the main game loop, given the needed parameters:
|
||||||
void * gameLogicLoop(void * parameters)
|
void * gameLogicLoop(void * parameters)
|
||||||
{
|
{
|
||||||
char formattedString[64];
|
|
||||||
gameLogicParameters * threadParameters = parameters;
|
gameLogicParameters * threadParameters = parameters;
|
||||||
inputMessage * currentInput = NULL;
|
inputMessage * currentInput = NULL;
|
||||||
bool keepRunning = true;
|
bool keepRunning = true;
|
||||||
|
commandQueue * commandQueue = createCommandQueue();
|
||||||
while(keepRunning)
|
while(keepRunning)
|
||||||
{
|
{
|
||||||
|
// Evaluate remaining commands:
|
||||||
|
if(commandQueue->currentLength != 0)
|
||||||
|
{
|
||||||
|
evaluateNextCommand(threadParameters, commandQueue);
|
||||||
|
}
|
||||||
// Check for new messages and pop them off the queue:
|
// Check for new messages and pop them off the queue:
|
||||||
if(threadParameters->inputQueue->currentLength != 0)
|
if(threadParameters->inputQueue->currentLength != 0)
|
||||||
{
|
{
|
||||||
while(threadParameters->inputQueue->lock == true)
|
while(threadParameters->inputQueue->lock == true);
|
||||||
{
|
threadParameters->inputQueue->lock = true;
|
||||||
threadParameters->inputQueue->lock = true;
|
|
||||||
}
|
|
||||||
currentInput = peekInputMessage(threadParameters->inputQueue);
|
currentInput = peekInputMessage(threadParameters->inputQueue);
|
||||||
userInputSanatize(currentInput->content->messageContent, MAX);
|
userInputSanatize(currentInput->content->messageContent, MAX);
|
||||||
// A slash as the first character means the message is a user command:
|
// A slash as the first character means the message is a user command:
|
||||||
if(currentInput->content->messageContent[0] == '/')
|
if(currentInput->content->messageContent[0] == '/')
|
||||||
{
|
{
|
||||||
// TODO: Implement Command Queue.
|
queueMessagedCommand(commandQueue, currentInput);
|
||||||
// For now, basic intepretation will do.
|
}
|
||||||
|
else if(currentInput->sender->currentArea == getAreaFromList(threadParameters->areaList, 0))
|
||||||
// Exit command: Sends an "empty" exit message to disconnect a client:
|
{
|
||||||
if(strncmp(¤tInput->content->messageContent[1], "EXIT", 4) == 0 ||
|
currentInput = NULL;
|
||||||
strncmp(¤tInput->content->messageContent[1], "exit", 4) == 0)
|
threadParameters->inputQueue->lock = false;
|
||||||
{
|
dequeueInputMessage(threadParameters->inputQueue);
|
||||||
userMessage * exitMessage = malloc(sizeof(userMessage));
|
|
||||||
exitMessage->senderName[0] = '\0';
|
|
||||||
exitMessage->messageContent[0] = '\0';
|
|
||||||
queueTargetedOutputMessage(threadParameters->outputQueue, exitMessage, ¤tInput->sender, 1);
|
|
||||||
free(exitMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move command: Moves the current player down a path in their current area, given a pathname or number:
|
|
||||||
if(strncmp(¤tInput->content->messageContent[1], "MOVE", 4) == 0 ||
|
|
||||||
strncmp(¤tInput->content->messageContent[1], "move", 4) == 0)
|
|
||||||
{
|
|
||||||
userMessage * moveMessage = malloc(sizeof(userMessage));
|
|
||||||
bzero(moveMessage, 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)
|
|
||||||
{
|
|
||||||
moveMessage->senderName[0] = '\0';
|
|
||||||
strncat(moveMessage->messageContent, currentInput->sender->currentArea->areaName, 33);
|
|
||||||
strncat(moveMessage->messageContent, "\n", 2);
|
|
||||||
strncat(moveMessage->messageContent, currentInput->sender->currentArea->areaDescription, 256);
|
|
||||||
strncat(moveMessage->messageContent, "\nYou can go:", 13);
|
|
||||||
for(int index = 0; index < 16; index++)
|
|
||||||
{
|
|
||||||
if(currentInput->sender->currentArea->areaExits[index] != NULL)
|
|
||||||
{
|
|
||||||
snprintf(formattedString, 64, "\n\t%d. %s", index + 1, currentInput->sender->currentArea->areaExits[index]->pathName);
|
|
||||||
strncat(moveMessage->messageContent, formattedString, 64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queueTargetedOutputMessage(threadParameters->outputQueue, moveMessage, ¤tInput->sender, 1);
|
|
||||||
}
|
|
||||||
free(moveMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look command: Sends the current area's name, description, and
|
|
||||||
if(strncmp(¤tInput->content->messageContent[1], "LOOK", 4) == 0 ||
|
|
||||||
strncmp(¤tInput->content->messageContent[1], "look", 4) == 0)
|
|
||||||
{
|
|
||||||
userMessage * lookMessage = malloc(sizeof(userMessage));
|
|
||||||
strncat(lookMessage->messageContent, currentInput->sender->currentArea->areaName, 33);
|
|
||||||
strncat(lookMessage->messageContent, "\n", 2);
|
|
||||||
strncat(lookMessage->messageContent, currentInput->sender->currentArea->areaDescription, 256);
|
|
||||||
strncat(lookMessage->messageContent, "\nYou can go:", 13);
|
|
||||||
for(int index = 0; index < 16; index++)
|
|
||||||
{
|
|
||||||
if(currentInput->sender->currentArea->areaExits[index] != NULL)
|
|
||||||
{
|
|
||||||
snprintf(formattedString, 64, "\n\t%d. %s", index + 1, currentInput->sender->currentArea->areaExits[index]->pathName);
|
|
||||||
strncat(lookMessage->messageContent, formattedString, 64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queueTargetedOutputMessage(threadParameters->outputQueue, lookMessage, ¤tInput->sender, 1);
|
|
||||||
free(lookMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name command: Checks if the name is isn't used and is valid, then changes the player's name:
|
|
||||||
if(strncmp(¤tInput->content->messageContent[1], "NAME", 4) == 0 ||
|
|
||||||
strncmp(¤tInput->content->messageContent[1], "name", 4) == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -143,3 +76,635 @@ void * gameLogicLoop(void * parameters)
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a commandQueue:
|
||||||
|
commandQueue * createCommandQueue(void)
|
||||||
|
{
|
||||||
|
commandQueue * newCommandQueue = calloc(1, sizeof(commandQueue));
|
||||||
|
newCommandQueue->back = NULL;
|
||||||
|
newCommandQueue->front = NULL;
|
||||||
|
newCommandQueue->lock = false;
|
||||||
|
newCommandQueue->paused = false;
|
||||||
|
newCommandQueue->currentLength = 0;
|
||||||
|
return newCommandQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the front commandEvent from a commandQueue:
|
||||||
|
commandEvent * peekCommand(commandQueue * queue)
|
||||||
|
{
|
||||||
|
// Do nothing until the command queue is unlocked.
|
||||||
|
while(queue->lock);
|
||||||
|
|
||||||
|
// Return the front item.
|
||||||
|
return queue->front;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue a messaged command to a commandQueue:
|
||||||
|
int queueMessagedCommand(commandQueue * 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:
|
||||||
|
strncpy(newCommand->command, &messageToQueue->content->messageContent[1], 16);
|
||||||
|
strncpy(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);
|
||||||
|
userNameSanatize(newCommand->arguments, MAX);
|
||||||
|
|
||||||
|
// Lowercase the command for easier comparison:
|
||||||
|
for (char * character = newCommand->command; *character; ++character)
|
||||||
|
{
|
||||||
|
*character = tolower(*character);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the queue to unlock:
|
||||||
|
while (queue->lock);
|
||||||
|
|
||||||
|
// 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 commandEvent as both the front and back of the queue:
|
||||||
|
if(queue->front == NULL)
|
||||||
|
{
|
||||||
|
queue->front = newCommand;
|
||||||
|
queue->back = newCommand;
|
||||||
|
queue->currentLength++;
|
||||||
|
|
||||||
|
// Unlock the queue:
|
||||||
|
queue->lock = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue->back->next = newCommand;
|
||||||
|
queue->back = newCommand;
|
||||||
|
queue->currentLength++;
|
||||||
|
|
||||||
|
// Unlock the queue:
|
||||||
|
queue->lock = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue a command to a commandQueue:
|
||||||
|
int queueCommand(commandQueue * 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);
|
||||||
|
strncpy(newCommand->arguments, arguments, argumentsLength);
|
||||||
|
|
||||||
|
// Ensure the arguments are safe to parse, without adding newlines:
|
||||||
|
userNameSanatize(newCommand->command, 16);
|
||||||
|
|
||||||
|
// Wait for the queue to unlock:
|
||||||
|
while (queue->lock);
|
||||||
|
|
||||||
|
// 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 commandEvent as both the front and back of the queue:
|
||||||
|
if(queue->front == NULL)
|
||||||
|
{
|
||||||
|
queue->front = newCommand;
|
||||||
|
queue->back = newCommand;
|
||||||
|
queue->currentLength++;
|
||||||
|
|
||||||
|
// Unlock the queue:
|
||||||
|
queue->lock = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue->back->next = newCommand;
|
||||||
|
queue->back = newCommand;
|
||||||
|
queue->currentLength++;
|
||||||
|
|
||||||
|
// Unlock the queue:
|
||||||
|
queue->lock = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue the front commandEvent from a commandQueue:
|
||||||
|
int dequeueCommand(commandQueue * 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->command);
|
||||||
|
free(queue->front->arguments);
|
||||||
|
free(queue->front);
|
||||||
|
queue->front = NULL;
|
||||||
|
queue->back = NULL;
|
||||||
|
queue->currentLength--;
|
||||||
|
queue->lock = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the front item:
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commandEvent * commandToDelete = queue->front;
|
||||||
|
queue->front = queue->front->next;
|
||||||
|
free(commandToDelete->command);
|
||||||
|
free(commandToDelete->arguments);
|
||||||
|
free(commandToDelete);
|
||||||
|
queue->currentLength--;
|
||||||
|
queue->lock = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the next commandEvent:
|
||||||
|
int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
||||||
|
{
|
||||||
|
commandEvent * currentCommand = peekCommand(queue);
|
||||||
|
while(queue->lock);
|
||||||
|
queue->lock = true;
|
||||||
|
if(currentCommand == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Try command: Attempt to use a stat or skill on an object:
|
||||||
|
if(strncmp(currentCommand->command, "try", 3) == 0)
|
||||||
|
{
|
||||||
|
userMessage * tryMessage = malloc(sizeof(userMessage));
|
||||||
|
tryMessage->senderName[0] = '\0';
|
||||||
|
switch (getCoreStatFromString(currentCommand->arguments, 9))
|
||||||
|
{
|
||||||
|
case STRENGTH:
|
||||||
|
{
|
||||||
|
switch (statCheck(currentCommand->caller, 20, STRENGTH))
|
||||||
|
{
|
||||||
|
case CRITICAL_FAILURE:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "You weak, puny shit. Bet you don't even lift, bro.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FAILURE:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "Come on, bro, you should be able to get this set done.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SUCCESS:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "Nice set, bro. Keep it up.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CRITICAL_SUCCESS:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "HOLY SHIT, BRO! THAT'S SOME MAD REPS RIGHT THERE!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "I don't even, bro.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
strcpy(tryMessage->messageContent, "Not at the moment, mate.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, tryMessage, ¤tCommand->caller, 1);
|
||||||
|
free(tryMessage);
|
||||||
|
}
|
||||||
|
// Exit command: Sends an "empty" exit message to disconnect a client:
|
||||||
|
if(strncmp(currentCommand->command, "exit", 4) == 0)
|
||||||
|
{
|
||||||
|
userMessage * exitMessage = malloc(sizeof(userMessage));
|
||||||
|
exitMessage->senderName[0] = '\0';
|
||||||
|
exitMessage->messageContent[0] = '\0';
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, exitMessage, ¤tCommand->caller, 1);
|
||||||
|
free(exitMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move command: Moves the caller to a different area given a path name or number:
|
||||||
|
if(strncmp(currentCommand->command, "move", 4) == 0)
|
||||||
|
{
|
||||||
|
char requestedPath[32];
|
||||||
|
if(strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getAreaFromList(parameters->areaList, 0))
|
||||||
|
{
|
||||||
|
strncpy(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look command: Returns the description of the current area and paths:
|
||||||
|
if(strncmp(currentCommand->command, "look", 4) == 0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
|
||||||
|
bzero(lookMessage, sizeof(userMessage));
|
||||||
|
if(currentCommand->caller->currentArea->areaExits[0] != NULL)
|
||||||
|
{
|
||||||
|
strncat(lookMessage->messageContent, "You can go:", 13);
|
||||||
|
for(int index = 0; index < 16; index++)
|
||||||
|
{
|
||||||
|
if(currentCommand->caller->currentArea->areaExits[index] != NULL)
|
||||||
|
{
|
||||||
|
snprintf(formattedString, 64, "\n\t%d. %s", index + 1, currentCommand->caller->currentArea->areaExits[index]->pathName);
|
||||||
|
strncat(lookMessage->messageContent, formattedString, 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
|
||||||
|
}
|
||||||
|
free(lookMessage);
|
||||||
|
}
|
||||||
|
// Join command: Allows the player to join the game given a name:
|
||||||
|
// TODO: Implement login/character creation. Will be a while:
|
||||||
|
if(strncmp(currentCommand->command, "join", 4) == 0)
|
||||||
|
{
|
||||||
|
if(currentCommand->caller->currentArea == getAreaFromList(parameters->areaList, 0))
|
||||||
|
{
|
||||||
|
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 = getAreaFromList(parameters->areaList, 1);
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Talk command: Allows the player to begin a chat session with another player:
|
||||||
|
if(strncmp(currentCommand->command, "talk", 4) == 0)
|
||||||
|
{
|
||||||
|
// TODO: Implement.
|
||||||
|
}
|
||||||
|
if(strncmp(currentCommand->command, "skillissue", 10) == 0)
|
||||||
|
{
|
||||||
|
userMessage * statMessage = calloc(1, sizeof(userMessage));
|
||||||
|
statMessage->senderName[0] = '\0';
|
||||||
|
strcpy(statMessage->messageContent, "Have you tried getting good?");
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||||
|
free(statMessage);
|
||||||
|
}
|
||||||
|
// Stat command: Displays the current character's sheet.
|
||||||
|
if(strncmp(currentCommand->command, "stat", 4) == 0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||||
|
bzero(statMessage->messageContent, sizeof(char) * MAX);
|
||||||
|
if(currentCommand->caller->skills->head != NULL)
|
||||||
|
{
|
||||||
|
skillNode * currentSkill = currentCommand->caller->skills->head;
|
||||||
|
int charCount = 0;
|
||||||
|
bool addNewline = false;
|
||||||
|
while(currentSkill != NULL)
|
||||||
|
{
|
||||||
|
snprintf(formattedString, 120, "| %2d | %31s ",
|
||||||
|
currentSkill->skill->skillPoints, currentSkill->skill->skillName);
|
||||||
|
charCount += 43;
|
||||||
|
strncat(statMessage->messageContent, formattedString, 120);
|
||||||
|
if((charCount + 43) >= MAX)
|
||||||
|
{
|
||||||
|
strncat(statMessage->messageContent, "\n", 2);
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||||
|
bzero(statMessage, sizeof(userMessage));
|
||||||
|
charCount = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(addNewline)
|
||||||
|
{
|
||||||
|
strncat(statMessage->messageContent, "|\n", 3);
|
||||||
|
charCount++;
|
||||||
|
addNewline = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addNewline = true;
|
||||||
|
}
|
||||||
|
currentSkill = currentSkill->next;
|
||||||
|
|
||||||
|
}
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||||
|
}
|
||||||
|
free(statMessage);
|
||||||
|
free(formattedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec command: Assign spec points to stats:
|
||||||
|
if(strncmp(currentCommand->command, "spec", 4) == 0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message:
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, specMessage, ¤tCommand->caller, 1);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
if(strncmp(currentCommand->command, "skill", 5) == 0)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, skillMessage, ¤tCommand->caller, 1);
|
||||||
|
free(skillMessage);
|
||||||
|
}
|
||||||
|
if(strncmp(currentCommand->command, "listskills", 10) == 0)
|
||||||
|
{
|
||||||
|
skillNode * currentSkill = parameters->globalSkillList->head;
|
||||||
|
userMessage * listMessage = calloc(1, sizeof(userMessage));
|
||||||
|
char * formattedString = calloc(121, sizeof(char));
|
||||||
|
int charCount = 0;
|
||||||
|
bool addNewline = false;
|
||||||
|
while(currentSkill != NULL)
|
||||||
|
{
|
||||||
|
snprintf(formattedString, 120, "| %-31s ", currentSkill->skill->skillName);
|
||||||
|
charCount += 43;
|
||||||
|
strncat(listMessage->messageContent, formattedString, 120);
|
||||||
|
if((charCount + 46) >= MAX)
|
||||||
|
{
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, listMessage, ¤tCommand->caller, 1);
|
||||||
|
bzero(listMessage, sizeof(userMessage));
|
||||||
|
charCount = 0;
|
||||||
|
addNewline = false;
|
||||||
|
}
|
||||||
|
else if(addNewline)
|
||||||
|
{
|
||||||
|
strncat(listMessage->messageContent, "|\n", 3);
|
||||||
|
charCount++;
|
||||||
|
addNewline = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addNewline = true;
|
||||||
|
}
|
||||||
|
currentSkill = currentSkill->next;
|
||||||
|
}
|
||||||
|
queueTargetedOutputMessage(parameters->outputQueue, listMessage, ¤tCommand->caller, 1);
|
||||||
|
free(listMessage);
|
||||||
|
free(formattedString);
|
||||||
|
}
|
||||||
|
// Remove the current command and unlock the queue:
|
||||||
|
currentCommand = NULL;
|
||||||
|
queue->lock = false;
|
||||||
|
dequeueCommand(queue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run a stat check:
|
||||||
|
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
||||||
|
{
|
||||||
|
if(chance > 100 || chance < 0)
|
||||||
|
{
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
chance = 100 - chance;
|
||||||
|
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 = (random() % 100) + modifier;
|
||||||
|
if(attempt >= chance)
|
||||||
|
{
|
||||||
|
if(attempt >= 98)
|
||||||
|
{
|
||||||
|
return CRITICAL_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(attempt <= 2)
|
||||||
|
{
|
||||||
|
return CRITICAL_FAILURE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
// 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 "lists.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;
|
||||||
|
areaNode * areaList;
|
||||||
|
playerInfo * connectedPlayers;
|
||||||
|
inputMessageQueue * inputQueue;
|
||||||
|
outputMessageQueue * outputQueue;
|
||||||
|
skillList * 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
|
||||||
|
{
|
||||||
|
playerInfo * caller;
|
||||||
|
commandEvent * next;
|
||||||
|
char * command;
|
||||||
|
char * arguments;
|
||||||
|
} commandEvent;
|
||||||
|
|
||||||
|
// A first-in first-out queue for message input from players:
|
||||||
|
typedef struct commandQueue
|
||||||
|
{
|
||||||
|
bool lock;
|
||||||
|
bool paused;
|
||||||
|
int currentLength;
|
||||||
|
commandEvent * back;
|
||||||
|
commandEvent * front;
|
||||||
|
} commandQueue;
|
||||||
|
|
||||||
|
// Create a commandQueue:
|
||||||
|
commandQueue * createCommandQueue(void);
|
||||||
|
|
||||||
|
// Enqueue a command to a commandQueue:
|
||||||
|
int queueCommand(commandQueue * queue, char * command, char * arguments,
|
||||||
|
int commandLength, int argumentsLength , playerInfo * callingPlayer);
|
||||||
|
|
||||||
|
// Enqueue a messaged command to a commandQueue:
|
||||||
|
int queueMessagedCommand(commandQueue * queue, inputMessage * messageToQueue);
|
||||||
|
|
||||||
|
// Dequeue the front commandEvent from a commandQueue:
|
||||||
|
int dequeueCommand(commandQueue * queue);
|
||||||
|
|
||||||
|
// Return the front commandEvent from a commandQueue:
|
||||||
|
commandEvent * peekCommand(commandQueue * queue);
|
||||||
|
|
||||||
|
// Evaluate the next commandEvent:
|
||||||
|
int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue);
|
||||||
|
|
||||||
|
/* // Evaluate the given commandEvent: */
|
||||||
|
/* int evaluateCommand(gameLogicParameters * parameters, commandEvent * command); */
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// -=[ Gameplay Primitives ]=-:
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
typedef enum outcome
|
||||||
|
{
|
||||||
|
CRITICAL_FAILURE,
|
||||||
|
FAILURE,
|
||||||
|
SUCCESS,
|
||||||
|
CRITICAL_SUCCESS,
|
||||||
|
ERROR
|
||||||
|
} outcome;
|
||||||
|
|
||||||
|
// Run a stat check:
|
||||||
|
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck);
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,9 +6,9 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include "../include/constants.h"
|
#include "constants.h"
|
||||||
#include "../include/playerdata.h"
|
#include "playerdata.h"
|
||||||
#include "../include/inputoutput.h"
|
#include "inputoutput.h"
|
||||||
|
|
||||||
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
|
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
|
||||||
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
|
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
|
||||||
|
@ -34,7 +34,7 @@ int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToM
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName,
|
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName,
|
||||||
sizeof(((userMessage*)0)->senderName));
|
sizeof(((userMessage*)0)->senderName));
|
||||||
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
|
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -313,7 +313,7 @@ int queueInputMessage(inputMessageQueue * queue, userMessage messageToQueue, pla
|
||||||
queue->back = inputMessage;
|
queue->back = inputMessage;
|
||||||
queue->currentLength++;
|
queue->currentLength++;
|
||||||
|
|
||||||
// Unlock the queue:
|
// Unlock the queue:
|
||||||
queue->lock = false;
|
queue->lock = false;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -335,6 +335,19 @@ void userInputSanatize(char * inputString, int length)
|
||||||
inputString[length - 1] = '\0';
|
inputString[length - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void userNameSanatize(char * inputString, int length)
|
||||||
|
{
|
||||||
|
for(int index = 0; index <= length; index++)
|
||||||
|
{
|
||||||
|
if(!isprint(inputString[index]))
|
||||||
|
{
|
||||||
|
inputString[index] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputString[length - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
// Return the front inputMessage from an inputMessageQueue:
|
// Return the front inputMessage from an inputMessageQueue:
|
||||||
inputMessage * peekInputMessage(inputMessageQueue * queue)
|
inputMessage * peekInputMessage(inputMessageQueue * queue)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
// A message datastructure containing a user/character name and the content:
|
// A message datastructure containing a user/character name and the content:
|
||||||
typedef struct userMessage
|
typedef struct userMessage
|
||||||
{
|
{
|
||||||
char senderName[32];
|
char senderName[32];
|
||||||
char messageContent[MAX];
|
char messageContent[MAX];
|
||||||
} userMessage;
|
} userMessage;
|
||||||
|
@ -100,5 +100,8 @@ inputMessage * peekInputMessage(inputMessageQueue * queue);
|
||||||
// Sanatize user input to ensure it's okay to send to the server:
|
// Sanatize user input to ensure it's okay to send to the server:
|
||||||
void userInputSanatize(char * inputString, int length);
|
void userInputSanatize(char * inputString, int length);
|
||||||
|
|
||||||
|
// Sanatize user names so they display correctly;
|
||||||
|
void userNameSanatize(char * inputString, int length);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Implementation of lists library for SilverMUD.
|
// Implementation of lists library for SilverMUD.
|
||||||
// Barry Kane, 2021
|
// Barry Kane, 2021
|
||||||
#include "../include/lists.h"
|
#include "lists.h"
|
||||||
#include "../include/playerdata.h"
|
#include "playerdata.h"
|
||||||
|
|
||||||
areaNode * createAreaList(playerArea * initialArea)
|
areaNode * createAreaList(playerArea * initialArea)
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,7 @@ int addAreaNodeToList(areaNode * toList, playerArea * areaToAdd)
|
||||||
current->next = malloc(sizeof(areaNode));
|
current->next = malloc(sizeof(areaNode));
|
||||||
current->next->prev = current;
|
current->next->prev = current;
|
||||||
current->next->data = areaToAdd;
|
current->next->data = areaToAdd;
|
||||||
current->next->next = NULL;
|
current->next->next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int deleteAreaNodeFromList(areaNode * fromList, playerArea * areaToDelete)
|
int deleteAreaNodeFromList(areaNode * fromList, playerArea * areaToDelete)
|
||||||
|
@ -61,7 +61,7 @@ int addPathNodeToList(pathNode * toList, playerPath * pathToAdd)
|
||||||
current = toList;
|
current = toList;
|
||||||
while(current->next != NULL)
|
while(current->next != NULL)
|
||||||
{
|
{
|
||||||
current = current->next;
|
current = current->next;
|
||||||
}
|
}
|
||||||
current->next = malloc(sizeof(pathNode));
|
current->next = malloc(sizeof(pathNode));
|
||||||
current->next->prev = current;
|
current->next->prev = current;
|
||||||
|
|
320
src/playerdata.c
320
src/playerdata.c
|
@ -1,18 +1,21 @@
|
||||||
// playerdata.c: Contains functions definitions for working with player data.
|
// playerdata.c: Contains functions definitions for working with player data.
|
||||||
// Barry Kane, 2021
|
// Barry Kane, 2021
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../include/playerdata.h"
|
#include <stdbool.h>
|
||||||
|
#include "constants.h"
|
||||||
|
#include "playerdata.h"
|
||||||
|
|
||||||
// Move a player to a different area given a path in the area:
|
// Move a player to a different area given a path in the area:
|
||||||
int movePlayerToArea(playerInfo * player, char * requestedPath)
|
int movePlayerToArea(playerInfo * player, char * requestedPath)
|
||||||
{
|
{
|
||||||
// Check if a number was given first:
|
// Check if a number was given first:
|
||||||
int selected = atoi(requestedPath);
|
int selected = atoi(requestedPath);
|
||||||
if(selected != 0)
|
if(selected != 0)
|
||||||
{
|
{
|
||||||
if(player->currentArea->areaExits[selected -1]->areaToJoin != NULL)
|
if(player->currentArea->areaExits[selected -1] != NULL && player->currentArea->areaExits[selected -1]->areaToJoin != NULL)
|
||||||
{
|
{
|
||||||
player->currentArea = player->currentArea->areaExits[selected - 1]->areaToJoin;
|
player->currentArea = player->currentArea->areaExits[selected - 1]->areaToJoin;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -42,13 +45,24 @@ int movePlayerToArea(playerInfo * player, char * requestedPath)
|
||||||
// Create an area given a name and description:
|
// Create an area given a name and description:
|
||||||
playerArea * createArea(char * nameString, char * descriptionString)
|
playerArea * createArea(char * nameString, char * descriptionString)
|
||||||
{
|
{
|
||||||
|
// Allocate and zero memory for the new area:
|
||||||
playerArea * createdArea = calloc(1, sizeof(playerArea));
|
playerArea * createdArea = calloc(1, sizeof(playerArea));
|
||||||
strncpy(createdArea->areaName, nameString, 32);
|
|
||||||
strncpy(createdArea->areaDescription, descriptionString, 256);
|
// Copy the strings into the newly created area:
|
||||||
|
strncpy(createdArea->areaName, nameString, 32 - 1);
|
||||||
|
strncpy(createdArea->areaDescription, descriptionString, MAX - 35);
|
||||||
|
|
||||||
|
// Properly null-terminate the strings:
|
||||||
|
createdArea->areaName[31] = '\0';
|
||||||
|
createdArea->areaDescription[MAX] = '\0';
|
||||||
|
|
||||||
|
// Ensure that all the paths are set to NULL:
|
||||||
for(int index = 0; index < 16; index++)
|
for(int index = 0; index < 16; index++)
|
||||||
{
|
{
|
||||||
createdArea->areaExits[index] = NULL;
|
createdArea->areaExits[index] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the pointer:
|
||||||
return createdArea;
|
return createdArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,13 +92,301 @@ int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescriptio
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playerPath * fromPath = calloc(1, sizeof(playerPath));
|
playerPath * fromPath = malloc(sizeof(playerPath));
|
||||||
playerPath * toPath = calloc(1, sizeof(playerPath));
|
playerPath * toPath = malloc(sizeof(playerPath));
|
||||||
fromArea->areaExits[fromAreaSlot] = fromPath;
|
fromArea->areaExits[fromAreaSlot] = fromPath;
|
||||||
toArea->areaExits[toAreaSlot] = toPath;
|
toArea->areaExits[toAreaSlot] = toPath;
|
||||||
strncpy(fromArea->areaExits[fromAreaSlot]->pathName, fromDescription, 32);
|
strncpy(fromPath->pathName, fromDescription, 32 - 1);
|
||||||
strncpy(toArea->areaExits[toAreaSlot]->pathName, toDescription, 32);
|
fromPath->pathName[31] = '\0';
|
||||||
|
strncpy(toPath->pathName, toDescription, 32 - 1);
|
||||||
|
toPath->pathName[31] = '\0';
|
||||||
fromArea->areaExits[fromAreaSlot]->areaToJoin = toArea;
|
fromArea->areaExits[fromAreaSlot]->areaToJoin = toArea;
|
||||||
toArea->areaExits[toAreaSlot]->areaToJoin = fromArea;
|
toArea->areaExits[toAreaSlot]->areaToJoin = fromArea;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new skill and add it to the global skill list:
|
||||||
|
int createSkill(skillList * 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 -1;
|
||||||
|
}
|
||||||
|
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(addSkillNode(globalSkillList, newSkill));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a skill node to a skill list:
|
||||||
|
int addSkillNode(skillList * skillList, playerSkill * skill)
|
||||||
|
{
|
||||||
|
if(skillList->head == NULL)
|
||||||
|
{
|
||||||
|
skillList->head = malloc(sizeof(skillNode));
|
||||||
|
skillList->head->skill = skill;
|
||||||
|
skillList->head->next = NULL;
|
||||||
|
skillList->skillCount = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skillNode * currentNode = skillList->head;
|
||||||
|
while(currentNode->next != NULL)
|
||||||
|
{
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
currentNode->next = malloc(sizeof(skillNode));
|
||||||
|
currentNode->next->skill = skill;
|
||||||
|
currentNode->next->next = NULL;
|
||||||
|
skillList->skillCount++;
|
||||||
|
return skillList->skillCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a skill node from a skill list:
|
||||||
|
int removeSkillNode(skillList * skillList, playerSkill * skill)
|
||||||
|
{
|
||||||
|
if(skillList->head->skill == skill)
|
||||||
|
{
|
||||||
|
free(skillList->head->skill);
|
||||||
|
if(skillList->head->next != NULL)
|
||||||
|
{
|
||||||
|
skillNode * deletedNode = skillList->head;
|
||||||
|
skillList->head = skillList->head->next;
|
||||||
|
free(deletedNode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skillNode * currentNode = skillList->head;
|
||||||
|
skillNode * previousNode = skillList->head;
|
||||||
|
while(currentNode->skill != skill)
|
||||||
|
{
|
||||||
|
if(currentNode->next == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
previousNode = currentNode;
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
free(currentNode->skill);
|
||||||
|
previousNode->next = currentNode->next;
|
||||||
|
free(currentNode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take a skill and add it to the player's skill list:
|
||||||
|
int takeSkill(skillList * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer)
|
||||||
|
{
|
||||||
|
|
||||||
|
skillNode * currentNode = globalSkillList->head;
|
||||||
|
while(strncmp(skillName, currentNode->skill->skillName, 32) != 0)
|
||||||
|
{
|
||||||
|
if(currentNode->next == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Skill doesn't exist in skill list.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
currentNode = currentNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool playerHasSkill = false;
|
||||||
|
skillNode * currentPlayerNode = targetPlayer->skills->head;
|
||||||
|
while(currentPlayerNode != NULL)
|
||||||
|
{
|
||||||
|
if(strncmp(skillName, currentPlayerNode->skill->skillName, skillNameLength) == 0)
|
||||||
|
{
|
||||||
|
playerHasSkill = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentPlayerNode = currentPlayerNode->next;
|
||||||
|
}
|
||||||
|
if(playerHasSkill)
|
||||||
|
{
|
||||||
|
currentPlayerNode->skill->skillPoints++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addSkillNode(targetPlayer->skills, currentNode->skill);
|
||||||
|
currentPlayerNode = targetPlayer->skills->head;
|
||||||
|
while(currentPlayerNode->next != NULL)
|
||||||
|
{
|
||||||
|
currentPlayerNode = currentPlayerNode->next;
|
||||||
|
}
|
||||||
|
currentPlayerNode->skill->skillPoints = 1;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int deallocatePlayer(playerInfo * playerToDeallocate)
|
||||||
|
{
|
||||||
|
// Deallocate the skill list:
|
||||||
|
if(playerToDeallocate->skills->skillCount > 0)
|
||||||
|
{
|
||||||
|
// Allocate enough pointers:
|
||||||
|
skillNode * nodesToDeallocate[playerToDeallocate->skills->skillCount];
|
||||||
|
skillNode * currentSkillNode = playerToDeallocate->skills->head;
|
||||||
|
|
||||||
|
// Get a list of all the nodes together:
|
||||||
|
for(int index = 0; index < playerToDeallocate->skills->skillCount; index++)
|
||||||
|
{
|
||||||
|
nodesToDeallocate[index] = currentSkillNode;
|
||||||
|
currentSkillNode = currentSkillNode->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate all the nodes:
|
||||||
|
for(int index = 0; index < playerToDeallocate->skills->skillCount; index++)
|
||||||
|
{
|
||||||
|
free(nodesToDeallocate[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate the stat block:
|
||||||
|
free(playerToDeallocate->stats);
|
||||||
|
|
||||||
|
// Deallocate the player:
|
||||||
|
free(playerToDeallocate);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
// 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 <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
typedef struct playerPath playerPath;
|
||||||
|
typedef struct playerArea playerArea;
|
||||||
|
|
||||||
|
struct playerPath
|
||||||
|
{
|
||||||
|
char pathName[32];
|
||||||
|
playerArea * areaToJoin;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct playerArea
|
||||||
|
{
|
||||||
|
char areaName[32];
|
||||||
|
char areaDescription[MAX - 35];
|
||||||
|
playerPath * areaExits[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
typedef struct playerSkill
|
||||||
|
{
|
||||||
|
char skillName[32];
|
||||||
|
int skillPoints;
|
||||||
|
int skillModifier;
|
||||||
|
bool trainedSkill;
|
||||||
|
} playerSkill;
|
||||||
|
|
||||||
|
typedef struct skillNode skillNode;
|
||||||
|
struct skillNode
|
||||||
|
{
|
||||||
|
playerSkill * skill;
|
||||||
|
skillNode * next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct skillList
|
||||||
|
{
|
||||||
|
skillNode * head;
|
||||||
|
int skillCount;
|
||||||
|
} skillList;
|
||||||
|
|
||||||
|
typedef struct playerInfo
|
||||||
|
{
|
||||||
|
char playerName[32];
|
||||||
|
playerArea * currentArea;
|
||||||
|
statBlock * stats;
|
||||||
|
skillList * skills;
|
||||||
|
} playerInfo;
|
||||||
|
|
||||||
|
typedef enum coreStat
|
||||||
|
{
|
||||||
|
WITS,
|
||||||
|
INTELLECT,
|
||||||
|
STRENGTH,
|
||||||
|
ENDURANCE,
|
||||||
|
DEXERITY,
|
||||||
|
INVALID
|
||||||
|
} coreStat;
|
||||||
|
|
||||||
|
// Move a player to a different area given a path in the area:
|
||||||
|
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 two strings:
|
||||||
|
int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription);
|
||||||
|
|
||||||
|
// Create a new skill and add it to the global skill list:
|
||||||
|
int createSkill(skillList * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill);
|
||||||
|
|
||||||
|
// Add a skill node to a skill list:
|
||||||
|
int addSkillNode(skillList * skillList, playerSkill * skill);
|
||||||
|
|
||||||
|
// Remove a skill node from a skill list:
|
||||||
|
int removeSkillNode(skillList * skillList, playerSkill * skill);
|
||||||
|
int removeSkillByID(skillList * skillList, playerSkill * skill);
|
||||||
|
|
||||||
|
// Take a skill and add it to the player's skill list:
|
||||||
|
int takeSkill(skillList * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer);
|
||||||
|
int takeSkillbyID(skillList * 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:
|
||||||
|
int deallocatePlayer(playerInfo * playerToDeallocate);
|
||||||
|
#endif
|
|
@ -1,6 +1,7 @@
|
||||||
// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.3.
|
// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.3.
|
||||||
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
||||||
// Barry Kane, 2021
|
// Barry Kane, 2021
|
||||||
|
#include <time.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -14,17 +15,19 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include "../../include/lists.h"
|
|
||||||
#include "../../include/gamelogic.h"
|
#include "../lists.h"
|
||||||
#include "../../include/constants.h"
|
#include "../gamelogic.h"
|
||||||
#include "../../include/playerdata.h"
|
#include "../constants.h"
|
||||||
#include "../../include/texteffects.h"
|
#include "../playerdata.h"
|
||||||
#include "../../include/inputoutput.h"
|
#include "../texteffects.h"
|
||||||
|
#include "../inputoutput.h"
|
||||||
|
|
||||||
typedef struct sockaddr sockaddr;
|
typedef struct sockaddr sockaddr;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
time_t currentTime;
|
||||||
bool keepRunning = true;
|
bool keepRunning = true;
|
||||||
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
||||||
socketCheck, activityCheck, returnVal;
|
socketCheck, activityCheck, returnVal;
|
||||||
|
@ -38,25 +41,61 @@ int main()
|
||||||
inputMessageQueue * inputQueue = createInputMessageQueue();
|
inputMessageQueue * inputQueue = createInputMessageQueue();
|
||||||
outputMessageQueue * outputQueue = createOutputMessageQueue();
|
outputMessageQueue * outputQueue = createOutputMessageQueue();
|
||||||
|
|
||||||
|
// -==[ TEST GAME-STATE INITIALIZATION ]==-
|
||||||
// Initialize test areas:
|
// 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."));
|
areaNode * areas = createAreaList(createArea("Login Area", "Please login with the /join command."));
|
||||||
addAreaNodeToList(areas, createArea("Spawn - South", "A strange, white void. You feel rather uncomfortable."));
|
addAreaNodeToList(areas, createArea("Temple Entrance",
|
||||||
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."));
|
"You are standing outside a large, elaborate temple, of white marble and delicate construction. "
|
||||||
createPath(getAreaFromList(areas, 0), getAreaFromList(areas, 1), "To South Spawn", "To North Spawn");
|
"Etched onto the left pillar next to the large opening is the same symbol, over and over again, a gentle curve with it's ends pointing to the right. "
|
||||||
createPath(getAreaFromList(areas, 2), getAreaFromList(areas, 1), "Back to South Spawn", "Path to Enlightenment.");
|
"A similar symbol is on the right pillar, but it's ends are pointing to the left. "));
|
||||||
|
|
||||||
|
addAreaNodeToList(areas, createArea("The Hall of Documentation",
|
||||||
|
"Just past the threshold of the entrance lies a large hall, with bookshelves lining the walls, ceiling to floor. "
|
||||||
|
"The shelves are filled to the brim with finely-bound books, each with titles in silver lettering on the spine. "
|
||||||
|
"There are countless books, but you notice a large lectern in the center of the room, and a path leading upwards at the back. "));
|
||||||
|
|
||||||
|
addAreaNodeToList(areas, createArea("Monument to GNU",
|
||||||
|
"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."));
|
||||||
|
// Initialize test paths:
|
||||||
|
createPath(getAreaFromList(areas, 1), getAreaFromList(areas, 2), "Go inside the temple.", "Leave the temple.");
|
||||||
|
createPath(getAreaFromList(areas, 3), getAreaFromList(areas, 2), "Back to the Hall of Documentation.", "Path to Enlightenment.");
|
||||||
|
skillList * globalSkillList = malloc(sizeof(skillList));
|
||||||
|
globalSkillList->head = NULL;
|
||||||
|
|
||||||
|
// 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:
|
// Initialize playerdata:
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||||
{
|
{
|
||||||
sprintf(testString, "UNNAMED %d", 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);
|
strcpy(connectedPlayers[index].playerName, testString);
|
||||||
connectedPlayers[index].currentArea = getAreaFromList(areas, 0);
|
connectedPlayers[index].currentArea = getAreaFromList(areas, 0);
|
||||||
|
connectedPlayers[index].stats = calloc(1, sizeof(statBlock));
|
||||||
|
connectedPlayers[index].stats->specPoints = 30;
|
||||||
|
connectedPlayers[index].stats->skillPoints = 30;
|
||||||
|
connectedPlayers[index].skills = calloc(1, sizeof(skillList));
|
||||||
|
connectedPlayers[index].skills->head = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -==[ TEST GAME-STATE INITIALIZATION END ]==-
|
||||||
|
|
||||||
// Give an intro: Display the Silverkin Industries logo and splash text.
|
// Give an intro: Display the Silverkin Industries logo and splash text.
|
||||||
slowPrint(logostring, 3000);
|
slowPrint(logostring, 3000);
|
||||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.3\n", 5000);
|
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.3\n", 5000);
|
||||||
|
|
||||||
|
// Seed random number generator from the current time:
|
||||||
|
srandom((unsigned) time(¤tTime));
|
||||||
|
|
||||||
// Initialize the sockets to 0, so we don't crash.
|
// Initialize the sockets to 0, so we don't crash.
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||||
{
|
{
|
||||||
|
@ -67,7 +106,7 @@ int main()
|
||||||
socketFileDesc = socket(AF_INET, SOCK_STREAM, 0);
|
socketFileDesc = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (socketFileDesc == -1)
|
if (socketFileDesc == -1)
|
||||||
{
|
{
|
||||||
perror("\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +114,7 @@ int main()
|
||||||
{
|
{
|
||||||
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
bzero(&serverAddress, sizeof(serverAddress));
|
bzero(&serverAddress, sizeof(serverAddress));
|
||||||
|
|
||||||
// Assign IP and port:
|
// Assign IP and port:
|
||||||
|
@ -86,7 +125,7 @@ int main()
|
||||||
// Binding newly created socket to given IP, and checking it works:
|
// Binding newly created socket to given IP, and checking it works:
|
||||||
if ((bind(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress))) != 0)
|
if ((bind(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress))) != 0)
|
||||||
{
|
{
|
||||||
perror("\tSocket Binding is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
fprintf(stderr, "\tSocket Binding is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,48 +137,53 @@ int main()
|
||||||
// Let's start listening:
|
// Let's start listening:
|
||||||
if ((listen(socketFileDesc, PLAYERCOUNT)) != 0)
|
if ((listen(socketFileDesc, PLAYERCOUNT)) != 0)
|
||||||
{
|
{
|
||||||
perror("\tServer Listening is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
fprintf(stderr, "\tServer Listener is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
slowPrint("\tServer Listening is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||||
}
|
}
|
||||||
length = sizeof(clientAddress);
|
length = sizeof(clientAddress);
|
||||||
|
|
||||||
|
// Declare the needed variables for TLS sessions:
|
||||||
gnutls_session_t tlssessions[PLAYERCOUNT];
|
gnutls_session_t tlssessions[PLAYERCOUNT];
|
||||||
gnutls_anon_server_credentials_t serverkey = NULL;
|
gnutls_anon_server_credentials_t serverkey = NULL;
|
||||||
gnutls_anon_allocate_server_credentials(&serverkey);
|
gnutls_anon_allocate_server_credentials(&serverkey);
|
||||||
gnutls_anon_set_server_known_dh_params(serverkey, GNUTLS_SEC_PARAM_MEDIUM);
|
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."
|
// 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++)
|
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||||
{
|
{
|
||||||
tlssessions[index] = NULL;
|
tlssessions[index] = NULL;
|
||||||
if (gnutls_init(&tlssessions[index], GNUTLS_SERVER) < 0)
|
if (gnutls_init(&tlssessions[index], GNUTLS_SERVER) < 0)
|
||||||
{
|
{
|
||||||
perror("\tTLS Sessions Initialization is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
fprintf(stderr, "\tTLS Sessions Initialization is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
||||||
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
||||||
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||||
}
|
}
|
||||||
slowPrint("\tTLS Sessions Initialization is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||||
|
|
||||||
// Prepare the game logic thread:
|
// Prepare the game logic thread:
|
||||||
gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters));
|
gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters));
|
||||||
gameLogicThreadParameters->connectedPlayers = connectedPlayers;
|
gameLogicThreadParameters->connectedPlayers = connectedPlayers;
|
||||||
gameLogicThreadParameters->playerCount = &clientsAmount;
|
gameLogicThreadParameters->playerCount = &clientsAmount;
|
||||||
|
gameLogicThreadParameters->globalSkillList = globalSkillList;
|
||||||
gameLogicThreadParameters->outputQueue = outputQueue;
|
gameLogicThreadParameters->outputQueue = outputQueue;
|
||||||
gameLogicThreadParameters->inputQueue = inputQueue;
|
gameLogicThreadParameters->inputQueue = inputQueue;
|
||||||
|
gameLogicThreadParameters->areaList = areas;
|
||||||
pthread_create(&gameLogicThread, NULL, &gameLogicLoop, gameLogicThreadParameters);
|
pthread_create(&gameLogicThread, NULL, &gameLogicLoop, gameLogicThreadParameters);
|
||||||
|
|
||||||
|
slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||||
|
slowPrint("=====\n", 5000);
|
||||||
struct timeval timeout = {0, 500};
|
struct timeval timeout = {0, 500};
|
||||||
|
|
||||||
while(keepRunning)
|
while(keepRunning)
|
||||||
{
|
{
|
||||||
// Clear the set of file descriptors and add the master socket:
|
// Clear the set of file descriptors angad add the master socket:
|
||||||
FD_ZERO(&connectedClients);
|
FD_ZERO(&connectedClients);
|
||||||
FD_SET(socketFileDesc, &connectedClients);
|
FD_SET(socketFileDesc, &connectedClients);
|
||||||
clientsAmount = socketFileDesc;
|
clientsAmount = socketFileDesc;
|
||||||
|
@ -168,7 +212,7 @@ int main()
|
||||||
// Check if select() worked:
|
// Check if select() worked:
|
||||||
if ((activityCheck < 0) && (errno != EINTR))
|
if ((activityCheck < 0) && (errno != EINTR))
|
||||||
{
|
{
|
||||||
perror("Error in select(), retrying.\n");
|
fprintf(stderr, "Error in select(), retrying.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's the master socket selected, there is a new connection:
|
// If it's the master socket selected, there is a new connection:
|
||||||
|
@ -176,7 +220,7 @@ int main()
|
||||||
{
|
{
|
||||||
if ((connectionFileDesc = accept(socketFileDesc, (struct sockaddr *)&clientAddress, (socklen_t*)&length)) < 0)
|
if ((connectionFileDesc = accept(socketFileDesc, (struct sockaddr *)&clientAddress, (socklen_t*)&length)) < 0)
|
||||||
{
|
{
|
||||||
perror("Failed to accept connection. Aborting.\n");
|
fprintf(stderr, "Failed to accept connection. Aborting.\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
// See if we can put in the client:
|
// See if we can put in the client:
|
||||||
|
@ -193,9 +237,13 @@ int main()
|
||||||
returnVal = gnutls_handshake(tlssessions[index]);
|
returnVal = gnutls_handshake(tlssessions[index]);
|
||||||
}
|
}
|
||||||
while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0);
|
while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0);
|
||||||
|
|
||||||
|
// Send a greeting message:
|
||||||
strcpy(sendBuffer.senderName, "");
|
strcpy(sendBuffer.senderName, "");
|
||||||
strcpy(sendBuffer.messageContent, "Welcome to the server!");
|
strcpy(sendBuffer.messageContent, "Welcome to the server!");
|
||||||
messageSend(tlssessions[index], &sendBuffer);
|
messageSend(tlssessions[index], &sendBuffer);
|
||||||
|
strcpy(receiveBuffer.messageContent, "/look");
|
||||||
|
queueInputMessage(inputQueue, receiveBuffer, &connectedPlayers[index]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,45 +258,46 @@ int main()
|
||||||
if(FD_ISSET(socketCheck, &connectedClients))
|
if(FD_ISSET(socketCheck, &connectedClients))
|
||||||
{
|
{
|
||||||
int returnVal = messageReceive(tlssessions[index], &receiveBuffer);
|
int returnVal = messageReceive(tlssessions[index], &receiveBuffer);
|
||||||
|
// If player has disconnected:
|
||||||
if(returnVal == -10 || returnVal == 0)
|
if(returnVal == -10 || returnVal == 0)
|
||||||
{
|
{
|
||||||
|
// Close the session:
|
||||||
gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR);
|
gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR);
|
||||||
gnutls_deinit(tlssessions[index]);
|
gnutls_deinit(tlssessions[index]);
|
||||||
shutdown(clientSockets[index], 2);
|
shutdown(clientSockets[index], 2);
|
||||||
close(clientSockets[index]);
|
close(clientSockets[index]);
|
||||||
clientSockets[index] = 0;
|
clientSockets[index] = 0;
|
||||||
tlssessions[index] = NULL;
|
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 = getAreaFromList(areas, 0);
|
||||||
|
|
||||||
|
// Prepare a fresh SSL session for the next new player:
|
||||||
gnutls_init(&tlssessions[index], GNUTLS_SERVER);
|
gnutls_init(&tlssessions[index], GNUTLS_SERVER);
|
||||||
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
||||||
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
||||||
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||||
}
|
}
|
||||||
else
|
// Otherwise, they've sent a message:
|
||||||
|
else
|
||||||
{
|
{
|
||||||
queueInputMessage(inputQueue, receiveBuffer, &connectedPlayers[index]);
|
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); */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
|
// Run through the output queue and send all unused messages:
|
||||||
while(outputQueue->currentLength != 0)
|
while(outputQueue->currentLength != 0)
|
||||||
{
|
{
|
||||||
while(outputQueue->lock);
|
while(outputQueue->lock);
|
||||||
outputQueue->lock = true;
|
outputQueue->lock = true;
|
||||||
outputMessage * message = peekOutputMessage(outputQueue);
|
outputMessage * message = peekOutputMessage(outputQueue);
|
||||||
outputQueue->lock = false;
|
outputQueue->lock = false;
|
||||||
|
|
||||||
|
// If the first target is set to NULL, it's intended for all connected:
|
||||||
if(message->targets[0] == NULL)
|
if(message->targets[0] == NULL)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// texteffects.c: Implementation of text effect library for SilverMUD.
|
// texteffects.c: Implementation of text effect library for SilverMUD.
|
||||||
// Barry Kane, 2021.
|
// Barry Kane, 2021.
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
@ -38,3 +39,60 @@ void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bol
|
||||||
}
|
}
|
||||||
wrefresh(window);
|
wrefresh(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bruteForcePrintNcurses(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,4 +14,5 @@ void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bol
|
||||||
// A string containing an ASCII art version of the Silverkin Industries logo.
|
// A string containing an ASCII art version of the Silverkin Industries logo.
|
||||||
char * logostring = " ///////\n //////////////////////////////////////////\n ///////////////////////////////////////////////////////////\n ////////// ////////////////////////////\n ### # # # # ##### ### # # # # # /////////////////\n ### # # # # ## # # ## # ## # //////////////\n ## # # # # # ### # # # # # # /////////\n #### # ### # ##### # # # # # # ## ///////\n # ## # ##### # # ### ### ### # ##### ### ////// \n # # # # # # # # ## # # # # ## ## ////\n # # # # # # # # ## # ### # # ## //\n # # ### ##### ##### ### # # # # #### ### //\n";
|
char * logostring = " ///////\n //////////////////////////////////////////\n ///////////////////////////////////////////////////////////\n ////////// ////////////////////////////\n ### # # # # ##### ### # # # # # /////////////////\n ### # # # # ## # # ## # ## # //////////////\n ## # # # # # ### # # # # # # /////////\n #### # ### # ##### # # # # # # ## ///////\n # ## # ##### # # ### ### ### # ##### ### ////// \n # # # # # # # # ## # # # # ## ## ////\n # # # # # # # # ## # ### # # ## //\n # # ### ##### ##### ### # # # # #### ### //\n";
|
||||||
|
|
||||||
|
void wrapString(char * stringToWrap, int stringLength, int screenWidth);
|
||||||
#endif
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Hopefully optimized corestat to string:
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "../src/playerdata.h"
|
||||||
|
|
||||||
|
void main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
getCoreStatFromString(argv[1], strlen(argv[1]));
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
#include "../misc/lists.h"
|
#include "../src/lists.h"
|
||||||
#include "../misc/playerdata.h"
|
#include "../src/playerdata.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../misc/inputoutput.h"
|
#include "../src/inputoutput.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
int main()
|
int main()
|
||||||
|
|
Loading…
Reference in New Issue