Alpha 0.5 release of SilverMUD:
Features Changed: - Refactored old lists and queues to a single type. - Commented and cleaned up the codebase. - Various improvements. See commits for more details. Squashed commit of the following: commitff281e5ce6
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 15 22:16:12 2023 +0000 Increment version number commitf5cb3ad16e
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 15 22:09:21 2023 +0000 More cleaning up. - Brought remaining files in line with style guides, and improved comments. commitf31f0c79a5
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 15 21:43:13 2023 +0000 Cleaned up gamelogic.c/h - Improved comments to clarify the purpose and usage of the data structures and functions. - Brought the files in line with the SilverMUD style guide. commitc2af4a551a
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 15 21:30:40 2023 +0000 Cleaned up inputoutput.h - Changed the comments about the data structures to be more descriptive about their function. commitc753182827
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 15 21:22:14 2023 +0000 Cleaned up inputoutput.c. - Added additional comments in sections functions that were unclear. - Renamed targetIndex to sentCount, in order to clarify the usage of the variable. - Linted according to the current SilverMUD style guide. commitf411333203
Author: Barry Kane <barry@omnimenu.ie> Date: Mon Feb 13 17:23:30 2023 +0000 Refactored SilverMUD to use the queue type for command events. - Removed the old commandQueue type. - Removed the functions relating to creating, pushing, popping, and peeking the commandQueues. - Refactored the gameLogicHandler to use a queue type. commit602f177a8f
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Feb 12 23:32:39 2023 +0000 Added some more comments. - Commented the data structures in areadata.h and gamelogic.h. commitd0e4a8f9fc
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Feb 12 23:13:10 2023 +0000 Removed the body of the try command. - Removed the try command's test functionality to allow for the later implementation of the actual functionality. commit66e0279e78
Author: Barry Kane <barry@omnimenu.ie> Date: Sat Feb 11 00:07:30 2023 +0000 Commented the data structures in queue.h. - Commented all the data structures in queue, to make it slightly clearer as to their usage and what they store. commitfeb1743425
Author: Barry Kane <barry@omnimenu.ie> Date: Fri Feb 10 23:33:36 2023 +0000 Added naming rule 2 commit52fd7ef6fb
Author: Barry Kane <barry@omnimenu.ie> Date: Thu Feb 9 21:53:23 2023 +0000 Wrote more documentation and a rule for the style guide. Documented basic usage of the client's launch options to join a server and added the no one letter variable names rule. commita38cbb70a8
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Feb 8 17:15:23 2023 +0000 Fixed Thomas's Bug. In short: - When a large amount of input was recieved, the server would hang. - The server would hang on queue->lock for the input queue, in pushQueue(). - Upon debugging, it was revealed that queue->lock was actually false at this time. - GCC had optimized out the actual loop, leaving it to get stuck even though the variable had changed. - Adding the volatile keyword to the lock fixed this issue. commitc2c77d6343
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Dec 21 20:31:32 2022 +0000 Cleaned and styled SilverMUDClient.c - Brought SilverMUDClient.c to a consistent style. - Cleaned and neatened various parts of SilverMUDClient.c. - Minor cleanup of playerdata.h. - Began writing the SilverMUD Style Guide. - Removed outputQueue-test.c, because outputQueue no longer exists. commit6a653c75b9
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Dec 21 00:49:26 2022 +0000 Implemented proper thread sleeping and additional output thread - Replaced previous inefficient "spin-waiting" with proper thread sleeping. - Added threading primitives to the queue type to enable this. - Added additional thread for output management. - Miscellanous cleanup and restructuring. commit15d82f59ee
Author: Barry Kane <barry@omnimenu.ie> Date: Tue Dec 20 15:55:24 2022 +0000 Refactored the outputQueue to use the new queue type - Patched a bug where I didn't set the correct type for queue nodes. - Removed all traces of the previous outputMessageQueue type. - Removed the pointer "next" in outputMessage. - Rewrote the the main thread and game logic thread to use a queue for output. - Refactored outputMessage to allow for a variable amount of playerInfo pointers. commit9b3df5928b
Author: Barry Kane <barry@omnimenu.ie> Date: Mon Dec 19 23:44:16 2022 +0000 Refactored the input queue to use the new queue type - Removed all traces of the previous inputMessageQueue type. - Removed the pointer "next" in inputMessage. - Rewrote the the main thread and game logic thread to use a queue for input. commit8ae3eaf2b8
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Dec 18 21:07:10 2022 +0000 Added queue.c and queue.h. - Added a singular queue type for input/output messages and commands. - Added a test of the queue type in tests. - Made some edits to the Makefile to enable the addition of debug code using the preprocessor. - Minor styling and spelling updates in gamelogic.c/h and text effects .h. - Ready to integrate the new queue type in place of the previous ones. commit2ab873b40b
Author: Barry Kane <barry@omnimenu.ie> Date: Wed Nov 30 15:31:14 2022 +0000 Make the client respect the set character delay. - The client now respects the character delay set on the command line. commit4cc0d3a0f6
Author: Barry Kane <barry@omnimenu.ie> Date: Tue Nov 29 21:04:36 2022 +0000 Completed the conversion to the new linked-list type. - Moved all code relating to skills to use the new linked-lists. - Removed all old code relating to other lists. - Improved linked lists to get nodes more efficiently in the second half of the list. commit51f1a953e7
Author: Barry Kane <barry@omnimenu.ie> Date: Fri Nov 18 14:44:25 2022 +0000 Refactored areas to use linked-lists. - Refactored the server to rely on the linked-list version of area lists. - Removed all old code pertaining to Area/Path lists. - Removed a no-longer useful test for corestat-from string performance.y commit6b3d9febf6
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Nov 13 18:26:36 2022 +0000 Linked lists now destroy the pathList of an area. - Modified destroyList to destroy pathLists in areas. commitd843f0b170
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Nov 13 18:21:06 2022 +0000 Added one-way path function. - Added a function to create one way paths. - Changed the test areas to a new setting and to include one-way paths. commit582a0d02ae
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Nov 13 00:23:42 2022 +0000 Refactored paths to use linked lists. - Removed the code for the now-obsolete-before-actually-being-used pathLists. - playerAreas now contain a list called pathList. - Refactored createArea, createPath, and the /move and /look commands. - Added typedefs to prevent the compiler being unable to link. commitf3ad758e4f
Author: Barry Kane <barry@omnimenu.ie> Date: Fri Nov 11 22:58:05 2022 +0000 Preliminary implementation of linked-list.c - Implemented first pass of linked-list.c. TODO: - Polish linked-list.c 'till you can see a reflection in it. - Refactor existing codebase to use linked-list.c.y commitc68e66e7bc
Author: Barry Kane <barry@omnimenu.ie> Date: Mon Oct 31 01:55:44 2022 +0000 Squashed current warnings. - Fixed all current warnings. - Added the ability to set the text delay as a command line option in the server. commitca8ba5e410
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Oct 30 13:00:18 2022 +0000 Incremented version numbering in preperation for merge to master. - Incremented version numbering to Alpha 0.4. commitd9497679cb
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Oct 30 12:58:39 2022 +0000 Implemented Skill Checks. - Implemented skillCheck. - Allowed for the in-game testing of skillCheck via /try. - Slightly reorganized the Makefile. - Tweaked the logoString to display correctly. - Edited the client and server to generate gprof data when in debug builds. commitf2dd83857f
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Oct 23 17:07:13 2022 +0100 Completed Reorganization of Area Data - Moved the appropriate data structures and functions into areaData. - Made movePlayerToArea a gameplay primitive. commit52b4b1e2f0
Author: Barry Kane <barry@omnimenu.ie> Date: Tue Oct 18 21:00:57 2022 +0100 Warning fixes and Makefile edits - Adjusted the Makefile to create gprof data in the server (currently inactive as the server never terminates.) - Fixed warnings in areadata.c and gamelogic.c. - Added bruteforcePrint for completeness. commit60110d3abd
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Oct 16 21:28:32 2022 +0100 Made client exit gracefully upon server exit: - The client now checks the return value of messageReceive. - Renamed lists.c/.h to areadata.c/.h. commitb8189ae2de
Author: Barry Kane <barry@omnimenu.ie> Date: Sun Oct 16 16:13:33 2022 +0100 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. commit8673bb1ad5
Author: Barry Kane <barry@omnimenu.ie> Date: Fri May 20 22:28:07 2022 +0100 Reorganized file structure. - Reimplemented /LOOK. - Commands are now accepted in both upper and lower case. - Move now accepts a number for easier movement. commit151f3002b8
Author: Barry Kane <barry@omnimenu.ie> Date: Thu May 5 19:45:27 2022 +0100 Began implementing game logic and re-implementing commands - Reimplemented /MOVE and /EXIT - The server is now multi-threaded - Input and output is now queued commit0b3a72beff
Author: Barry <bazzakane@gmail.com> Date: Thu Apr 7 01:39:59 2022 +0100 Removed inputhandling library The functionality was moved to inputoutput. commit4ddb80b8b2
Author: Barry <bazzakane@gmail.com> Date: Thu Apr 7 01:38:36 2022 +0100 Basic message queuing implemented - Messages are now queued on reception by the server. - Message queue datastructures are now added. commite4b8693037
Author: Barry <barry@omnimenu.ie> Date: Tue Mar 15 14:52:49 2022 +0000 Cleaned up client. - Cleaned up the client codebase. - Throughly commented SilverMUDClient.c. - Added a boolean for bolding slowPrintNcurses. - Added a user-configurable delay for text printing. - Other small improvements. commit5d772df469
Author: Barry <barry@omnimenu.ie> Date: Sun Mar 6 00:36:42 2022 +0000 Added initial GnuTLS encryption. - Added inputoutput.c - Added inputoutput.h - inputoutput contains wrapper and helper functions for transmitting messages over GnuTLS. - Moved the userMessage struct definition to inputoutput. - Reworked client and server to use GnuTLS. - Removed all commands from server in preperation for upcoming command and message queues. - Names and areas are no longer considered for messaging. - Changed Makefile to link GnuTLS. commit235ff8e74f
Author: Barry <barry@omnimenu.ie> Date: Sun Dec 26 19:07:30 2021 +0000 Added basic logging support and command-line options to the client. - Added basic logging support to the client. - Added basic command-line options to the client: -g: Enables a game-log, must have a file-path. -c: Enables a chat-log, must have a file-path. -i: Sets the IP address to connect to. - Removed the C-c handler, appeared to be broken anyways. Consider reimplementation at some point. - Added /EXIT command to allow for leaving the game. - The client now exits gracefully if the server dies. commit6c93805d6f
Author: Barry <barry@omnimenu.ie> Date: Sun Dec 5 23:33:53 2021 +0000 Added initial implementation of doubly-linked lists - Added lists.c - Added lists.h - Changed initialisation of rooms to add a third room and to add the rooms to a list. - Added datastructures for area and path nodes for doubly-linked lists. commit241ac7a92b
Author: Barry <barry@omnimenu.ie> Date: Thu Nov 4 23:14:47 2021 +0000 Added area and path creation functions - Added missing header guards. - Increased the size of message contents to 2048. - Added area and path initialization functions. - movePlayerToArea no longer segfaults. - /LOOK added to allow players to find exits. - Amount of paths allowed out of an area has been decreased to 16. - Debug builds are now available from the Makefile. - Removed unused variables. - Input sanatization has been moved to the server-side, phew. - Server messages are now displayed differently to player messages. - New area initialization has been added until I can integrate Guile. - Server's sendBuffer has been renamed messageBuffer. - Areas now have descriptions. - Descriptions are sent to the player upon joining an area and /LOOK-ing. commit85a31a2933
Author: Barry <barry@omnimenu.ie> Date: Thu Oct 21 21:58:55 2021 +0100 Added basic area system - Added playerdata.c - Added basic move command - Added a basic initialisation of two connected rooms - Added datastructures for areas and paths commitae8373d4ce
Author: Barry Kane <bazzakane@gmail.com> Date: Wed Sep 15 00:12:05 2021 +0100 Incremented Version Number. - Incremented version number in preperation for merge. commit18a4f416f6
Author: Barry Kane <bazzakane@gmail.com> Date: Wed Sep 15 00:07:13 2021 +0100 Added basic name system - Added basic name system. - Added playerdata.h. - Added basic /NAME command. TODO: Create proper command system. - Added datastructures for user messages and user names. commit9411803942
Author: Barry Kane <bazzakane@gmail.com> Date: Fri Sep 10 15:07:42 2021 +0100 Increment version message for merge. Incremented the version number by 0.1 for the server. Added version splash to the client. commit7047d0ee08
Author: Barry Kane <bazzakane@gmail.com> Date: Fri Sep 10 15:03:02 2021 +0100 Added two-window messaging to the client. Client now has two seperate Ncurses windows for sending and receiving. Added SIGINT handler which sets a global boolean to gracefully exit and free memory. Sending and Receiving are now on their own threads. A pointer-to-struct is now passed to the threads. The main thread will now wait to cancel the threads upon receiving SIGINT. slowPrintNcurses now takes a window argument. The server now doesn't check that a client receives the message that they sent, allowing for full chat history. commit33bc9bcda0
Author: Barry Kane <bazzakane@gmail.com> Date: Fri Sep 3 18:47:11 2021 +0100 Adapted client to use Ncurses instead of raw terminal output: Created "slowPrintNcurses", which is a version of "slowPrint" compatible with Ncurses screens. Ncurses is now used in place of raw-terminal output. The screen clears after inital start-up messages. C-d no longer exits, and still doesn't spam. Added Ncurses to the ld options of client in the Makefile. Created ld options for server in the Makefile. commit849a80bd37
Author: Barry Kane <barry@omnimenu.ie> Date: Thu Aug 19 23:07:58 2021 +0100 Basic input sanatization: Created new library to deal with user input. Implemented check in client to prevent C-d spamming the server. C-d now exits. Implemented check in client to prevent clients sending messages containing only newlines to the server. commit2c093903a4
Author: Barry Kane <barry@omnimenu.ie> Date: Tue Aug 17 18:57:56 2021 +0100 Git Sanity Check
This commit is contained in:
parent
fa46e40860
commit
e5ff66a84e
4
Makefile
4
Makefile
|
@ -24,6 +24,6 @@ clean:
|
|||
rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug
|
||||
|
||||
all: clean SilverMUDClient SilverMUDServer
|
||||
all: CFLAGS += -Wall -Wextra -Ofast
|
||||
debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og
|
||||
all: CFLAGS += -Wall -Wextra -Ofast
|
||||
debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug
|
||||
debug: clean SilverMUDClientDebug SilverMUDServerDebug
|
||||
|
|
|
@ -1,28 +1,91 @@
|
|||
#+LATEX_HEADER: \RequirePackage[left=0.3in,top=0.3in,right=0.3in,bottom=0.3in, a4paper]{geometry}
|
||||
* SilverMUD: The Hackable Terminal-Top Roleplaying Game.
|
||||
SilverMUD is a tool for creating engaging and communal stories, all over the
|
||||
world through the internet. It's designed to give a gamemaster the same power
|
||||
to improvise that they have at the table, through simple programming and
|
||||
easy-to-understand structures.
|
||||
** Player's Guide
|
||||
*** The Basic Commands
|
||||
|
||||
* Player's Guide
|
||||
** Running The Client
|
||||
*** How To Connect To A Server:
|
||||
To connect to a server, use the command-line option =-i=, and the IP address of
|
||||
the server. If the server admin is hosting the server on a port other than the
|
||||
default port of 5000, you can use the =-p= option with the number of the port. If
|
||||
the connection is successful, you will be placed in the server's login
|
||||
area. Type =/join <player name>=, where player name is a name of your choosing,
|
||||
and you will be placed in the spawn area, ready to play.
|
||||
|
||||
*** Other Launch Options:
|
||||
|
||||
** The Basic Commands
|
||||
SilverMUD is played through a set of very simple commands. To use a command,
|
||||
type a forward-slash (/) followed immediately by the command name. The command
|
||||
can be upper or lower-case.
|
||||
|
||||
| Command | Arguments | Effect |
|
||||
|---------+---------------------------------------------------+--------------------------------------------------------------------|
|
||||
| JOIN | 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. |
|
||||
| Command | Arguments | Effect |
|
||||
|---------+------------------------------------------+---------------------------------------------------------|
|
||||
| JOIN | Character Name | Logs you into the server with the given character name. |
|
||||
| MOVE | Path Name/Path Number | Moves you down the given path. |
|
||||
| LOOK | None | Describes the current area. |
|
||||
| STAT | None | Displays your current status and character sheet. |
|
||||
| SPEC | Core Stat Name | Allows you to apply spec points to a given stat. |
|
||||
| TRY | Core Stat Name/Skill Name, Object Number | Attempt to use the given stat or skill on the object. |
|
||||
|
||||
** Gamemaster's Guide
|
||||
*** Running the Server:
|
||||
* Gamemaster's Guide
|
||||
** Running the Server:
|
||||
|
||||
** Developer's Guide
|
||||
*** Build Prerequisites:
|
||||
* Developer's Guide
|
||||
** Build Prerequisites:
|
||||
SilverMUD has the following dependencies:
|
||||
- GnuTLS
|
||||
- ncurses
|
||||
|
||||
** C Style Guide:
|
||||
These rules attempt to make the program as visually clear as possible, while
|
||||
some rules may be made based on my own personal tastes.
|
||||
|
||||
- () :: These are parentheses.
|
||||
- [] :: These are brackets.
|
||||
- {} :: These are braces.
|
||||
*** Formatting:
|
||||
**** Control Statements:
|
||||
- A space should be between the keyword and the condition. This is to make
|
||||
control statements visually distinct from function calls.
|
||||
|
||||
- Opening braces should be on the line after the control statement, and closing
|
||||
braces on the line after the last statement, on it's own. This is to make the
|
||||
scope of the control statement easily identifiable.
|
||||
|
||||
- else and else if should always be on a new line, not the same line as an if
|
||||
statement's closing brace. This is to more easily distinguish the seperate
|
||||
blocks.
|
||||
|
||||
- Control statements should never omit braces and do single statements. This is
|
||||
mostly personal preference, but I do think it makes things more clear.
|
||||
|
||||
*** Naming:
|
||||
**** Rule 0: NEVER USE i AND j!
|
||||
Never use the variable names i and j. These are easy to confuse, and often make
|
||||
nested loops awful to read. Name these more descriptively.
|
||||
For example:
|
||||
- If you are using a variable to index an array, name the variable index.
|
||||
- If you are indexing multiple arrays, name it "array name + Index".
|
||||
- If you are using it to count something, call it count, or "name of the
|
||||
thing you are counting + count".
|
||||
|
||||
**** Rule 1: No one letter variable names, unless in a mathematical function.
|
||||
You should never use one letter variable names. They're needlessly obtuse and
|
||||
you will not remember their meaning upon re-reading of the source code. The
|
||||
exception to this is when you are writing a function which replicates a
|
||||
mathematical formula or function with commonly accepted notation. However, you
|
||||
should consider if it would be better to break mathematical convention for
|
||||
clarity inside the program, such as when the variable names are the first letter
|
||||
of a word or the mathematical notation uses many similar looking variables.
|
||||
|
||||
**** Rule 2: Prefer to use full words in variable and function names:
|
||||
You should always prefer to use full words in variable and function names. It
|
||||
makes the source code much easier to read, like a sentence. Ideally, if you want
|
||||
to shorten the name, use synonyms or rephrasing before you resort to removing
|
||||
letters.
|
||||
|
||||
*** Comments:
|
||||
|
|
207
src/areadata.c
207
src/areadata.c
|
@ -3,6 +3,7 @@
|
|||
#include <string.h>
|
||||
#include "areadata.h"
|
||||
#include "playerdata.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// ====================
|
||||
// -=[ Area/Paths: ]=-:
|
||||
|
@ -16,18 +17,15 @@ playerArea * createArea(char * nameString, char * descriptionString)
|
|||
|
||||
// Copy the strings into the newly created area:
|
||||
strncpy(createdArea->areaName, nameString, 32 - 1);
|
||||
strncpy(createdArea->areaDescription, descriptionString, MAX - 35);
|
||||
strncpy(createdArea->areaDescription, descriptionString, MAX - 36);
|
||||
|
||||
// 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++)
|
||||
{
|
||||
createdArea->areaExits[index] = NULL;
|
||||
}
|
||||
createdArea->areaDescription[MAX - 36] = '\0';
|
||||
|
||||
// Create a list for the paths in the area:
|
||||
createdArea->pathList = createList(PATH);
|
||||
|
||||
// Return the pointer:
|
||||
return createdArea;
|
||||
}
|
||||
|
@ -35,183 +33,40 @@ 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)
|
||||
{
|
||||
int fromAreaSlot, toAreaSlot;
|
||||
for(fromAreaSlot = 0; fromAreaSlot < 16; fromAreaSlot++)
|
||||
{
|
||||
if(fromArea->areaExits[fromAreaSlot] == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if((fromArea->areaExits[fromAreaSlot] != NULL) && (fromAreaSlot == 15))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
for(toAreaSlot = 0; toAreaSlot < 32; toAreaSlot++)
|
||||
{
|
||||
if(toArea->areaExits[toAreaSlot] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if((toArea->areaExits[toAreaSlot] != 0) && (toAreaSlot == 31))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
// Allocate the new paths:
|
||||
playerPath * fromPath = malloc(sizeof(playerPath));
|
||||
playerPath * toPath = malloc(sizeof(playerPath));
|
||||
fromArea->areaExits[fromAreaSlot] = fromPath;
|
||||
toArea->areaExits[toAreaSlot] = toPath;
|
||||
|
||||
// Setup the from path:
|
||||
strncpy(fromPath->pathName, fromDescription, 32 - 1);
|
||||
fromPath->pathName[31] = '\0';
|
||||
fromPath->pathName[31] = '\0';
|
||||
fromPath->areaToJoin = toArea;
|
||||
|
||||
// Setup the to path:
|
||||
strncpy(toPath->pathName, toDescription, 32 - 1);
|
||||
toPath->pathName[31] = '\0';
|
||||
fromArea->areaExits[fromAreaSlot]->areaToJoin = toArea;
|
||||
toArea->areaExits[toAreaSlot]->areaToJoin = fromArea;
|
||||
toPath->pathName[31] = '\0';
|
||||
toPath->areaToJoin = fromArea;
|
||||
|
||||
// Add to the lists:
|
||||
addToList(fromArea->pathList, fromPath, PATH);
|
||||
addToList(toArea->pathList, toPath, PATH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// -=[ Area/Path Lists: ]=-:
|
||||
// =========================
|
||||
|
||||
// Create and initialize an areaList:
|
||||
areaNode * createAreaList(playerArea * initialArea)
|
||||
// Create a one-way path between two areas given two areas and a string:
|
||||
int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description)
|
||||
{
|
||||
areaNode * newAreaList = malloc(sizeof(areaNode));
|
||||
newAreaList->data = initialArea;
|
||||
newAreaList->next = NULL;
|
||||
newAreaList->prev = NULL;
|
||||
return newAreaList;
|
||||
}
|
||||
// Allocate the new paths:
|
||||
playerPath * path = calloc(1, sizeof(playerPath));
|
||||
|
||||
// Create and initialize an pathList:
|
||||
pathNode * createPathList(playerPath * initialPath)
|
||||
{
|
||||
pathNode * newPathList = malloc(sizeof(pathNode));
|
||||
newPathList->data = initialPath;
|
||||
newPathList->next = NULL;
|
||||
newPathList->prev = NULL;
|
||||
return newPathList;
|
||||
}
|
||||
// Setup the path:
|
||||
strncpy(path->pathName, description, 32 - 1);
|
||||
path->pathName[31] = '\0';
|
||||
path->areaToJoin = toArea;
|
||||
|
||||
// Adds an areaNode to the end of a list, returning it's position:
|
||||
int addAreaNodeToList(areaNode * toList, playerArea * areaToAdd)
|
||||
{
|
||||
areaNode * current;
|
||||
int index = 0;
|
||||
current = toList;
|
||||
while(current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
index++;
|
||||
}
|
||||
current->next = malloc(sizeof(areaNode));
|
||||
current->next->prev = current;
|
||||
current->next->data = areaToAdd;
|
||||
current->next->next = NULL;
|
||||
// Add to the list:
|
||||
addToList(fromArea->pathList, path, PATH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Removes an areaNode from the list, returning 0 on success and -1 on failure:
|
||||
int deleteAreaNodeFromList(areaNode * fromList, playerArea * areaToDelete)
|
||||
{
|
||||
areaNode * current = fromList;
|
||||
while(current->data != areaToDelete && current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
}
|
||||
if(current->next == NULL && current->data != areaToDelete)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
current->prev->next = current->next;
|
||||
if(current->next != NULL)
|
||||
{
|
||||
current->next->prev = current->prev;
|
||||
}
|
||||
free(current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Adds an pathNode to the end of a list, returning it's position:
|
||||
int addPathNodeToList(pathNode * toList, playerPath * pathToAdd)
|
||||
{
|
||||
pathNode * current;
|
||||
int index = 0;
|
||||
current = toList;
|
||||
while(current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
index++;
|
||||
}
|
||||
current->next = malloc(sizeof(pathNode));
|
||||
current->next->prev = current;
|
||||
current->next->data = pathToAdd;
|
||||
current->next->next = NULL;
|
||||
return index;
|
||||
}
|
||||
|
||||
// Removes an pathNode from the list, returning 0 on success and -1 on failure:
|
||||
int deletePathNodeFromList(pathNode * fromList, playerPath * pathToDelete)
|
||||
{
|
||||
pathNode * current = fromList;
|
||||
while(current->data != pathToDelete || current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
}
|
||||
if(current->next == NULL && current->data != pathToDelete)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
current->prev->next = current->next;
|
||||
if(current->next != NULL)
|
||||
{
|
||||
current->next->prev = current->prev;
|
||||
}
|
||||
free(current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the areaNode at the given index from the list:
|
||||
areaNode * getAreaNode(areaNode * fromList, int listIndex)
|
||||
{
|
||||
areaNode * current = fromList;
|
||||
for(int index = 0; index < listIndex; index++)
|
||||
{
|
||||
if(current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Return the pathNode at the given index from the list:
|
||||
pathNode * getPathNode(pathNode * fromList, int listIndex)
|
||||
{
|
||||
pathNode * current = fromList;
|
||||
for(int index = 0; index < listIndex; index++)
|
||||
{
|
||||
if(current->next != NULL)
|
||||
{
|
||||
current = current->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Return the playerArea of the areaNode at the given index from the list:
|
||||
playerArea * getAreaFromList(areaNode * fromList, int listIndex)
|
||||
{
|
||||
areaNode * current = getAreaNode(fromList, listIndex);
|
||||
return current->data;
|
||||
}
|
||||
|
|
|
@ -3,79 +3,39 @@
|
|||
#ifndef AREADATA_H
|
||||
#define AREADATA_H
|
||||
#include "constants.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// ====================
|
||||
// -=[ Area/Paths: ]=-:
|
||||
// ====================
|
||||
|
||||
// Let the compiler know that we're going to define these types:
|
||||
typedef struct playerPath playerPath;
|
||||
typedef struct playerArea playerArea;
|
||||
|
||||
// A path, which contains a name, and a pointer to the area which the player will travel to:
|
||||
struct playerPath
|
||||
{
|
||||
char pathName[32];
|
||||
playerArea * areaToJoin;
|
||||
};
|
||||
|
||||
// An area, containing a list of paths exiting from the area, and a name and description of the area:
|
||||
struct playerArea
|
||||
{
|
||||
list * pathList;
|
||||
char areaName[32];
|
||||
char areaDescription[MAX - 35];
|
||||
playerPath * areaExits[16];
|
||||
};
|
||||
|
||||
// Create an area given a name and description:
|
||||
// Create an area given a name and description, returning a pointer to the new area:
|
||||
playerArea * createArea(char * nameString, char * descriptionString);
|
||||
|
||||
// Create a path between two areas given two areas and two strings:
|
||||
// Create a path between two areas given two areas and two strings, adding it to the both area's list of paths:
|
||||
int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription);
|
||||
|
||||
// =========================
|
||||
// -=[ Area/Path Lists: ]=-:
|
||||
// =========================
|
||||
|
||||
typedef struct areaNode areaNode;
|
||||
typedef struct pathNode pathNode;
|
||||
|
||||
struct pathNode
|
||||
{
|
||||
playerPath * data;
|
||||
pathNode * next;
|
||||
pathNode * prev;
|
||||
};
|
||||
|
||||
struct areaNode
|
||||
{
|
||||
playerArea * data;
|
||||
areaNode * next;
|
||||
areaNode * prev;
|
||||
};
|
||||
|
||||
// Create and initialize an areaList:
|
||||
areaNode * createAreaList(playerArea * initialArea);
|
||||
|
||||
// Create and initialize an pathList:
|
||||
pathNode * createPathList(playerPath * initialPath);
|
||||
|
||||
// Adds an areaNode to the end of a list, returning it's position:
|
||||
int addAreaNodeToList(areaNode * toList, playerArea * areaToAdd);
|
||||
|
||||
// Removes an areaNode from the list, returning 0 on success and -1 on failure:
|
||||
int deleteAreaNodeFromList(areaNode * fromList, playerArea * areaToDelete);
|
||||
|
||||
// Adds an pathNode to the end of a list, returning it's position:
|
||||
int addPathNodeToList(pathNode * toList, playerPath * pathToAdd);
|
||||
|
||||
// Removes an pathNode from the list, returning 0 on success and -1 on failure:
|
||||
int deletePathNodeFromList(pathNode * fromList, playerPath * pathToDelete);
|
||||
|
||||
// Return the areaNode at the given index from the list:
|
||||
areaNode * getAreaNode(areaNode * fromList, int listIndex);
|
||||
|
||||
// Return the pathNode at the given index from the list:
|
||||
pathNode * getPathNode(pathNode * fromList, int listIndex);
|
||||
|
||||
// Return the playerArea of the areaNode at the given index from the list:
|
||||
playerArea * getAreaFromList(areaNode * fromList, int listIndex);
|
||||
// Create a one-way path between two areas given two areas and a string, adding it to the first area's list of paths:
|
||||
int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description);
|
||||
|
||||
// TO BE IMPLEMENTED:
|
||||
/* int saveAreaList(areaNode * listToSave); */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Silverkin Industries Comm-Link Client, Public Demonstration Sample Alpha 0.4.
|
||||
// Silverkin Industries Comm-Link Client, Public Demonstration Sample Alpha 0.5.
|
||||
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
||||
// Barry Kane, 2021
|
||||
#include <netdb.h>
|
||||
|
@ -14,18 +14,20 @@
|
|||
#include <sys/socket.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../queue.h"
|
||||
#include "../constants.h"
|
||||
#include "../playerdata.h"
|
||||
#include "../texteffects.h"
|
||||
#include "../inputoutput.h"
|
||||
|
||||
// A struct for bundling all needed paramaters for a thread so we can pass them using a void pointer:
|
||||
// A struct for bundling all needed parameters for a thread so we can pass them using a void pointer:
|
||||
typedef struct threadparameters
|
||||
{
|
||||
gnutls_session_t tlsSession;
|
||||
FILE * loggingStream;
|
||||
bool loggingFlag;
|
||||
WINDOW * window;
|
||||
int characterDelay;
|
||||
} threadparameters;
|
||||
|
||||
// Use sockaddr as a type:
|
||||
|
@ -34,17 +36,22 @@ typedef struct sockaddr sockaddr;
|
|||
// A globally available exit boolean:
|
||||
bool shouldExit = false;
|
||||
|
||||
// A function for managing the sending thread:
|
||||
void * messageSender(void * parameters)
|
||||
{
|
||||
struct threadparameters *threadParameters = parameters;
|
||||
threadparameters * threadParameters = parameters;
|
||||
gnutls_session_t tlsSession = threadParameters->tlsSession;
|
||||
FILE * loggingStream = threadParameters->loggingStream;
|
||||
bool loggingFlag = threadParameters->loggingFlag;
|
||||
WINDOW * window = threadParameters->window;
|
||||
userMessage sendBuffer;
|
||||
|
||||
// Repeatedly get input from the user, place it in a userMessage, and send it to the server:
|
||||
while (!shouldExit)
|
||||
{
|
||||
// Print the prompt:
|
||||
wprintw(threadParameters->window, "\n\n\nCOMM-LINK> ");
|
||||
if (wgetnstr(threadParameters->window, sendBuffer.messageContent, MAX) == ERR)
|
||||
wprintw(window, "\n\n\nCOMM-LINK> ");
|
||||
if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR)
|
||||
{
|
||||
// Quit if there's any funny business with getting input:
|
||||
pthread_exit(NULL);
|
||||
|
@ -57,78 +64,104 @@ void * messageSender(void * parameters)
|
|||
}
|
||||
|
||||
// Send the message to the log if logging is enabled:
|
||||
if (threadParameters->loggingFlag == true)
|
||||
if (loggingFlag == true)
|
||||
{
|
||||
fputs(sendBuffer.messageContent, threadParameters->loggingStream);
|
||||
fputs("\n", threadParameters->loggingStream);
|
||||
fflush(threadParameters->loggingStream);
|
||||
fputs(sendBuffer.messageContent, loggingStream);
|
||||
fputs("\n", loggingStream);
|
||||
fflush(loggingStream);
|
||||
}
|
||||
|
||||
// Send the message off to the server:
|
||||
messageSend(threadParameters->tlsSession, &sendBuffer);
|
||||
messageSend(tlsSession, &sendBuffer);
|
||||
}
|
||||
|
||||
// Rejoin the main thread:
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
// A function for managing the receiving thread:
|
||||
void * messageReceiver(void * parameters)
|
||||
{
|
||||
threadparameters * threadParameters = parameters;
|
||||
gnutls_session_t tlsSession = threadParameters->tlsSession;
|
||||
FILE * loggingStream = threadParameters->loggingStream;
|
||||
int characterDelay = threadParameters->characterDelay;
|
||||
bool loggingFlag = threadParameters->loggingFlag;
|
||||
WINDOW * window = threadParameters->window;
|
||||
|
||||
int returnValue = 0;
|
||||
userMessage receiveBuffer;
|
||||
bool serverMessage = false;
|
||||
struct threadparameters *threadParameters = parameters;
|
||||
int screenWidth = getmaxx(threadParameters->window);
|
||||
|
||||
// Repeatedly take messages from the server and print them to the chat log window:
|
||||
while (!shouldExit)
|
||||
{
|
||||
returnValue = messageReceive(threadParameters->tlsSession, &receiveBuffer);
|
||||
// Get the next message:
|
||||
returnValue = messageReceive(tlsSession, &receiveBuffer);
|
||||
|
||||
// Check we haven't been disconnected:
|
||||
if(returnValue == -10 || returnValue == 0)
|
||||
if (returnValue == -10 || returnValue == 0)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
// Check if it's a server message:
|
||||
else if (receiveBuffer.senderName[0] == '\0')
|
||||
{
|
||||
wrapString(receiveBuffer.messageContent,
|
||||
strlen(receiveBuffer.messageContent) - 1, screenWidth);
|
||||
// Check if it's a command to disconnect:
|
||||
if (receiveBuffer.messageContent[0] == '\0')
|
||||
{
|
||||
shouldExit = true;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
if(serverMessage == false)
|
||||
|
||||
// Fit the string to the screen:
|
||||
wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1, screenWidth);
|
||||
|
||||
// If it's the first server message in a block, begin a block of server messages:
|
||||
if (serverMessage == false)
|
||||
{
|
||||
slowPrintNcurses("\n --====<>====--", 4000, threadParameters->window, true);
|
||||
slowPrintNcurses("\n --====<>====--", characterDelay, window, true);
|
||||
serverMessage = true;
|
||||
}
|
||||
slowPrintNcurses("\n", 4000, threadParameters->window, true);
|
||||
slowPrintNcurses(receiveBuffer.messageContent, 4000, threadParameters->window, false);
|
||||
slowPrintNcurses("\n", 4000, threadParameters->window, true);
|
||||
|
||||
// Print the message:
|
||||
slowPrintNcurses("\n", characterDelay, window, true);
|
||||
slowPrintNcurses(receiveBuffer.messageContent, characterDelay,
|
||||
window, false);
|
||||
slowPrintNcurses("\n", characterDelay, window, true);
|
||||
}
|
||||
// It's a user message:
|
||||
else
|
||||
{
|
||||
wrapString(receiveBuffer.messageContent,
|
||||
strlen(receiveBuffer.messageContent) - 1,
|
||||
// Fit the string to the screen:
|
||||
wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1,
|
||||
screenWidth - strlen(receiveBuffer.senderName) - 2);
|
||||
if (threadParameters->loggingFlag == true)
|
||||
|
||||
// If the user has requested logging, insert the message into the file:
|
||||
if (loggingFlag == true)
|
||||
{
|
||||
fputs(receiveBuffer.senderName, threadParameters->loggingStream);
|
||||
fputs(": ", threadParameters->loggingStream);
|
||||
fputs(receiveBuffer.messageContent, threadParameters->loggingStream);
|
||||
fflush(threadParameters->loggingStream);
|
||||
fputs(receiveBuffer.senderName, loggingStream);
|
||||
fputs(": ", loggingStream);
|
||||
fputs(receiveBuffer.messageContent, loggingStream);
|
||||
fflush(loggingStream);
|
||||
}
|
||||
if(serverMessage == true)
|
||||
|
||||
// If we're in a block of server messages, end it:
|
||||
if (serverMessage == true)
|
||||
{
|
||||
slowPrintNcurses("\n --====<>====-- \n", 4000, threadParameters->window, true);
|
||||
slowPrintNcurses("\n --====<>====-- \n", characterDelay, window, true);
|
||||
serverMessage = false;
|
||||
}
|
||||
slowPrintNcurses(receiveBuffer.senderName, 4000, threadParameters->window, true);
|
||||
slowPrintNcurses(": ", 4000, threadParameters->window, true);
|
||||
slowPrintNcurses(receiveBuffer.messageContent, 4000, threadParameters->window, false);
|
||||
|
||||
// Print the message:
|
||||
slowPrintNcurses(receiveBuffer.senderName, characterDelay, window, true);
|
||||
slowPrintNcurses(": ", characterDelay, window, true);
|
||||
slowPrintNcurses(receiveBuffer.messageContent, characterDelay, window, false);
|
||||
}
|
||||
}
|
||||
// Exit the thread if shouldExit is true:
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
@ -148,61 +181,61 @@ int main(int argc, char ** argv)
|
|||
bool chatLogging = false, gameLogging = false;
|
||||
|
||||
// Print welcome message:
|
||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK CLIENT ====--\nVersion Alpha 0.4\n", 5000);
|
||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK CLIENT ====--\nVersion Alpha 0.5\n", 5000);
|
||||
|
||||
// Parse command-line options:
|
||||
while ((currentopt = getopt(argc, argv, "i:c:g:p:d:")) != -1)
|
||||
{
|
||||
switch (currentopt)
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
strncpy(ipAddress, optarg, 32);
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
strncpy(chatLogPath, optarg, PATH_MAX + 1);
|
||||
chatLog = fopen(chatLogPath, "a+");
|
||||
if (chatLog == NULL)
|
||||
case 'i':
|
||||
{
|
||||
chatLogging = false;
|
||||
memcpy(ipAddress, optarg, 32);
|
||||
break;
|
||||
}
|
||||
else
|
||||
case 'c':
|
||||
{
|
||||
chatLogging = true;
|
||||
memcpy(chatLogPath, optarg, PATH_MAX + 1);
|
||||
chatLog = fopen(chatLogPath, "a+");
|
||||
if (chatLog == NULL)
|
||||
{
|
||||
chatLogging = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
chatLogging = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
{
|
||||
strncpy(gameLogPath, optarg, PATH_MAX + 1);
|
||||
gameLog = fopen(gameLogPath, "a+");
|
||||
if (gameLog == NULL)
|
||||
case 'g':
|
||||
{
|
||||
gameLogging = false;
|
||||
memcpy(gameLogPath, optarg, PATH_MAX + 1);
|
||||
gameLog = fopen(gameLogPath, "a+");
|
||||
if (gameLog == NULL)
|
||||
{
|
||||
gameLogging = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
gameLogging = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case 'p':
|
||||
{
|
||||
gameLogging = true;
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
characterDelay = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
{
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
characterDelay = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
{
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,9 +250,8 @@ int main(int argc, char ** argv)
|
|||
{
|
||||
slowPrint("Socket successfully created.\n", characterDelay);
|
||||
}
|
||||
bzero(&serverAddress, sizeof(serverAddress));
|
||||
|
||||
// Set our IP Address and port. Default to localhost for testing:
|
||||
// Set our IP address and port. Default to localhost for testing:
|
||||
serverAddress.sin_family = AF_INET;
|
||||
serverAddress.sin_addr.s_addr = inet_addr(ipAddress);
|
||||
serverAddress.sin_port = htons(port);
|
||||
|
@ -230,17 +262,11 @@ int main(int argc, char ** argv)
|
|||
slowPrint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", characterDelay);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
slowPrint("Connected to the Silverkin Industries Comm-Link Server:\nHave a pleasant day.\n", characterDelay);
|
||||
}
|
||||
usleep(100000);
|
||||
|
||||
// Setup a GnuTLS session and initialize it:
|
||||
gnutls_session_t tlsSession = NULL;
|
||||
if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0)
|
||||
{
|
||||
// Failure Case
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -256,7 +282,7 @@ int main(int argc, char ** argv)
|
|||
// Use the default for the GnuTLS handshake timeout:
|
||||
gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
|
||||
// Repeatedly attempt to handshake unless we encounter a fatal error:
|
||||
// Repeatedly attempt to handshake unless we encounter a fatal error:
|
||||
int returnValue = -1;
|
||||
do
|
||||
{
|
||||
|
@ -278,6 +304,7 @@ int main(int argc, char ** argv)
|
|||
logArea->window = newwin(LINES - 5, COLS - 2, 1, 1);
|
||||
logArea->tlsSession = tlsSession;
|
||||
logArea->loggingFlag = chatLogging;
|
||||
logArea->characterDelay = characterDelay;
|
||||
if (chatLog != NULL)
|
||||
{
|
||||
logArea->loggingStream = chatLog;
|
||||
|
|
571
src/gamelogic.c
571
src/gamelogic.c
|
@ -5,9 +5,11 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "gamelogic.h"
|
||||
#include "playerdata.h"
|
||||
#include "linkedlist.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// =======================
|
||||
|
@ -15,92 +17,84 @@
|
|||
// =======================
|
||||
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicLoop(void * parameters)
|
||||
void * gameLogicHandler(void * parameters)
|
||||
{
|
||||
gameLogicParameters * threadParameters = parameters;
|
||||
inputMessage * currentInput = NULL;
|
||||
commandQueue * commandQueue = createCommandQueue();
|
||||
while(true)
|
||||
queue * commandQueue = createQueue();
|
||||
while (true)
|
||||
{
|
||||
// Evaluate remaining commands:
|
||||
if(commandQueue->currentLength != 0)
|
||||
while (commandQueue->itemCount != 0)
|
||||
{
|
||||
evaluateNextCommand(threadParameters, commandQueue);
|
||||
}
|
||||
// Check for new messages and pop them off the queue:
|
||||
if(threadParameters->inputQueue->currentLength != 0)
|
||||
|
||||
// Wait if there is nothing to do:
|
||||
if (threadParameters->inputQueue->itemCount == 0)
|
||||
{
|
||||
while(threadParameters->inputQueue->lock == true);
|
||||
pthread_cond_wait(&threadParameters->inputQueue->condition, &threadParameters->inputQueue->mutex);
|
||||
}
|
||||
|
||||
// Check for new messages and pop them off the queue:
|
||||
if (threadParameters->inputQueue->itemCount != 0)
|
||||
{
|
||||
while (threadParameters->inputQueue->lock == true);
|
||||
threadParameters->inputQueue->lock = true;
|
||||
currentInput = peekInputMessage(threadParameters->inputQueue);
|
||||
currentInput = peekQueue(threadParameters->inputQueue)->data.inputMessage;
|
||||
userInputSanatize(currentInput->content->messageContent, MAX);
|
||||
// A slash as the first character means the message is a user command:
|
||||
if(currentInput->content->messageContent[0] == '/')
|
||||
if (currentInput->content->messageContent[0] == '/')
|
||||
{
|
||||
queueMessagedCommand(commandQueue, currentInput);
|
||||
}
|
||||
else if(currentInput->sender->currentArea == getAreaFromList(threadParameters->areaList, 0))
|
||||
{
|
||||
currentInput = NULL;
|
||||
threadParameters->inputQueue->lock = false;
|
||||
dequeueInputMessage(threadParameters->inputQueue);
|
||||
}
|
||||
else
|
||||
|
||||
else if (!(currentInput->sender->currentArea == getFromList(threadParameters->areaList, 0)->area) &&
|
||||
currentInput->content->messageContent[0] != '\n')
|
||||
{
|
||||
// Copy the correct name into the sender name field:
|
||||
strncpy(currentInput->content->senderName, currentInput->sender->playerName, 32);
|
||||
// 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++)
|
||||
currentInput->content->senderName[31] = '\0';
|
||||
|
||||
// Allocate an array of playerInfo to store the current players in the area for the output message:
|
||||
playerInfo ** recipients = malloc(sizeof(playerInfo*) * PLAYERCOUNT);
|
||||
|
||||
// Initialize them all to NULL:
|
||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
recipients[index] = NULL;
|
||||
}
|
||||
int recipientCount = 0;
|
||||
for(int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++)
|
||||
|
||||
// Get the players in the current area and add them to our array:
|
||||
int recipientIndex = 0;
|
||||
for (int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++)
|
||||
{
|
||||
if(threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea)
|
||||
if (threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea)
|
||||
{
|
||||
recipients[recipientCount] = &threadParameters->connectedPlayers[playerIndex];
|
||||
recipientCount++;
|
||||
recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex];
|
||||
recipientIndex++;
|
||||
}
|
||||
}
|
||||
if(currentInput->content->messageContent[0] != '\n')
|
||||
{
|
||||
queueTargetedOutputMessage(threadParameters->outputQueue, currentInput->content, recipients, recipientCount);
|
||||
}
|
||||
|
||||
// Create the outputMessage for the queue:
|
||||
outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex);
|
||||
|
||||
// Push the message onto the queue:
|
||||
pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Free the array;
|
||||
free(recipients);
|
||||
}
|
||||
currentInput = NULL;
|
||||
threadParameters->inputQueue->lock = false;
|
||||
dequeueInputMessage(threadParameters->inputQueue);
|
||||
popQueue(threadParameters->inputQueue);
|
||||
}
|
||||
}
|
||||
pthread_exit(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)
|
||||
// Evaluate the next commandEvent in a queue:
|
||||
void queueMessagedCommand(queue * queue, inputMessage * messageToQueue)
|
||||
{
|
||||
// Prepare the new commandEvent:
|
||||
commandEvent * newCommand = calloc(1, sizeof(commandEvent));
|
||||
|
@ -112,60 +106,29 @@ int queueMessagedCommand(commandQueue * queue, inputMessage * messageToQueue)
|
|||
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],
|
||||
memcpy(newCommand->command, &messageToQueue->content->messageContent[1], 16);
|
||||
memcpy(newCommand->arguments, &messageToQueue->content->messageContent[strlen(newCommand->command) + 2],
|
||||
MAX - (strlen(newCommand->command) + 2));
|
||||
|
||||
// Ensure the arguments are safe to parse, without adding newlines:
|
||||
userNameSanatize(newCommand->command, 16);
|
||||
newCommand->command[15] = '\0';
|
||||
|
||||
userNameSanatize(newCommand->arguments, MAX);
|
||||
|
||||
newCommand->arguments[MAX - 1] = '\0';
|
||||
|
||||
// Lowercase the command for easier comparison:
|
||||
for (char * character = newCommand->command; *character; ++character)
|
||||
{
|
||||
*character = tolower(*character);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
pushQueue(queue, newCommand, COMMAND);
|
||||
}
|
||||
|
||||
// Enqueue a command to a commandQueue:
|
||||
int queueCommand(commandQueue * queue, char * command, char * arguments, int commandLength, int argumentsLength, playerInfo * callingPlayer)
|
||||
// Enqueue a command to a queue:
|
||||
void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength,
|
||||
playerInfo * callingPlayer)
|
||||
{
|
||||
// Prepare the new commandEvent:
|
||||
commandEvent * newCommand = calloc(1, sizeof(commandEvent));
|
||||
|
@ -175,171 +138,72 @@ int queueCommand(commandQueue * queue, char * command, char * arguments, int com
|
|||
|
||||
// Copy the command and arguments:
|
||||
strncpy(newCommand->command, command, commandLength);
|
||||
strncpy(newCommand->arguments, arguments, argumentsLength);
|
||||
|
||||
if (argumentsLength > 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
pushQueue(queue, newCommand, COMMAND);
|
||||
}
|
||||
|
||||
// Dequeue the front commandEvent from a commandQueue:
|
||||
int dequeueCommand(commandQueue * queue)
|
||||
// Evaluate the next commandEvent in a queue:
|
||||
int evaluateNextCommand(gameLogicParameters * parameters, queue * queue)
|
||||
{
|
||||
// Wait for the queue to unlock:
|
||||
commandEvent * currentCommand = peekQueue(queue)->data.command;
|
||||
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)
|
||||
if (currentCommand == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Try command: Attempt to use a stat or skill on an object:
|
||||
if(strncmp(currentCommand->command, "try", 3) == 0)
|
||||
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:
|
||||
{
|
||||
sprintf(tryMessage->messageContent,"%d",
|
||||
skillCheck(currentCommand->caller, 10, currentCommand->arguments, strlen(currentCommand->arguments),
|
||||
parameters->globalSkillList));
|
||||
break;
|
||||
}
|
||||
}
|
||||
queueTargetedOutputMessage(parameters->outputQueue, tryMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Temporary message until we can implement objects, events, and challenges.
|
||||
strcpy(tryMessage->messageContent, "The try command is currently not implemented. Implement it if you want to use it.\n");
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * tryOutputMessage = createTargetedOutputMessage(tryMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, tryOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Free the userMessage:
|
||||
free(tryMessage);
|
||||
}
|
||||
// Exit command: Sends an "empty" exit message to disconnect a client:
|
||||
if(strncmp(currentCommand->command, "exit", 4) == 0)
|
||||
if (strncmp(currentCommand->command, "exit", 4) == 0)
|
||||
{
|
||||
// Allocate a userMessage containing null characters as the first char in both fields:
|
||||
userMessage * exitMessage = malloc(sizeof(userMessage));
|
||||
exitMessage->senderName[0] = '\0';
|
||||
exitMessage->messageContent[0] = '\0';
|
||||
queueTargetedOutputMessage(parameters->outputQueue, exitMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * exitOutputMessage = createTargetedOutputMessage(exitMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, exitOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Free the userMessage
|
||||
free(exitMessage);
|
||||
}
|
||||
|
||||
// Move command: Moves the caller to a different area given a path name or number:
|
||||
if(strncmp(currentCommand->command, "move", 4) == 0)
|
||||
if (strncmp(currentCommand->command, "move", 4) == 0)
|
||||
{
|
||||
char requestedPath[32];
|
||||
if(strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getAreaFromList(parameters->areaList, 0))
|
||||
if (strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area)
|
||||
{
|
||||
strncpy(requestedPath, currentCommand->arguments, 32);
|
||||
memcpy(requestedPath, currentCommand->arguments, 32);
|
||||
userNameSanatize(requestedPath, 32);
|
||||
requestedPath[31] = '\0';
|
||||
if(movePlayerToArea(currentCommand->caller, requestedPath) == 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:
|
||||
|
@ -351,7 +215,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
}
|
||||
|
||||
// Look command: Returns the description of the current area and paths:
|
||||
if(strncmp(currentCommand->command, "look", 4) == 0)
|
||||
if (strncmp(currentCommand->command, "look", 4) == 0)
|
||||
{
|
||||
char formattedString[64];
|
||||
userMessage * lookMessage = calloc(1, sizeof(userMessage));
|
||||
|
@ -359,45 +223,69 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
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);
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
//queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
|
||||
bzero(lookMessage, sizeof(userMessage));
|
||||
if(currentCommand->caller->currentArea->areaExits[0] != NULL)
|
||||
|
||||
// Loop through the paths and send the appropriate amount of messages:
|
||||
int charCount = 13;
|
||||
strncat(lookMessage->messageContent, "You can go:", 13);
|
||||
|
||||
if (currentCommand->caller->currentArea->pathList->itemCount > 0)
|
||||
{
|
||||
strncat(lookMessage->messageContent, "You can go:", 13);
|
||||
for(int index = 0; index < 16; index++)
|
||||
for(size_t index = 0; index < currentCommand->caller->currentArea->pathList->itemCount; index++)
|
||||
{
|
||||
if(currentCommand->caller->currentArea->areaExits[index] != NULL)
|
||||
if ((charCount + 64) >= MAX)
|
||||
{
|
||||
snprintf(formattedString, 64, "\n\t%d. %s", index + 1, currentCommand->caller->currentArea->areaExits[index]->pathName);
|
||||
strncat(lookMessage->messageContent, formattedString, 64);
|
||||
lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
bzero(lookMessage, sizeof(userMessage));
|
||||
charCount = 0;
|
||||
}
|
||||
snprintf(formattedString, 64, "\n\t%ld. %s", index + 1,
|
||||
getFromList(currentCommand->caller->currentArea->pathList, index)->path->pathName);
|
||||
strncat(lookMessage->messageContent, formattedString, 64);
|
||||
charCount += 64;
|
||||
}
|
||||
queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
|
||||
// Allocate another outputMessage for the queue:
|
||||
lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
||||
}
|
||||
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 (strncmp(currentCommand->command, "join", 4) == 0)
|
||||
{
|
||||
if(currentCommand->caller->currentArea == getAreaFromList(parameters->areaList, 0))
|
||||
if (currentCommand->caller->currentArea == getFromList(parameters->areaList, 0)->area)
|
||||
{
|
||||
bool validName = true;
|
||||
for(int index = 0; index < *parameters->playerCount; index++)
|
||||
{
|
||||
if(currentCommand->arguments[0] == '\0')
|
||||
if (currentCommand->arguments[0] == '\0')
|
||||
{
|
||||
validName = false;
|
||||
}
|
||||
if(strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0)
|
||||
if (strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0)
|
||||
{
|
||||
validName = false;
|
||||
}
|
||||
}
|
||||
if(validName)
|
||||
if (validName)
|
||||
{
|
||||
strncpy(currentCommand->caller->playerName, currentCommand->arguments, 16);
|
||||
currentCommand->caller->currentArea = getAreaFromList(parameters->areaList, 1);
|
||||
currentCommand->caller->currentArea = getFromList(parameters->areaList, 1)->area;
|
||||
// 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;
|
||||
|
@ -407,20 +295,12 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
}
|
||||
}
|
||||
// Talk command: Allows the player to begin a chat session with another player:
|
||||
if(strncmp(currentCommand->command, "talk", 4) == 0)
|
||||
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)
|
||||
if (strncmp(currentCommand->command, "stat", 4) == 0)
|
||||
{
|
||||
char * formattedString = calloc(121, sizeof(char));
|
||||
userMessage * statMessage = calloc(1, sizeof(userMessage));
|
||||
|
@ -440,7 +320,7 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
strncat(statMessage->messageContent, formattedString, 120);
|
||||
|
||||
// Levelling stats: Current XP, and spec points.
|
||||
if(currentCommand->caller->stats->specPoints > 0 || currentCommand->caller->stats->skillPoints > 0)
|
||||
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);
|
||||
|
@ -450,29 +330,39 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
snprintf(formattedString, 120, "Current Experience: %ld", currentCommand->caller->stats->experience);
|
||||
}
|
||||
strncat(statMessage->messageContent, formattedString, 120);
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||
bzero(statMessage->messageContent, sizeof(char) * MAX);
|
||||
if(currentCommand->caller->skills->head != NULL)
|
||||
if (currentCommand->caller->skills->head != NULL)
|
||||
{
|
||||
skillNode * currentSkill = currentCommand->caller->skills->head;
|
||||
size_t skillIndex = 0;
|
||||
int charCount = 0;
|
||||
bool addNewline = false;
|
||||
while(currentSkill != NULL)
|
||||
playerSkill * skill;
|
||||
while (skillIndex < currentCommand->caller->skills->itemCount)
|
||||
{
|
||||
snprintf(formattedString, 120, "| %2d | %31s ",
|
||||
currentSkill->skill->skillPoints, currentSkill->skill->skillName);
|
||||
skill = getFromList(currentCommand->caller->skills, skillIndex)->skill;
|
||||
skillIndex++;
|
||||
snprintf(formattedString, 120, "| %2d | %31s ", skill->skillPoints, skill->skillName);
|
||||
charCount += 43;
|
||||
strncat(statMessage->messageContent, formattedString, 120);
|
||||
if((charCount + 43) >= MAX)
|
||||
if ((charCount + 43) >= MAX)
|
||||
{
|
||||
strncat(statMessage->messageContent, "\n", 2);
|
||||
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||
// Allocate an outputMessage for the queue:
|
||||
statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
||||
bzero(statMessage, sizeof(userMessage));
|
||||
charCount = 0;
|
||||
break;
|
||||
}
|
||||
else if(addNewline)
|
||||
else if (addNewline)
|
||||
{
|
||||
strncat(statMessage->messageContent, "|\n", 3);
|
||||
charCount++;
|
||||
|
@ -481,29 +371,31 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
else
|
||||
{
|
||||
addNewline = true;
|
||||
}
|
||||
currentSkill = currentSkill->next;
|
||||
|
||||
}
|
||||
}
|
||||
queueTargetedOutputMessage(parameters->outputQueue, statMessage, ¤tCommand->caller, 1);
|
||||
// Allocate an outputMessage for the queue:
|
||||
statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
||||
}
|
||||
free(statMessage);
|
||||
free(formattedString);
|
||||
}
|
||||
|
||||
// Spec command: Assign spec points to stats:
|
||||
if(strncmp(currentCommand->command, "spec", 4) == 0)
|
||||
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)
|
||||
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)
|
||||
if (selectedAmount > 0 && (currentCommand->caller->stats->specPoints - selectedAmount) >= 0)
|
||||
{
|
||||
switch (selectedStat)
|
||||
{
|
||||
|
@ -558,8 +450,11 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
strncat(specMessage->messageContent, "You have no spec points available.", 35);
|
||||
}
|
||||
|
||||
// Send the message:
|
||||
queueTargetedOutputMessage(parameters->outputQueue, specMessage, ¤tCommand->caller, 1);
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * specOutputMessage = createTargetedOutputMessage(specMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, specOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Show the new stat sheet:
|
||||
queue->lock = false;
|
||||
|
@ -570,11 +465,11 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
free(specMessage);
|
||||
free(formattedString);
|
||||
}
|
||||
if(strncmp(currentCommand->command, "skill", 5) == 0)
|
||||
if (strncmp(currentCommand->command, "skill", 5) == 0)
|
||||
{
|
||||
userMessage * skillMessage = calloc(1, sizeof(userMessage));
|
||||
skillMessage->senderName[0] = '\0';
|
||||
if((currentCommand->caller->stats->skillPoints - 1) >= 0)
|
||||
if ((currentCommand->caller->stats->skillPoints - 1) >= 0)
|
||||
{
|
||||
int returnValue = takeSkill(parameters->globalSkillList, currentCommand->arguments,
|
||||
strlen(currentCommand->arguments), currentCommand->caller);
|
||||
|
@ -599,29 +494,42 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
{
|
||||
strcpy(skillMessage->messageContent, "You don't have enough skill points to take this skill.\n");
|
||||
}
|
||||
queueTargetedOutputMessage(parameters->outputQueue, skillMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * skillOutputMessage = createTargetedOutputMessage(skillMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, skillOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
free(skillMessage);
|
||||
}
|
||||
if(strncmp(currentCommand->command, "listskills", 10) == 0)
|
||||
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;
|
||||
size_t skillIndex = 0;
|
||||
bool addNewline = false;
|
||||
while(currentSkill != NULL)
|
||||
playerSkill * currentSkill;
|
||||
while (skillIndex < parameters->globalSkillList->itemCount)
|
||||
{
|
||||
snprintf(formattedString, 120, "| %-31s ", currentSkill->skill->skillName);
|
||||
currentSkill = getFromList(parameters->globalSkillList, skillIndex)->skill;
|
||||
snprintf(formattedString, 120, "| %-31s ", currentSkill->skillName);
|
||||
charCount += 43;
|
||||
strncat(listMessage->messageContent, formattedString, 120);
|
||||
if((charCount + 46) >= MAX)
|
||||
if ((charCount + 46) >= MAX)
|
||||
{
|
||||
queueTargetedOutputMessage(parameters->outputQueue, listMessage, ¤tCommand->caller, 1);
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
bzero(listMessage, sizeof(userMessage));
|
||||
charCount = 0;
|
||||
addNewline = false;
|
||||
}
|
||||
else if(addNewline)
|
||||
else if (addNewline)
|
||||
{
|
||||
strncat(listMessage->messageContent, "|\n", 3);
|
||||
charCount++;
|
||||
|
@ -631,24 +539,28 @@ int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue)
|
|||
{
|
||||
addNewline = true;
|
||||
}
|
||||
currentSkill = currentSkill->next;
|
||||
skillIndex++;
|
||||
}
|
||||
queueTargetedOutputMessage(parameters->outputQueue, listMessage, ¤tCommand->caller, 1);
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE);
|
||||
free(listMessage);
|
||||
free(formattedString);
|
||||
}
|
||||
// Remove the current command and unlock the queue:
|
||||
currentCommand = NULL;
|
||||
queue->lock = false;
|
||||
dequeueCommand(queue);
|
||||
popQueue(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Run a stat check:
|
||||
// Run a stat check for the given player, returning an outcome:
|
||||
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
||||
{
|
||||
// Calculate the chance:
|
||||
if(chance > 100 || chance < 0)
|
||||
if (chance > 100 || chance < 0)
|
||||
{
|
||||
return ERROR;
|
||||
}
|
||||
|
@ -689,9 +601,9 @@ outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
|||
}
|
||||
}
|
||||
int attempt = (random() % 100) + modifier;
|
||||
if(attempt >= chance)
|
||||
if (attempt >= chance)
|
||||
{
|
||||
if(attempt >= 98)
|
||||
if (attempt >= 98)
|
||||
{
|
||||
return CRITICAL_SUCCESS;
|
||||
}
|
||||
|
@ -702,7 +614,7 @@ outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
|||
}
|
||||
else
|
||||
{
|
||||
if(attempt <= 2)
|
||||
if (attempt <= 2)
|
||||
{
|
||||
return CRITICAL_FAILURE;
|
||||
}
|
||||
|
@ -713,11 +625,11 @@ outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
|||
}
|
||||
}
|
||||
|
||||
// Run a skill check:
|
||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, skillList * globalSkillList)
|
||||
// Run a skill check for the given player, returning an outcome:
|
||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList)
|
||||
{
|
||||
// Calculate the chance:
|
||||
if(chance > 100 || chance < 0)
|
||||
if (chance > 100 || chance < 0)
|
||||
{
|
||||
return ERROR;
|
||||
}
|
||||
|
@ -725,53 +637,46 @@ outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t ski
|
|||
|
||||
// Check if the player has the given skill:
|
||||
bool playerHasSkill = false;
|
||||
skillNode * currentPlayerNode = player->skills->head;
|
||||
while(currentPlayerNode != NULL)
|
||||
size_t playerIndex = 0;
|
||||
while (playerIndex < player->skills->itemCount)
|
||||
{
|
||||
if(strncmp(skillName, currentPlayerNode->skill->skillName, skillNameLength) == 0)
|
||||
if (strncmp(skillName, getFromList(player->skills, playerIndex)->skill->skillName, skillNameLength) != 0)
|
||||
{
|
||||
playerHasSkill = true;
|
||||
break;
|
||||
}
|
||||
currentPlayerNode = currentPlayerNode->next;
|
||||
playerIndex++;
|
||||
}
|
||||
|
||||
// If the player doesn't have the skill, check if it's in the game and is trained:
|
||||
bool trainedSkill = false;
|
||||
if(!playerHasSkill)
|
||||
size_t globalIndex = 0;
|
||||
while (globalIndex < globalSkillList->itemCount)
|
||||
{
|
||||
skillNode * currentNode = globalSkillList->head;
|
||||
while(strncmp(skillName, currentNode->skill->skillName, 32) != 0)
|
||||
if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) != 0)
|
||||
{
|
||||
if(currentNode->next == NULL)
|
||||
{
|
||||
fprintf(stderr, "Skill doesn't exist in skill list.\n");
|
||||
return ERROR;
|
||||
}
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
if(currentNode->skill->trainedSkill == true)
|
||||
{
|
||||
trainedSkill = true;
|
||||
trainedSkill = getFromList(globalSkillList, globalIndex)->skill->trainedSkill;
|
||||
break;
|
||||
}
|
||||
globalIndex++;
|
||||
}
|
||||
|
||||
|
||||
// Calculate the modifier:
|
||||
int modifier = 0;
|
||||
if(trainedSkill)
|
||||
if (trainedSkill)
|
||||
{
|
||||
modifier = -100;
|
||||
}
|
||||
else
|
||||
else if (playerHasSkill)
|
||||
{
|
||||
modifier = currentPlayerNode->skill->skillPoints * 4;
|
||||
modifier = getFromList(player->skills, playerIndex)->skill->skillModifier * 4;
|
||||
}
|
||||
|
||||
// Attempt the check:
|
||||
int attempt = (random() % 100) + modifier;
|
||||
if(attempt >= chance)
|
||||
if (attempt >= chance)
|
||||
{
|
||||
if(attempt >= 98)
|
||||
if (attempt >= 98)
|
||||
{
|
||||
return CRITICAL_SUCCESS;
|
||||
}
|
||||
|
@ -782,7 +687,7 @@ outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t ski
|
|||
}
|
||||
else
|
||||
{
|
||||
if(attempt <= 2)
|
||||
if (attempt <= 2)
|
||||
{
|
||||
return CRITICAL_FAILURE;
|
||||
}
|
||||
|
@ -793,17 +698,17 @@ outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t ski
|
|||
}
|
||||
}
|
||||
|
||||
// Move a player to a different area given a path in the area:
|
||||
// Move a player along a path in their current area:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath)
|
||||
{
|
||||
// Check if a number was given first:
|
||||
int selected = atoi(requestedPath);
|
||||
if(selected != 0)
|
||||
size_t selected = atoi(requestedPath);
|
||||
if (selected != 0 && !(selected > player->currentArea->pathList->itemCount))
|
||||
{
|
||||
if(player->currentArea->areaExits[selected - 1] != NULL &&
|
||||
player->currentArea->areaExits[selected - 1]->areaToJoin != NULL)
|
||||
if (getFromList(player->currentArea->pathList, selected - 1)->path != NULL &&
|
||||
getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin != NULL)
|
||||
{
|
||||
player->currentArea = player->currentArea->areaExits[selected - 1]->areaToJoin;
|
||||
player->currentArea = getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
|
@ -813,17 +718,15 @@ int movePlayerToArea(playerInfo * player, char * requestedPath)
|
|||
}
|
||||
|
||||
// Otherwise search for the description:
|
||||
for (int index = 0; index < 16; index++)
|
||||
for (size_t index = 0; index < player->currentArea->pathList->itemCount; index++)
|
||||
{
|
||||
if(player->currentArea->areaExits[index] != NULL)
|
||||
if (strncmp(getFromList(player->currentArea->pathList, index)->path->pathName,
|
||||
requestedPath, 32) == 0)
|
||||
{
|
||||
if(strncmp(player->currentArea->areaExits[index]->pathName, requestedPath, 32) == 0)
|
||||
{
|
||||
printf("%s: %s\n", player->playerName, player->currentArea->areaExits[index]->pathName);
|
||||
player->currentArea = player->currentArea->areaExits[index]->areaToJoin;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("%s: %s\n", player->playerName, getFromList(player->currentArea->pathList, index)->path->pathName);
|
||||
player->currentArea = getFromList(player->currentArea->pathList, index)->path->areaToJoin;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,34 +1,18 @@
|
|||
// gamelogic.h: Header file contatning function prototypes and datastructures
|
||||
// for dealing with the game's logic.
|
||||
// gamelogic.h: Function prototypes and data-structures for dealing with game logic.
|
||||
// Barry Kane, 2022.
|
||||
#ifndef GAMELOGIC_H
|
||||
#define GAMELOGIC_H
|
||||
#include "queue.h"
|
||||
#include "areadata.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// =======================
|
||||
// -=[ Main Game Loop ]=-:
|
||||
// =======================
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// 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 ]=-:
|
||||
// ======================
|
||||
// An event for storing the information needed to evaluate a command:
|
||||
typedef struct commandEvent commandEvent;
|
||||
typedef struct commandEvent
|
||||
{
|
||||
|
@ -38,45 +22,44 @@ typedef struct commandEvent
|
|||
char * arguments;
|
||||
} commandEvent;
|
||||
|
||||
// A first-in first-out queue for message input from players:
|
||||
typedef struct commandQueue
|
||||
// A data-structure containing the needed parameters for the main game loop:
|
||||
typedef struct gameLogicParameters
|
||||
{
|
||||
bool lock;
|
||||
bool paused;
|
||||
int currentLength;
|
||||
commandEvent * back;
|
||||
commandEvent * front;
|
||||
} commandQueue;
|
||||
// Players:
|
||||
int * playerCount;
|
||||
playerInfo * connectedPlayers;
|
||||
|
||||
// Create a commandQueue:
|
||||
commandQueue * createCommandQueue(void);
|
||||
// Queues:
|
||||
queue * inputQueue;
|
||||
queue * outputQueue;
|
||||
|
||||
// Enqueue a command to a commandQueue:
|
||||
int queueCommand(commandQueue * queue, char * command, char * arguments,
|
||||
int commandLength, int argumentsLength , playerInfo * callingPlayer);
|
||||
// Lists:
|
||||
list * areaList;
|
||||
list * globalSkillList;
|
||||
} gameLogicParameters;
|
||||
|
||||
// Enqueue a messaged command to a commandQueue:
|
||||
int queueMessagedCommand(commandQueue * queue, inputMessage * messageToQueue);
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Dequeue the front commandEvent from a commandQueue:
|
||||
int dequeueCommand(commandQueue * queue);
|
||||
// Thread function which runs the main game loop, given the needed parameters:
|
||||
void * gameLogicHandler(void * parameters);
|
||||
|
||||
// Return the front commandEvent from a commandQueue:
|
||||
commandEvent * peekCommand(commandQueue * queue);
|
||||
// Enqueue a command that has been sent as a message from a user to a queue:
|
||||
void queueMessagedCommand(queue * queue, inputMessage * messageToQueue);
|
||||
|
||||
// Evaluate the next commandEvent:
|
||||
int evaluateNextCommand(gameLogicParameters * parameters, commandQueue * queue);
|
||||
// Evaluate the next commandEvent in a queue:
|
||||
int evaluateNextCommand(gameLogicParameters * parameters, queue * queue);
|
||||
|
||||
/* // Evaluate the given commandEvent: */
|
||||
/* int evaluateCommand(gameLogicParameters * parameters, commandEvent * command); */
|
||||
// Enqueue a command to a queue:
|
||||
void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength,
|
||||
playerInfo * callingPlayer);
|
||||
|
||||
// ============================
|
||||
// -=[ Gameplay Primitives ]=-:
|
||||
// ============================
|
||||
|
||||
// Player movement:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
||||
|
||||
// The possible outcomes of a check or challenge:
|
||||
typedef enum outcome
|
||||
{
|
||||
CRITICAL_FAILURE,
|
||||
|
@ -86,10 +69,13 @@ typedef enum outcome
|
|||
ERROR
|
||||
} outcome;
|
||||
|
||||
// Run a stat check:
|
||||
// Move a player along a path in their current area:
|
||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
||||
|
||||
// Run a stat check for the given player, returning an outcome:
|
||||
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck);
|
||||
|
||||
// Run a skill check:
|
||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, skillList * globalSkillList);
|
||||
// Run a skill check for the given player, returning an outcome:
|
||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
@ -14,11 +17,14 @@
|
|||
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
|
||||
{
|
||||
int returnValue = 0;
|
||||
// Continuously attempt to send the name field until it succeeds or fatally errors:
|
||||
do
|
||||
{
|
||||
returnValue = gnutls_record_send(receivingSession, messageToSend->senderName,
|
||||
sizeof(((userMessage*)0)->senderName));
|
||||
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
|
||||
|
||||
// Continuously attempt to send the message field until it succeeds or fatally errors:
|
||||
do
|
||||
{
|
||||
returnValue = gnutls_record_send(receivingSession, messageToSend->messageContent,
|
||||
|
@ -32,11 +38,14 @@ int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend)
|
|||
int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage)
|
||||
{
|
||||
int returnValue = 0;
|
||||
// Continuously attempt to receive the name field until it succeeds or fatally errors:
|
||||
do
|
||||
{
|
||||
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->senderName,
|
||||
sizeof(((userMessage*)0)->senderName));
|
||||
} while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED);
|
||||
|
||||
// Continuously attempt to receive the message field until it succeeds or fatally errors:
|
||||
do
|
||||
{
|
||||
returnValue = gnutls_record_recv(receiveFromSession, receiveToMessage->messageContent,
|
||||
|
@ -46,282 +55,95 @@ int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToM
|
|||
return returnValue;
|
||||
}
|
||||
|
||||
outputMessageQueue * createOutputMessageQueue(void)
|
||||
// Allocate and initialize an outputMessage targeted to a variable amount of players:
|
||||
outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientsCount)
|
||||
{
|
||||
outputMessageQueue * newQueue = malloc(sizeof(outputMessageQueue));
|
||||
newQueue->front = NULL;
|
||||
newQueue->back = NULL;
|
||||
newQueue->currentLength = 0;
|
||||
newQueue->lock = false;
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue)
|
||||
{
|
||||
// Copy the message into a new output message:
|
||||
// Allocate a new output message:
|
||||
outputMessage * newOutputMessage = malloc(sizeof(outputMessage));
|
||||
|
||||
// Allocate the internal userMessage to store the message:
|
||||
newOutputMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Copy the userMessage to the internal userMessage:
|
||||
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:
|
||||
newOutputMessage->targets[0] = NULL;
|
||||
|
||||
// Wait for the queue to unlock:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check that we're not overflowing the queue:
|
||||
if ((queue->currentLength + 1) > MAXQUEUELENGTH)
|
||||
{
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the queue is empty, set the first message as both the front and back of the queue:
|
||||
if(queue->front == NULL)
|
||||
{
|
||||
queue->front = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Allocate an array of playerInfo for the output message recepients:
|
||||
newOutputMessage->recipients = malloc(sizeof(playerInfo*) * recipientsCount);
|
||||
|
||||
// Copy in the appropriate data:
|
||||
memcpy(newOutputMessage->recipients, recipients, sizeof(playerInfo *) * recipientsCount);
|
||||
memcpy(newOutputMessage->content, messageToQueue, sizeof(userMessage));
|
||||
newOutputMessage->recipientsCount = recipientsCount;
|
||||
|
||||
// Return a pointer to the new outputMessage:
|
||||
return newOutputMessage;
|
||||
}
|
||||
|
||||
// A function for the output thread, which sends queued messages:
|
||||
void * outputThreadHandler(void * parameters)
|
||||
{
|
||||
outputThreadParameters * variables = parameters;
|
||||
queue * outputQueue = variables->outputQueue;
|
||||
gnutls_session_t * tlssessions = variables->tlssessions;
|
||||
playerInfo * connectedPlayers = variables->connectedPlayers;
|
||||
|
||||
// Copy the userMessage to the internal userMessage:
|
||||
strncpy(newOutputMessage->content->senderName, messageToQueue->senderName, 32);
|
||||
strncpy(newOutputMessage->content->messageContent, messageToQueue->messageContent, MAX);
|
||||
while (true)
|
||||
{
|
||||
// If there's nothing to do, put the thread to sleep:
|
||||
if (outputQueue->itemCount == 0)
|
||||
{
|
||||
pthread_cond_wait(&outputQueue->condition, &outputQueue->mutex);
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Run through the output queue and send all unsent messages:
|
||||
while (outputQueue->itemCount != 0)
|
||||
{
|
||||
queue->front = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
// Wait until the queue unlocks:
|
||||
while (outputQueue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
outputQueue->lock = true;
|
||||
|
||||
// Get a message off the queue:
|
||||
outputMessage * message = peekQueue(outputQueue)->data.outputMessage;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = newOutputMessage;
|
||||
queue->back = newOutputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dequeueOutputMessage(outputMessageQueue * queue)
|
||||
{
|
||||
// Wait for the queue to unlock:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check the list isn't empty:
|
||||
if(queue->front == NULL)
|
||||
{
|
||||
queue->lock = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If there is only one item in the queue:
|
||||
else if(queue->front == queue->back)
|
||||
{
|
||||
free(queue->front->content);
|
||||
free(queue->front);
|
||||
queue->front = NULL;
|
||||
queue->back = NULL;
|
||||
queue->currentLength--;
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove the front item:
|
||||
else
|
||||
{
|
||||
outputMessage * messageToDelete = queue->front;
|
||||
queue->front = queue->front->next;
|
||||
free(messageToDelete->content);
|
||||
free(messageToDelete);
|
||||
queue->currentLength--;
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inputMessageQueue * createInputMessageQueue(void)
|
||||
{
|
||||
inputMessageQueue * newQueue = malloc(sizeof(inputMessageQueue));
|
||||
newQueue->front = NULL;
|
||||
newQueue->back = NULL;
|
||||
newQueue->currentLength = 0;
|
||||
newQueue->lock = false;
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
int dequeueInputMessage(inputMessageQueue * queue)
|
||||
{
|
||||
// Wait for the queue to unlock:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check the list isn't empty:
|
||||
if(queue->front == NULL)
|
||||
{
|
||||
queue->lock = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If there is only one item in the queue:
|
||||
else if(queue->front == queue->back)
|
||||
{
|
||||
free(queue->front->content);
|
||||
free(queue->front);
|
||||
queue->front = NULL;
|
||||
queue->back = NULL;
|
||||
queue->currentLength--;
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove the front item:
|
||||
else
|
||||
{
|
||||
inputMessage * messageToDelete = queue->front;
|
||||
queue->front = queue->front->next;
|
||||
free(messageToDelete->content);
|
||||
free(messageToDelete);
|
||||
queue->currentLength--;
|
||||
queue->lock = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int queueInputMessage(inputMessageQueue * queue, userMessage messageToQueue, playerInfo * sendingPlayer)
|
||||
{
|
||||
// Copy the message into a new input message:
|
||||
inputMessage * inputMessage = malloc(sizeof(inputMessage));
|
||||
|
||||
// Allocate the internal userMessage to store the message:
|
||||
inputMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Copy the userMessage to the internal userMessage:
|
||||
strncpy(inputMessage->content->senderName, messageToQueue.senderName, 32);
|
||||
strncpy(inputMessage->content->messageContent, messageToQueue.messageContent, MAX);
|
||||
|
||||
// We have no targets, NULL sends to all players in an area:
|
||||
inputMessage->sender = sendingPlayer;
|
||||
|
||||
// Wait for the queue to unlock:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check that we're not overflowing the queue:
|
||||
if ((queue->currentLength + 1) > MAXQUEUELENGTH)
|
||||
{
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the queue is empty, set the first message as both the front and back of the queue:
|
||||
if(queue->front == NULL)
|
||||
{
|
||||
queue->front = inputMessage;
|
||||
queue->back = inputMessage;
|
||||
queue->currentLength++;
|
||||
outputQueue->lock = false;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
// If the first target is set to NULL, it's intended for all connected:
|
||||
if (message->recipientsCount == 0)
|
||||
{
|
||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
messageSend(tlssessions[index], message->content);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
// Otherwise, send it only to the targeted players:
|
||||
else
|
||||
{
|
||||
int sentCount = 0;
|
||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
if (sentCount == message->recipientsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (&connectedPlayers[index] == message->recipients[sentCount])
|
||||
{
|
||||
sentCount++;
|
||||
messageSend(tlssessions[index], message->content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the output message from the queue:
|
||||
popQueue(outputQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = inputMessage;
|
||||
queue->back = inputMessage;
|
||||
queue->currentLength++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanatize user input to ensure it's okay to process:
|
||||
void userInputSanatize(char * inputString, int length)
|
||||
{
|
||||
for(int index = 0; index <= length; index++)
|
||||
for (int index = 0; index <= length; index++)
|
||||
{
|
||||
// If it's not a printable character, it has no business being here:
|
||||
if(!isprint(inputString[index]))
|
||||
{
|
||||
inputString[index] = '\n';
|
||||
|
@ -329,29 +151,23 @@ void userInputSanatize(char * inputString, int length)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it's null-terminated:
|
||||
inputString[length - 1] = '\0';
|
||||
}
|
||||
|
||||
// Sanatize user names so they display correctly:
|
||||
void userNameSanatize(char * inputString, int length)
|
||||
{
|
||||
for(int index = 0; index <= length; index++)
|
||||
{
|
||||
// If it's not a printable character, it has no business being here:
|
||||
if(!isprint(inputString[index]))
|
||||
{
|
||||
inputString[index] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Make sure it's null-terminated:
|
||||
inputString[length - 1] = '\0';
|
||||
}
|
||||
|
||||
// Return the front inputMessage from an inputMessageQueue:
|
||||
inputMessage * peekInputMessage(inputMessageQueue * queue)
|
||||
{
|
||||
return queue->front;
|
||||
}
|
||||
|
||||
outputMessage * peekOutputMessage(outputMessageQueue * queue)
|
||||
{
|
||||
return queue->front;
|
||||
}
|
||||
|
|
|
@ -4,22 +4,54 @@
|
|||
#ifndef INPUTOUTPUT_H
|
||||
#define INPUTOUTPUT_H
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include <stdbool.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
// A message datastructure containing a user/character name and the content:
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct queue queue;
|
||||
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// Contains a character/player name and the content of a message:
|
||||
typedef struct userMessage
|
||||
{
|
||||
char senderName[32];
|
||||
char messageContent[MAX];
|
||||
} userMessage;
|
||||
|
||||
// ==================
|
||||
// -=[Message I/O]=-:
|
||||
// ==================
|
||||
// Contains a message sent to the server and a pointer to the playerInfo of the connection which sent it:
|
||||
typedef struct inputMessage
|
||||
{
|
||||
playerInfo * sender;
|
||||
userMessage * content;
|
||||
} inputMessage;
|
||||
|
||||
// Contains a message to be sent, the amount of recipients, and pointers to their playerInfo:
|
||||
typedef struct outputMessage
|
||||
{
|
||||
int recipientsCount;
|
||||
userMessage * content;
|
||||
playerInfo ** recipients;
|
||||
} outputMessage;
|
||||
|
||||
// Contains pointers to the necessary information to be shared outputThreadHandler function:
|
||||
typedef struct outputThreadParameters
|
||||
{
|
||||
queue * outputQueue;
|
||||
gnutls_session_t * tlssessions;
|
||||
playerInfo * connectedPlayers;
|
||||
} outputThreadParameters;
|
||||
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
|
||||
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend);
|
||||
|
@ -27,80 +59,16 @@ int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend);
|
|||
// Receives a message from a given TLS session, wraps the calls to gnutls_read:
|
||||
int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage);
|
||||
|
||||
// ===================
|
||||
// -=[Output Queue]=-:
|
||||
// ===================
|
||||
typedef struct outputMessage outputMessage;
|
||||
typedef struct outputMessage
|
||||
{
|
||||
outputMessage * next;
|
||||
playerInfo * targets[PLAYERCOUNT];
|
||||
userMessage * content;
|
||||
} outputMessage;
|
||||
// Create a targetedOutput message to be delivered to the players pointed to in recipients:
|
||||
outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientCount);
|
||||
|
||||
// A first-in first-out queue for message output to players:
|
||||
typedef struct outputMessageQueue
|
||||
{
|
||||
bool lock;
|
||||
int currentLength;
|
||||
outputMessage * back;
|
||||
outputMessage * front;
|
||||
} outputMessageQueue;
|
||||
// A function for the output thread, which sends queued messages:
|
||||
void * outputThreadHandler(void * parameters);
|
||||
|
||||
// Creates and initializes a outputMessageQueue:
|
||||
outputMessageQueue * createOutputMessageQueue(void);
|
||||
|
||||
// Enqueue a userMessage to an outputMessageQueue:
|
||||
int queueOutputMessage(outputMessageQueue * queue, userMessage messageToQueue);
|
||||
int queueTargetedOutputMessage(outputMessageQueue * queue, userMessage * messageToQueue,
|
||||
playerInfo ** targets, int numberOfTargets);
|
||||
|
||||
// Dequeue the front outputMessage from an outputMessageQueue:
|
||||
int dequeueOutputMessage(outputMessageQueue * queue);
|
||||
|
||||
// Return the front outputMessage from an outputMessageQueue:
|
||||
outputMessage * peekOutputMessage(outputMessageQueue * queue);
|
||||
|
||||
// ==================
|
||||
// -=[Input Queue]=-:
|
||||
// ==================
|
||||
typedef struct inputMessage inputMessage;
|
||||
typedef struct inputMessage
|
||||
{
|
||||
inputMessage * next;
|
||||
playerInfo * sender;
|
||||
userMessage * content;
|
||||
} inputMessage;
|
||||
|
||||
// A first-in first-out queue for message input from players:
|
||||
typedef struct inputMessageQueue
|
||||
{
|
||||
bool lock;
|
||||
int currentLength;
|
||||
inputMessage * back;
|
||||
inputMessage * front;
|
||||
} inputMessageQueue;
|
||||
|
||||
// Create a inputMessageQueue:
|
||||
inputMessageQueue * createInputMessageQueue(void);
|
||||
|
||||
// Enqueue a userMessage to an inputMessageQueue:
|
||||
int queueInputMessage(inputMessageQueue * queue, userMessage messageToQueue, playerInfo * sendingPlayer);
|
||||
|
||||
// Dequeue the front inputMessage from an inputMessageQueue:
|
||||
int dequeueInputMessage(inputMessageQueue * queue);
|
||||
|
||||
// Return the front inputMessage from an inputMessageQueue:
|
||||
inputMessage * peekInputMessage(inputMessageQueue * queue);
|
||||
|
||||
// =======================
|
||||
// -=[Input Sanitation]=-:
|
||||
// =======================
|
||||
|
||||
// Sanatize user input to ensure it's okay to send to the server:
|
||||
// Sanatize user input to ensure it's okay to process:
|
||||
void userInputSanatize(char * inputString, int length);
|
||||
|
||||
// Sanatize user names so they display correctly;
|
||||
// Sanatize user names so they display correctly:
|
||||
void userNameSanatize(char * inputString, int length);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,479 @@
|
|||
// linkedlist.h: Function definitions for the list type for SilverMUD.
|
||||
// Barry Kane, 2022.
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "playerdata.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// Deallocate a given list node, including it's data:
|
||||
static inline void deallocateListNode(listNode * node, listDataType type)
|
||||
{
|
||||
// Delete the node:
|
||||
switch (type)
|
||||
{
|
||||
case PLAYER:
|
||||
{
|
||||
deallocatePlayer(node->data.player);
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
destroyList(&(node->data.area->pathList));
|
||||
free(node->data.area);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
case PATH:
|
||||
{
|
||||
free(node->data.path);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
free(node->data.skill);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocates and instantiates a list of the specified type:
|
||||
list * createList(listDataType type)
|
||||
{
|
||||
// Allocate and clear the memory for the list:
|
||||
list * newList = calloc(sizeof(list), 1);
|
||||
|
||||
// Set the appropriate values in the new list:
|
||||
newList->type = type;
|
||||
newList->itemCount = 0;
|
||||
newList->head = NULL;
|
||||
newList->tail = NULL;
|
||||
|
||||
// Return the new list:
|
||||
return newList;
|
||||
}
|
||||
|
||||
// Deallocates a list and all of it's members:
|
||||
int destroyList(list ** list)
|
||||
{
|
||||
// Check if the list is empty:
|
||||
if ((*list)->itemCount == 0)
|
||||
{
|
||||
free(*list);
|
||||
list = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((*list)->itemCount > 0)
|
||||
{
|
||||
removeFromList((*list), (*list)->type, (*list)->itemCount - 1);
|
||||
}
|
||||
free(*list);
|
||||
*list = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the data at a given index in a list:
|
||||
listData * getFromList(list * list, size_t listIndex)
|
||||
{
|
||||
// Check that we were given a valid index:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
perror("Invalid index specified.\n");
|
||||
return NULL;
|
||||
}
|
||||
// Return the head if index is 0:
|
||||
else if (listIndex == 0)
|
||||
{
|
||||
return &(list->head->data);
|
||||
}
|
||||
// Loop through the entries in the list until we get to the right one:
|
||||
else
|
||||
{
|
||||
listNode * currentNode = list->head;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
return &(currentNode->data);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the node at a given index in a list:
|
||||
listNode * getNodeFromList(list * list, size_t listIndex)
|
||||
{
|
||||
// Check that we were given a valid index:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
perror("Invalid index specified.\n");
|
||||
return NULL;
|
||||
}
|
||||
// Return the head if index is 0:
|
||||
else if (listIndex == 0)
|
||||
{
|
||||
return list->head;
|
||||
}
|
||||
// Loop through the entries in the list until we get to the right one:
|
||||
else
|
||||
{
|
||||
if ((list->itemCount / 2) < listIndex)
|
||||
{
|
||||
listNode * currentNode = list->tail;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->previous;
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
listNode * currentNode = list->head;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the given data to the end of a list:
|
||||
listNode * addToList(list * list, void * data, listDataType type)
|
||||
{
|
||||
// Check the type:
|
||||
if (type != list->type)
|
||||
{
|
||||
fprintf(stderr, "Not the correct type for this list.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If this is the first item in the list:
|
||||
if (list->itemCount == 0)
|
||||
{
|
||||
// Allocate the new node for the list:
|
||||
list->head = calloc(1, sizeof(listNode));
|
||||
|
||||
// Set the appropriate pointers for the list:
|
||||
list->head->next = NULL;
|
||||
list->head->previous = NULL;
|
||||
list->tail = list->head;
|
||||
|
||||
// Add the data to the new node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
list->head->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
list->head->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
list->head->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
list->head->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate the new node at the end of the list:
|
||||
list->tail->next = calloc(1, sizeof(listNode));
|
||||
|
||||
// Add the data to the new node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
list->tail->next->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
list->tail->next->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
list->tail->next->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
list->tail->next->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the appropriate pointers in the new node:
|
||||
list->tail->next->previous = list->tail;
|
||||
|
||||
// Set the list's tail to the new tail:
|
||||
list->tail = list->tail->next;
|
||||
}
|
||||
// Increase the count of items in the list:
|
||||
list->itemCount++;
|
||||
|
||||
// Return the new item in the list:
|
||||
return list->tail;
|
||||
}
|
||||
|
||||
// Insert the given data at a given index in the list:
|
||||
listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex)
|
||||
{
|
||||
// Check that the types are correct:
|
||||
if (list->type != type)
|
||||
{
|
||||
fprintf(stderr, "Types do not match.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Handle the special case of adding to the end of the list:
|
||||
if (listIndex == (list->itemCount - 1))
|
||||
{
|
||||
return addToList(list, data, type);
|
||||
}
|
||||
|
||||
// Handle the special case of adding to the beginning of the list:
|
||||
if (listIndex == 0)
|
||||
{
|
||||
// Create the new node:
|
||||
listNode * newNode = calloc(1, sizeof(listNode));
|
||||
|
||||
// Add the data to the node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
newNode->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
newNode->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
newNode->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
newNode->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Place it in the list:
|
||||
newNode->next = list->head;
|
||||
newNode->previous = NULL;
|
||||
list->head->previous = newNode;
|
||||
list->head = newNode;
|
||||
list->itemCount++;
|
||||
|
||||
// Return the node:
|
||||
return newNode;
|
||||
}
|
||||
|
||||
// Check that the index is valid:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
fprintf(stderr, "Index is invalid for the list.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the current node at the index:
|
||||
listNode * currentNode = list->head;
|
||||
for(size_t index = 0; index < listIndex; index++)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
|
||||
// Get the node before the current node:
|
||||
listNode * previousNode = currentNode->previous;
|
||||
|
||||
// Create the new node:
|
||||
previousNode->next = calloc(1, sizeof(listNode));
|
||||
currentNode->previous = previousNode->next;
|
||||
previousNode->next->next = currentNode;
|
||||
previousNode->next->previous = previousNode;
|
||||
|
||||
// Add the data to the node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
previousNode->next->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
previousNode->next->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
previousNode->next->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
previousNode->next->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
list->itemCount++;
|
||||
return previousNode->next;
|
||||
}
|
||||
|
||||
// Delete the given data from a list:
|
||||
bool deleteFromList(list * list, void * data, listDataType type)
|
||||
{
|
||||
size_t index = 0;
|
||||
if (getIndexFromList(list, data, type, &index) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
removeFromList(list, type, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the data from a given point in a list:
|
||||
int removeFromList(list * list, listDataType type, size_t listIndex)
|
||||
{
|
||||
// Check that we're removing the correct type:
|
||||
if (list->type != type)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the list index is valid:
|
||||
if (listIndex > list->itemCount - 1)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
// The first node in the list:
|
||||
if (listIndex == 0)
|
||||
{
|
||||
// Get the current head and move the list's head on:
|
||||
listNode * oldHead = list->head;
|
||||
list->head = list->head->next;
|
||||
|
||||
// If we haven't removed the last item, set the previous pointer
|
||||
// in the new head to null.
|
||||
if (list->head != NULL)
|
||||
{
|
||||
list->head->previous = NULL;
|
||||
}
|
||||
|
||||
// Delete the node:
|
||||
deallocateListNode(oldHead, type);
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
// The last node in the list:
|
||||
else if (listIndex == (list->itemCount - 1))
|
||||
{
|
||||
// Move the tail up by one:
|
||||
list->tail = list->tail->previous;
|
||||
|
||||
// Deallocate the former tail:
|
||||
deallocateListNode(list->tail->next, type);
|
||||
|
||||
// Set the appropriate pointer:
|
||||
list->tail->next = NULL;
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
// A node in the middle of the list:
|
||||
else
|
||||
{
|
||||
// Get the needed node as a pointer:
|
||||
listNode * nodeToDelete = getNodeFromList(list, listIndex);
|
||||
|
||||
// Set the appropriate pointers for the surrounding nodes:
|
||||
nodeToDelete->previous->next = nodeToDelete->next;
|
||||
nodeToDelete->next->previous = nodeToDelete->previous;
|
||||
|
||||
// Deallocate the node:
|
||||
deallocateListNode(nodeToDelete, type);
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the index of a given piece of data in a list:
|
||||
bool getIndexFromList(list * list, void * data, listDataType type, size_t * index)
|
||||
{
|
||||
// Check the list types are the same:
|
||||
if (list->type == type)
|
||||
{
|
||||
fprintf(stderr, "List types do not match.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(*index = 0; *index < list->itemCount; *index += 1)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AREA:
|
||||
{
|
||||
if (getFromList(list, *index)->area == data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
if (getFromList(list, *index)->player == data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PATH:
|
||||
{
|
||||
if (getFromList(list, *index)->path == data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
if (getFromList(list, *index)->skill == data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// linkedlist.h: Defines the linked list datatype for SilverMUD.
|
||||
// Barry Kane, 2022.
|
||||
#ifndef LINKEDLIST_H
|
||||
#define LINKEDLIST_H
|
||||
#include "areadata.h"
|
||||
#include "playerdata.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct playerPath playerPath;
|
||||
typedef struct playerArea playerArea;
|
||||
typedef struct playerInfo playerInfo;
|
||||
typedef struct playerSkill playerSkill;
|
||||
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// An enum of the possible data types that can be stored in a list:
|
||||
typedef enum listDataType
|
||||
{
|
||||
PATH,
|
||||
AREA,
|
||||
PLAYER,
|
||||
SKILL
|
||||
} listDataType;
|
||||
|
||||
// A union containing a pointers to all data types that can be stored in a list:
|
||||
typedef union listData
|
||||
{
|
||||
playerPath * path;
|
||||
playerArea * area;
|
||||
playerInfo * player;
|
||||
playerSkill * skill;
|
||||
} listData;
|
||||
|
||||
// A doubly linked node for the linked list type:
|
||||
typedef struct listNode listNode;
|
||||
typedef struct listNode
|
||||
{
|
||||
listData data;
|
||||
listNode * next;
|
||||
listNode * previous;
|
||||
} listNode;
|
||||
|
||||
// A header structure for the list containing the length, head, tail, and type of the list.
|
||||
typedef struct list
|
||||
{
|
||||
listDataType type;
|
||||
size_t itemCount;
|
||||
listNode * head;
|
||||
listNode * tail;
|
||||
} list;
|
||||
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Allocates and instantiates a list of the specified type:
|
||||
list * createList(listDataType type);
|
||||
|
||||
// Deallocates a list and all of it's members:
|
||||
int destroyList(list ** list);
|
||||
|
||||
// Returns the data at a given index in a list:
|
||||
listData * getFromList(list * list, size_t listIndex);
|
||||
|
||||
// Returns the node at a given index in a list:
|
||||
listNode * getNodeFromList(list * list, size_t listIndex);
|
||||
|
||||
// Adds the given data to the end of a list:
|
||||
listNode * addToList(list * list, void * data, listDataType type);
|
||||
|
||||
// Insert the given data at a given index in the list:
|
||||
listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex);
|
||||
|
||||
// Delete the given data from a list:
|
||||
bool deleteFromList(list * list, void * data, listDataType type);
|
||||
|
||||
// Delete the data from a given point in a list:
|
||||
int removeFromList(list * list, listDataType type, size_t listIndex);
|
||||
|
||||
// Get the index of a given piece of data in a list:
|
||||
bool getIndexFromList(list * list, void * data, listDataType type, size_t * index);
|
||||
|
||||
#endif
|
178
src/playerdata.c
178
src/playerdata.c
|
@ -9,12 +9,12 @@
|
|||
#include "playerdata.h"
|
||||
|
||||
// Create a new skill and add it to the global skill list:
|
||||
int createSkill(skillList * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill)
|
||||
listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill)
|
||||
{
|
||||
if(skillNameLength >= 32)
|
||||
if (skillNameLength >= 32)
|
||||
{
|
||||
fprintf(stderr, "Skill name is too long. Please shorten the name and try again.\n");
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
playerSkill * newSkill = malloc(sizeof(playerSkill));
|
||||
|
||||
|
@ -26,109 +26,56 @@ int createSkill(skillList * globalSkillList, char * skillName, int skillNameLeng
|
|||
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;
|
||||
return(addToList(globalSkillList, newSkill, SKILL));
|
||||
}
|
||||
|
||||
// Take a skill and add it to the player's skill list:
|
||||
int takeSkill(skillList * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer)
|
||||
int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer)
|
||||
{
|
||||
|
||||
skillNode * currentNode = globalSkillList->head;
|
||||
while(strncmp(skillName, currentNode->skill->skillName, 32) != 0)
|
||||
// Check if the skill exists in the game:
|
||||
size_t globalIndex = 0;
|
||||
bool skillExists = false;
|
||||
while (globalIndex < globalSkillList->itemCount)
|
||||
{
|
||||
if(currentNode->next == NULL)
|
||||
if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) == 0)
|
||||
{
|
||||
fprintf(stderr, "Skill doesn't exist in skill list.\n");
|
||||
return -1;
|
||||
skillExists = true;
|
||||
break;
|
||||
}
|
||||
currentNode = currentNode->next;
|
||||
globalIndex++;
|
||||
}
|
||||
|
||||
bool playerHasSkill = false;
|
||||
skillNode * currentPlayerNode = targetPlayer->skills->head;
|
||||
while(currentPlayerNode != NULL)
|
||||
if (!skillExists)
|
||||
{
|
||||
if(strncmp(skillName, currentPlayerNode->skill->skillName, skillNameLength) == 0)
|
||||
fprintf(stderr, "Skill doesn't exist in skill list.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if the player has the skill:
|
||||
size_t playerIndex = 0;
|
||||
bool playerHasSkill = false;
|
||||
while (playerIndex < targetPlayer->skills->itemCount)
|
||||
{
|
||||
if (strncmp(skillName, getFromList(targetPlayer->skills, playerIndex)->skill->skillName, skillNameLength) == 0)
|
||||
{
|
||||
playerHasSkill = true;
|
||||
break;
|
||||
}
|
||||
currentPlayerNode = currentPlayerNode->next;
|
||||
playerIndex++;
|
||||
}
|
||||
if(playerHasSkill)
|
||||
if (playerHasSkill)
|
||||
{
|
||||
currentPlayerNode->skill->skillPoints++;
|
||||
getFromList(targetPlayer->skills, playerIndex)->skill->skillPoints++;
|
||||
}
|
||||
|
||||
// Copy the skill into the player's skill list:
|
||||
else
|
||||
{
|
||||
addSkillNode(targetPlayer->skills, currentNode->skill);
|
||||
currentPlayerNode = targetPlayer->skills->head;
|
||||
while(currentPlayerNode->next != NULL)
|
||||
{
|
||||
currentPlayerNode = currentPlayerNode->next;
|
||||
}
|
||||
currentPlayerNode->skill->skillPoints = 1;
|
||||
playerSkill * newSkill = calloc(1, sizeof(playerSkill));
|
||||
strncpy(newSkill->skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, 32);
|
||||
printf("%s ", newSkill->skillName);
|
||||
newSkill->skillPoints = 1;
|
||||
addToList(targetPlayer->skills, newSkill, SKILL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -138,7 +85,7 @@ int takeSkill(skillList * globalSkillList, char * skillName, int skillNameLength
|
|||
coreStat getCoreStatFromString(char * inputString, int stringLength)
|
||||
{
|
||||
// Check we've got a long enough string to fit a stat:
|
||||
if(stringLength < 4)
|
||||
if (stringLength < 4)
|
||||
{
|
||||
return INVALID;
|
||||
}
|
||||
|
@ -152,11 +99,11 @@ coreStat getCoreStatFromString(char * inputString, int stringLength)
|
|||
|
||||
// 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 < 9)
|
||||
{
|
||||
if(stringLength <= 4)
|
||||
if (stringLength <= 4)
|
||||
{
|
||||
if(strncmp(string, "wits", 4) == 0)
|
||||
if (strncmp(string, "wits", 4) == 0)
|
||||
{
|
||||
free(string);
|
||||
return WITS;
|
||||
|
@ -168,19 +115,19 @@ coreStat getCoreStatFromString(char * inputString, int stringLength)
|
|||
}
|
||||
}
|
||||
// Hopefully one of the seven letter long ones:
|
||||
else if(stringLength <= 7)
|
||||
else if (stringLength <= 7)
|
||||
{
|
||||
if(strncmp(string, "strength", 7) == 0)
|
||||
if (strncmp(string, "strength", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return STRENGTH;
|
||||
}
|
||||
else if(strncmp(string, "dexerity", 7) == 0)
|
||||
else if (strncmp(string, "dexerity", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return DEXERITY;
|
||||
}
|
||||
if(strncmp(string, "wits", 4) == 0)
|
||||
if (strncmp(string, "wits", 4) == 0)
|
||||
{
|
||||
free(string);
|
||||
return WITS;
|
||||
|
@ -194,27 +141,27 @@ coreStat getCoreStatFromString(char * inputString, int stringLength)
|
|||
// Hopefully one of the 8 letter long stats:
|
||||
else
|
||||
{
|
||||
if(strncmp(string, "intellect", 8) == 0)
|
||||
if (strncmp(string, "intellect", 8) == 0)
|
||||
{
|
||||
free(string);
|
||||
return INTELLECT;
|
||||
}
|
||||
else if(strncmp(string, "endurance", 8) == 0)
|
||||
else if (strncmp(string, "endurance", 8) == 0)
|
||||
{
|
||||
free(string);
|
||||
return ENDURANCE;
|
||||
}
|
||||
else if(strncmp(string, "strength", 7) == 0)
|
||||
else if (strncmp(string, "strength", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return STRENGTH;
|
||||
}
|
||||
else if(strncmp(string, "dexerity", 7) == 0)
|
||||
else if (strncmp(string, "dexerity", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return DEXERITY;
|
||||
}
|
||||
if(strncmp(string, "wits", 4) == 0)
|
||||
if (strncmp(string, "wits", 4) == 0)
|
||||
{
|
||||
free(string);
|
||||
return WITS;
|
||||
|
@ -229,27 +176,27 @@ coreStat getCoreStatFromString(char * inputString, int stringLength)
|
|||
// Worst case, it's definitely a dirty string, compare them all:
|
||||
else
|
||||
{
|
||||
if(strncmp(string, "wits", 4) == 0)
|
||||
if (strncmp(string, "wits", 4) == 0)
|
||||
{
|
||||
free(string);
|
||||
return WITS;
|
||||
}
|
||||
else if(strncmp(string, "intellect", 8) == 0)
|
||||
else if (strncmp(string, "intellect", 8) == 0)
|
||||
{
|
||||
free(string);
|
||||
return INTELLECT;
|
||||
}
|
||||
else if(strncmp(string, "strength", 7) == 0)
|
||||
else if (strncmp(string, "strength", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return STRENGTH;
|
||||
}
|
||||
else if(strncmp(string, "endurance", 8) == 0)
|
||||
else if (strncmp(string, "endurance", 8) == 0)
|
||||
{
|
||||
free(string);
|
||||
return ENDURANCE;
|
||||
}
|
||||
else if(strncmp(string, "dexerity", 7) == 0)
|
||||
else if (strncmp(string, "dexerity", 7) == 0)
|
||||
{
|
||||
free(string);
|
||||
return DEXERITY;
|
||||
|
@ -262,28 +209,11 @@ coreStat getCoreStatFromString(char * inputString, int stringLength)
|
|||
}
|
||||
}
|
||||
|
||||
// Deallocate a player's information including the skill lists and stats:
|
||||
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]);
|
||||
}
|
||||
}
|
||||
destroyList(&(playerToDeallocate->skills));
|
||||
|
||||
// Deallocate the stat block:
|
||||
free(playerToDeallocate->stats);
|
||||
|
|
|
@ -6,7 +6,15 @@
|
|||
#include <stdbool.h>
|
||||
#include "areadata.h"
|
||||
#include "constants.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct playerArea playerArea;
|
||||
typedef struct playerPath playerPath;
|
||||
typedef struct listNode listNode;
|
||||
typedef struct list list;
|
||||
|
||||
// The basic information that needs to be stored for a player or creature's stats:
|
||||
typedef struct statBlock
|
||||
{
|
||||
// Levelling:
|
||||
|
@ -29,6 +37,7 @@ typedef struct statBlock
|
|||
int skillPoints;
|
||||
} statBlock;
|
||||
|
||||
// Information about a skill, including skill levels and modifiers for the player:
|
||||
typedef struct playerSkill
|
||||
{
|
||||
char skillName[32];
|
||||
|
@ -37,27 +46,16 @@ typedef struct playerSkill
|
|||
bool trainedSkill;
|
||||
} playerSkill;
|
||||
|
||||
typedef struct skillNode skillNode;
|
||||
struct skillNode
|
||||
{
|
||||
playerSkill * skill;
|
||||
skillNode * next;
|
||||
};
|
||||
|
||||
typedef struct skillList
|
||||
{
|
||||
skillNode * head;
|
||||
int skillCount;
|
||||
} skillList;
|
||||
\
|
||||
// Information about a single player's character:
|
||||
typedef struct playerInfo
|
||||
{
|
||||
char playerName[32];
|
||||
playerArea * currentArea;
|
||||
statBlock * stats;
|
||||
skillList * skills;
|
||||
list * skills;
|
||||
} playerInfo;
|
||||
|
||||
// An enum of the main stats of the game:
|
||||
typedef enum coreStat
|
||||
{
|
||||
WITS,
|
||||
|
@ -69,23 +67,16 @@ typedef enum coreStat
|
|||
} coreStat;
|
||||
|
||||
// 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);
|
||||
listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill);
|
||||
|
||||
// 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);
|
||||
int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer);
|
||||
int takeSkillbyID(list * globalSkillList, int skillID, playerInfo * targetPlayer);
|
||||
|
||||
// Take a string containing a core stat name and return the core stat:
|
||||
coreStat getCoreStatFromString(char * string, int stringLength);
|
||||
|
||||
// Deallocate a player:
|
||||
// Deallocate a player's information including the skill lists and stats:
|
||||
int deallocatePlayer(playerInfo * playerToDeallocate);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
// queue.c: Implements the queue data type and associated functions for SilverMUD.
|
||||
// Barry Kane, 2022
|
||||
#include <pthread.h>
|
||||
#include "queue.h"
|
||||
|
||||
// Allocates and instantiates a queue:
|
||||
queue * createQueue(void)
|
||||
{
|
||||
// Allocate the memory for the queue:
|
||||
queue * newQueue = malloc(sizeof(queue));
|
||||
|
||||
// Instantiate the variables in the data-structure:
|
||||
newQueue->itemCount = 0;
|
||||
newQueue->front = NULL;
|
||||
newQueue->back = NULL;
|
||||
|
||||
// Create the threading constructs:
|
||||
newQueue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
newQueue->condition = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
|
||||
// Return the pointer to the new queue:
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
// Destroys a queue and all of it's members:
|
||||
void destroyQueue(queue ** queue)
|
||||
{
|
||||
// Pop everything off of the queue:
|
||||
while ((*queue)->itemCount > 0)
|
||||
{
|
||||
popQueue(*queue);
|
||||
}
|
||||
|
||||
// Deallocate the queue:
|
||||
free(*queue);
|
||||
|
||||
// Point the queue pointer to NULL;
|
||||
*queue = NULL;
|
||||
}
|
||||
|
||||
// Returns the data at the front of the given queue:
|
||||
queueNode * peekQueue(queue * queue)
|
||||
{
|
||||
return queue->front;
|
||||
}
|
||||
|
||||
// Removes the current data from the front of the queue:
|
||||
void popQueue(queue * queue)
|
||||
{
|
||||
// Check if the queue is locked, and wait:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check there is actually anything to remove:
|
||||
if (queue->itemCount == 0)
|
||||
{
|
||||
queue->lock = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the special case of being the last item in the queue:
|
||||
else if (queue->itemCount == 1)
|
||||
{
|
||||
// Deallocate the correct data:
|
||||
switch (queue->front->type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
free(queue->front->data.command->command);
|
||||
free(queue->front->data.command->arguments);
|
||||
free(queue->front->data.command);
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.inputMessage->content);
|
||||
free(queue->front->data.inputMessage);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.outputMessage->content);
|
||||
free(queue->front->data.outputMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deallocate the node:
|
||||
free(queue->front);
|
||||
|
||||
// Set the correct variables for the queue:
|
||||
queue->front = NULL;
|
||||
queue->back = NULL;
|
||||
queue->itemCount = 0;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the current front of the queue:
|
||||
else
|
||||
{
|
||||
// Deallocate the correct data:
|
||||
switch (queue->front->type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
break;
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
free(queue->front->data.command->command);
|
||||
free(queue->front->data.command->arguments);
|
||||
free(queue->front->data.command);
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.inputMessage->content);
|
||||
free(queue->front->data.inputMessage);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.outputMessage->content);
|
||||
free(queue->front->data.outputMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save a pointer to the current node so we don't leak it:
|
||||
queueNode * nodeToDelete = queue->front;
|
||||
|
||||
// Advance the queue:
|
||||
queue->front = queue->front->next;
|
||||
queue->itemCount--;
|
||||
|
||||
// Deallocate the old node:
|
||||
free(nodeToDelete);
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds data to the back of a queue:
|
||||
void pushQueue(queue * queue, void * data, queueDataType type)
|
||||
{
|
||||
// Check if the queue is locked:
|
||||
while (queue->lock);
|
||||
|
||||
// Create a new node:
|
||||
queueNode * newNode = malloc(sizeof(queueNode));
|
||||
newNode->type = type;
|
||||
// Copy the data into the correct slot for the type:
|
||||
switch (type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
break;
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
newNode->data.command = data;
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
newNode->data.inputMessage = data;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
newNode->data.outputMessage = data;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if the queue is locked:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Set the correct pointers:
|
||||
newNode->next = NULL;
|
||||
|
||||
if (queue->itemCount == 0)
|
||||
{
|
||||
queue->front = newNode;
|
||||
queue->back = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = newNode;
|
||||
queue->back = newNode;
|
||||
}
|
||||
|
||||
// Increase the queue item count:
|
||||
queue->itemCount++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
// Flag that the queue was modified:
|
||||
pthread_cond_broadcast(&queue->condition);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// queue.h: Defines the queue data type and associated function prototypes for SilverMUD.
|
||||
// Barry Kane, 2022
|
||||
#ifndef QUEUE_H
|
||||
#define QUEUE_H
|
||||
#include "gamelogic.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct queue queue;
|
||||
|
||||
// An enum which is used to state what type of data is being stored in a queueNode:
|
||||
typedef enum queueDataType
|
||||
{
|
||||
EVENT,
|
||||
COMMAND,
|
||||
INPUT_MESSAGE,
|
||||
OUTPUT_MESSAGE
|
||||
} queueDataType;
|
||||
|
||||
// A union for storing a pointer to all the types of data a queueNode may hold:
|
||||
typedef union queueData
|
||||
{
|
||||
outputMessage * outputMessage;
|
||||
inputMessage * inputMessage;
|
||||
commandEvent * command;
|
||||
} queueData;
|
||||
|
||||
// A queue node, a singly-linked list node for our queue:
|
||||
typedef struct queueNode queueNode;
|
||||
typedef struct queueNode
|
||||
{
|
||||
queueDataType type;
|
||||
queueData data;
|
||||
queueNode * next;
|
||||
} queueNode;
|
||||
|
||||
// A queue, with pointers to the head and tail of the linked list, and data for multi-threading, locking, and an item count.
|
||||
typedef struct queue
|
||||
{
|
||||
volatile bool lock;
|
||||
size_t itemCount;
|
||||
queueNode * front;
|
||||
queueNode * back;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t condition;
|
||||
} queue;
|
||||
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Allocates and instantiates a queue:
|
||||
queue * createQueue(void);
|
||||
|
||||
// Destroys a queue and all of it's members:
|
||||
void destroyQueue(queue ** queue);
|
||||
|
||||
// Returns the node at the front of the given queue:
|
||||
queueNode * peekQueue(queue * queue);
|
||||
|
||||
// Removes the current node from the front of the queue:
|
||||
void popQueue(queue * queue);
|
||||
|
||||
// Adds data to the back of a queue:
|
||||
void pushQueue(queue * queue, void * data, queueDataType type);
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.4.
|
||||
// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.5
|
||||
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
||||
// Barry Kane, 2021
|
||||
#include <time.h>
|
||||
|
@ -17,59 +17,84 @@
|
|||
#include <netinet/in.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../queue.h"
|
||||
#include "../areadata.h"
|
||||
#include "../gamelogic.h"
|
||||
#include "../constants.h"
|
||||
#include "../playerdata.h"
|
||||
#include "../linkedlist.h"
|
||||
#include "../texteffects.h"
|
||||
#include "../inputoutput.h"
|
||||
|
||||
typedef struct sockaddr sockaddr;
|
||||
void sigintHandler(int signal)
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
printf("Caught signal %d.\n", signal);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
time_t currentTime;
|
||||
unsigned delay = 4000;
|
||||
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
||||
socketCheck, activityCheck, returnVal;
|
||||
fd_set connectedClients;
|
||||
pthread_t gameLogicThread;
|
||||
pthread_t gameLogicThread, outputThread;
|
||||
int clientSockets[PLAYERCOUNT];
|
||||
userMessage sendBuffer, receiveBuffer;
|
||||
playerInfo connectedPlayers[PLAYERCOUNT];
|
||||
char testString[32] = "Hehe.";
|
||||
struct sockaddr_in serverAddress, clientAddress;
|
||||
inputMessageQueue * inputQueue = createInputMessageQueue();
|
||||
outputMessageQueue * outputQueue = createOutputMessageQueue();
|
||||
queue * inputQueue = createQueue(), * outputQueue = createQueue();
|
||||
|
||||
// Parse command-line options:
|
||||
int currentopt = 0;
|
||||
while ((currentopt = getopt(argc, argv, "d:")) != -1)
|
||||
{
|
||||
switch(currentopt)
|
||||
{
|
||||
case 'd':
|
||||
{
|
||||
delay = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the handler for SIGINT:
|
||||
signal(2, sigintHandler);
|
||||
|
||||
// -==[ TEST GAME-STATE INITIALIZATION ]==-
|
||||
// Initialize test areas:
|
||||
areaNode * areas = createAreaList(createArea("Login Area", "Please login with the /join command."));
|
||||
addAreaNodeToList(areas, createArea("Temple Entrance",
|
||||
"You are standing outside a large, elaborate temple, of white marble and delicate construction. "
|
||||
"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. "
|
||||
"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."));
|
||||
list * areas = createList(AREA);
|
||||
addToList(areas, createArea("Login Area", "Please login with the /join command."), AREA);
|
||||
|
||||
// Create the areas:
|
||||
addToList(areas, createArea("Octal One - Docking Bay Alpha",
|
||||
"You are standing in the main docking bay of the largest station in the Octal System. "
|
||||
"The sheer size of the bay is awe-inpiring. The number of ships is endless. "
|
||||
"The bay is curved along with the body of the station. A catwalk runs along the back wall of the bay. "
|
||||
"Two large arches lie at each end, leading to the other bays, and in the center, a set of doors leading to the interior of the station."), AREA);
|
||||
|
||||
addToList(areas, createArea("Octal One - Station Access Control",
|
||||
"You enter into the hallway leading to the main interior of the station."
|
||||
"The attendant informs you that due to a computer error, exits cannot be proccessed at the moment,"
|
||||
" so you will be unable to leave, until it is resolved. "
|
||||
"He apologizes profusely for the inconvenience, and clears you for entry if you wish to continue."), AREA);
|
||||
|
||||
addToList(areas, createArea("Octal One - Floor Zero",
|
||||
"You've never quite seen so many people in one place. A large ring of shopfronts surrounds an area filled with benches and tables. "
|
||||
"There's so many buisnesses in sight that you feel you could find everything you need, and this is only one of 25 main floors, "
|
||||
"not to mention the 6 outer pylons which surround the main hull of the station. Staircases lead to an upper platform allowing access to the pylons. "), AREA);
|
||||
|
||||
// Initialize test paths:
|
||||
createPath(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;
|
||||
createPath(getFromList(areas, 1)->area, getFromList(areas, 2)->area,
|
||||
"Enter the station interior.", "Return to Docking Bay Alpha.");
|
||||
createOneWayPath(getFromList(areas, 2)->area, getFromList(areas, 3)->area,
|
||||
"Continue to station interior. ");
|
||||
|
||||
list * globalSkillList = createList(SKILL);
|
||||
|
||||
// Create a few basic skills:
|
||||
createSkill(globalSkillList, "Medicine", 8, true);
|
||||
|
@ -86,19 +111,18 @@ int main(int argc, char ** argv)
|
|||
// OH NO IT'S NOT MEMORY SAFE BETTER REWRITE IT IN RUST
|
||||
// But wait, we know the string won't be too big, so it's fine.
|
||||
strcpy(connectedPlayers[index].playerName, testString);
|
||||
connectedPlayers[index].currentArea = getAreaFromList(areas, 0);
|
||||
connectedPlayers[index].currentArea = getFromList(areas, 0)->area;
|
||||
connectedPlayers[index].stats = calloc(1, sizeof(statBlock));
|
||||
connectedPlayers[index].stats->specPoints = 30;
|
||||
connectedPlayers[index].stats->skillPoints = 30;
|
||||
connectedPlayers[index].skills = calloc(1, sizeof(skillList));
|
||||
connectedPlayers[index].skills->head = NULL;
|
||||
connectedPlayers[index].skills = createList(SKILL);
|
||||
}
|
||||
|
||||
// -==[ TEST GAME-STATE INITIALIZATION END ]==-
|
||||
|
||||
// Give an intro: Display the Silverkin Industries logo and splash text.
|
||||
slowPrint(logostring, 3000);
|
||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.4\n", 5000);
|
||||
slowPrint(logostring, delay);
|
||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.5\n", delay);
|
||||
|
||||
// Seed random number generator from the current time:
|
||||
srandom((unsigned)time(¤tTime));
|
||||
|
@ -119,10 +143,9 @@ int main(int argc, char ** argv)
|
|||
|
||||
else
|
||||
{
|
||||
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
}
|
||||
|
||||
//
|
||||
bzero(&serverAddress, sizeof(serverAddress));
|
||||
|
||||
// Assign IP and port:
|
||||
|
@ -139,7 +162,7 @@ int main(int argc, char ** argv)
|
|||
|
||||
else
|
||||
{
|
||||
slowPrint("\tSocket Binding is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
slowPrint("\tSocket Binding is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
}
|
||||
|
||||
// Let's start listening:
|
||||
|
@ -150,7 +173,7 @@ int main(int argc, char ** argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
}
|
||||
length = sizeof(clientAddress);
|
||||
|
||||
|
@ -173,7 +196,7 @@ int main(int argc, char ** argv)
|
|||
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
||||
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
}
|
||||
slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
|
||||
// Prepare the game logic thread:
|
||||
gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters));
|
||||
|
@ -183,15 +206,23 @@ int main(int argc, char ** argv)
|
|||
gameLogicThreadParameters->outputQueue = outputQueue;
|
||||
gameLogicThreadParameters->inputQueue = inputQueue;
|
||||
gameLogicThreadParameters->areaList = areas;
|
||||
pthread_create(&gameLogicThread, NULL, &gameLogicLoop, gameLogicThreadParameters);
|
||||
pthread_create(&gameLogicThread, NULL, &gameLogicHandler, gameLogicThreadParameters);
|
||||
|
||||
slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", 5000);
|
||||
slowPrint("=====\n", 5000);
|
||||
struct timeval timeout = {0, 500};
|
||||
slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
|
||||
// Prepare the output queue thread:
|
||||
outputThreadParameters * outputParameters = malloc(sizeof(outputThreadParameters));
|
||||
outputParameters->outputQueue = outputQueue;
|
||||
outputParameters->tlssessions = tlssessions;
|
||||
outputParameters->connectedPlayers = connectedPlayers;
|
||||
pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters);
|
||||
|
||||
slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
slowPrint("=====\n", delay);
|
||||
|
||||
while(true)
|
||||
{
|
||||
// Clear the set of file descriptors angad add the master socket:
|
||||
// Clear the set of file descriptors and add the master socket:
|
||||
FD_ZERO(&connectedClients);
|
||||
FD_SET(socketFileDesc, &connectedClients);
|
||||
clientsAmount = socketFileDesc;
|
||||
|
@ -215,7 +246,7 @@ int main(int argc, char ** argv)
|
|||
}
|
||||
|
||||
// See if a connection is ready to be interacted with:
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, &timeout);
|
||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL);
|
||||
|
||||
// Check if select() worked:
|
||||
if ((activityCheck < 0) && (errno != EINTR))
|
||||
|
@ -251,7 +282,17 @@ int main(int argc, char ** argv)
|
|||
strcpy(sendBuffer.messageContent, "Welcome to the server!");
|
||||
messageSend(tlssessions[index], &sendBuffer);
|
||||
strcpy(receiveBuffer.messageContent, "/look");
|
||||
queueInputMessage(inputQueue, receiveBuffer, &connectedPlayers[index]);
|
||||
|
||||
// Allocate the memory for a new input message:
|
||||
inputMessage * newMessage = malloc(sizeof(inputMessage));
|
||||
newMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Copy in the correct data:
|
||||
memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage));
|
||||
newMessage->sender = &connectedPlayers[index];
|
||||
|
||||
// Push the new message onto the queue:
|
||||
pushQueue(inputQueue, newMessage, INPUT_MESSAGE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +321,7 @@ int main(int argc, char ** argv)
|
|||
// 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);
|
||||
connectedPlayers[index].currentArea = getFromList(areas, 0)->area;
|
||||
|
||||
// Prepare a fresh SSL session for the next new player:
|
||||
gnutls_init(&tlssessions[index], GNUTLS_SERVER);
|
||||
|
@ -291,48 +332,22 @@ int main(int argc, char ** argv)
|
|||
// Otherwise, they've sent a message:
|
||||
else
|
||||
{
|
||||
queueInputMessage(inputQueue, receiveBuffer, &connectedPlayers[index]);
|
||||
// Allocate the memory for a new input message:
|
||||
inputMessage * newMessage = malloc(sizeof(inputMessage));
|
||||
newMessage->content = malloc(sizeof(userMessage));
|
||||
|
||||
// Copy in the correct data:
|
||||
memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage));
|
||||
newMessage->sender = &connectedPlayers[index];
|
||||
|
||||
// Push the new message onto the queue:
|
||||
pushQueue(inputQueue, newMessage, INPUT_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through the output queue and send all unused messages:
|
||||
while(outputQueue->currentLength != 0)
|
||||
{
|
||||
while(outputQueue->lock);
|
||||
outputQueue->lock = true;
|
||||
outputMessage * message = peekOutputMessage(outputQueue);
|
||||
outputQueue->lock = false;
|
||||
|
||||
// If the first target is set to NULL, it's intended for all connected:
|
||||
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);
|
||||
}
|
||||
}
|
||||
pthread_cancel(gameLogicThread);
|
||||
pthread_cancel(outputThread);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
#include <unistd.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
void slowPrint(char * stringToPrint, int delay)
|
||||
// A character by character print, similar to a serial terminal with lower baud rate:
|
||||
void slowPrint(const char * stringToPrint, int delay)
|
||||
{
|
||||
int characterIndex = 0;
|
||||
while(stringToPrint[characterIndex] != '\0')
|
||||
while (stringToPrint[characterIndex] != '\0')
|
||||
{
|
||||
putchar(stringToPrint[characterIndex]);
|
||||
// Flush the buffer so there's no line buffering.
|
||||
|
@ -18,14 +19,15 @@ void slowPrint(char * stringToPrint, int delay)
|
|||
}
|
||||
}
|
||||
|
||||
void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bolded)
|
||||
// The same, altered to work with ncurses:
|
||||
void slowPrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded)
|
||||
{
|
||||
int characterIndex = 0;
|
||||
if(bolded)
|
||||
if (bolded)
|
||||
{
|
||||
wattron(window, A_BOLD);
|
||||
}
|
||||
while(stringToPrint[characterIndex] != '\0')
|
||||
while (stringToPrint[characterIndex] != '\0')
|
||||
{
|
||||
waddch(window, stringToPrint[characterIndex]);
|
||||
// Refresh the ncurses screen.
|
||||
|
@ -33,17 +35,18 @@ void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bol
|
|||
usleep(delay);
|
||||
characterIndex++;
|
||||
}
|
||||
if(bolded)
|
||||
if (bolded)
|
||||
{
|
||||
wattroff(window, A_BOLD);
|
||||
}
|
||||
wrefresh(window);
|
||||
}
|
||||
|
||||
void bruteforcePrint(char * stringToPrint, int delay)
|
||||
// A character by character "brute-force" print, similar to Hollywood hacking scenes:
|
||||
void bruteforcePrint(const char * stringToPrint, int delay)
|
||||
{
|
||||
unsigned int characterIndex = 0;
|
||||
while(stringToPrint[characterIndex] != '\0')
|
||||
while (stringToPrint[characterIndex] != '\0')
|
||||
{
|
||||
for(unsigned char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++)
|
||||
{
|
||||
|
@ -58,14 +61,15 @@ void bruteforcePrint(char * stringToPrint, int delay)
|
|||
}
|
||||
}
|
||||
|
||||
void bruteforcePrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bolded)
|
||||
// The same, altered to work with ncurses:
|
||||
void bruteforcePrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded)
|
||||
{
|
||||
int characterIndex = 0;
|
||||
if(bolded)
|
||||
if (bolded)
|
||||
{
|
||||
wattron(window, A_BOLD);
|
||||
}
|
||||
while(stringToPrint[characterIndex] != '\0')
|
||||
while (stringToPrint[characterIndex] != '\0')
|
||||
{
|
||||
for(char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++)
|
||||
{
|
||||
|
@ -78,19 +82,20 @@ void bruteforcePrintNcurses(char * stringToPrint, int delay, WINDOW * window, bo
|
|||
waddch(window, stringToPrint[characterIndex]);
|
||||
characterIndex++;
|
||||
}
|
||||
if(bolded)
|
||||
if (bolded)
|
||||
{
|
||||
wattroff(window, A_BOLD);
|
||||
}
|
||||
wrefresh(window);
|
||||
}
|
||||
|
||||
// Word-wrap a string to a given width:
|
||||
void wrapString(char * stringToWrap, int stringLength, int screenWidth)
|
||||
{
|
||||
int characterCount = 0;
|
||||
for(int index = 0; index < stringLength; index++)
|
||||
{
|
||||
if(stringToWrap[index] == '\n')
|
||||
if (stringToWrap[index] == '\n')
|
||||
{
|
||||
characterCount = 0;
|
||||
}
|
||||
|
@ -98,13 +103,13 @@ void wrapString(char * stringToWrap, int stringLength, int screenWidth)
|
|||
{
|
||||
characterCount++;
|
||||
}
|
||||
if(characterCount == screenWidth)
|
||||
if (characterCount == screenWidth)
|
||||
{
|
||||
while(!isspace(stringToWrap[index]) && index > 0)
|
||||
while (!isspace(stringToWrap[index]) && index > 0)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
if(index == 0)
|
||||
if (index == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,20 +5,23 @@
|
|||
#include <stdio.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
// A character by character print, similar to a serial terminal with lower baud rate.
|
||||
void slowPrint(char * stringToPrint, int delay);
|
||||
// A character by character print, similar to a serial terminal with lower baud rate:
|
||||
void slowPrint(const char * stringToPrint, int delay);
|
||||
|
||||
// The same, altered to work with ncurses.
|
||||
void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bolded);
|
||||
// The same, altered to work with ncurses:
|
||||
void slowPrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded);
|
||||
|
||||
// A character by character "brute-force" print, similar to Hollywood hacking scenes.
|
||||
void bruteforcePrint(char * stringToPrint, int delay);
|
||||
// A character by character "brute-force" print, similar to Hollywood hacking scenes:
|
||||
void bruteforcePrint(const char * stringToPrint, int delay);
|
||||
|
||||
// The same, altered to work with ncurses.
|
||||
void bruteforcePrintNcurses(char * stringToPrint, int delay, WINDOW * window, bool bolded);
|
||||
// The same, altered to work with ncurses:
|
||||
void bruteforcePrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded);
|
||||
|
||||
// Word-wrap a string to a given width:
|
||||
void wrapString(char * stringToWrap, int stringLength, int screenWidth);
|
||||
|
||||
// A string containing an ASCII art version of the Silverkin Industries logo.
|
||||
char * logostring =
|
||||
const char * logostring =
|
||||
" ///////\n"
|
||||
" //////////////////////////////////////////\n"
|
||||
" ///////////////////////////////////////////////////////////\n"
|
||||
|
@ -32,5 +35,4 @@ char * logostring =
|
|||
" # # # # # # # # ## # ### # # ## //\n"
|
||||
" # # ### ##### ##### ### # # # # #### ### /\n";
|
||||
|
||||
void wrapString(char * stringToWrap, int stringLength, int screenWidth);
|
||||
#endif
|
||||
|
|
|
@ -1,24 +1,46 @@
|
|||
#include "../src/lists.h"
|
||||
#include "../src/playerdata.h"
|
||||
#include "../src/linkedlist.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static inline void printAreaList(list * areaList)
|
||||
{
|
||||
listData * currentData;
|
||||
for(int index = 0; index < areaList->itemCount; index++)
|
||||
{
|
||||
currentData = getFromList(areaList, index);
|
||||
printf("%d\t| %s - %s\n", index, currentData->area->areaName, currentData->area->areaDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
areaNode * areaList = createAreaList(createArea("Test Area A", "This is Test Area A"));
|
||||
areaNode * counter = areaList;
|
||||
addAreaNodeToList(areaList, createArea("Test Area B", "This is Test Area B"));
|
||||
addAreaNodeToList(areaList, createArea("Test Area C", "This is Test Area C"));
|
||||
for(int index = 0; index <= 2; index++)
|
||||
list * areaList = createList(AREA);
|
||||
char areaName[256];
|
||||
char areaDescription[256];
|
||||
|
||||
printf("\n--==[ Generating a list of ten items. ]==--\n\n");
|
||||
for(int count = 1; count <= 10; count++)
|
||||
{
|
||||
printf("%s\n", counter->data->areaName);
|
||||
counter = counter->next;
|
||||
}
|
||||
deleteAreaNodeFromList(areaList, getAreaFromList(areaList, 1));
|
||||
addAreaNodeToList(areaList, createArea("Test Area D", "This is Test Area D"));
|
||||
counter = areaList;
|
||||
for(int index = 0; index <= 2; index++)
|
||||
{
|
||||
printf("%s\n", counter->data->areaName);
|
||||
counter = counter->next;
|
||||
sprintf(areaName, "Area %d", count);
|
||||
sprintf(areaDescription, "This is Area %d.", count);
|
||||
|
||||
addToList(areaList, createArea(areaName, areaDescription) , AREA);
|
||||
}
|
||||
printAreaList(areaList);
|
||||
|
||||
printf("\n--==[ Inserting items into specific indexes. ]==--\n\n");
|
||||
insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 0);
|
||||
insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 6);
|
||||
insertIntoList(areaList, createArea("Cool, it worked.", "Cool, it worked."), AREA, 11);
|
||||
printAreaList(areaList);
|
||||
|
||||
printf("\n--==[ Removing certain areas from the list. ]==--\n\n");
|
||||
removeFromList(areaList, AREA, 12);
|
||||
removeFromList(areaList, AREA, 6);
|
||||
removeFromList(areaList, AREA, 0);
|
||||
|
||||
printAreaList(areaList);
|
||||
|
||||
destroyList(&areaList);
|
||||
printf("");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
// Test for the queue type in SilverMUD:
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../src/queue.h"
|
||||
|
||||
#define formatBoolean(b) ((b) ? "true" : "false")
|
||||
#define formatEquality(b) ((b) ? "equal" : "not equal")
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Create a queue:
|
||||
printf("-=[ Creating queue ]=-:\n");
|
||||
queue * testQueue = createQueue();
|
||||
|
||||
// Check that the queue has the correct values:
|
||||
printf("-=[ Checking initial values ]=-:\n");
|
||||
|
||||
printf("- Item count should be 0:\n");
|
||||
assert(testQueue->itemCount == 0);
|
||||
printf("Item count is %d.\n\n", testQueue->itemCount);
|
||||
|
||||
printf("- Lock should be false:\n");
|
||||
assert(testQueue->lock == false);
|
||||
printf("Lock is %s.\n\n", formatBoolean(testQueue->lock));
|
||||
|
||||
printf("- Front should be (nil):\n");
|
||||
assert(testQueue->front == NULL);
|
||||
printf("Front is %p.\n\n", testQueue->front);
|
||||
|
||||
printf("- Back should be (nil):\n");
|
||||
assert(testQueue->back == NULL);
|
||||
printf("Back is %p.\n\n", testQueue->front);
|
||||
|
||||
// Create some items for the queue:
|
||||
inputMessage * testInputMessage = malloc(sizeof(inputMessage));
|
||||
testInputMessage->sender = NULL;
|
||||
testInputMessage->next = NULL;
|
||||
testInputMessage->content = malloc(sizeof(userMessage));
|
||||
strcpy(testInputMessage->content->senderName,"Queue Test Input Sender");
|
||||
strcpy(testInputMessage->content->messageContent, "Queue Test Input Content - Hello!");
|
||||
|
||||
outputMessage * testOutputMessage = malloc(sizeof(outputMessage));
|
||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
testOutputMessage->targets[index] = NULL;
|
||||
}
|
||||
testOutputMessage->next = NULL;
|
||||
testOutputMessage->content = malloc(sizeof(userMessage));
|
||||
strcpy(testOutputMessage->content->senderName, "Queue Test Output Sender");
|
||||
strcpy(testOutputMessage->content->messageContent, "Queue Test Output Content - World!");
|
||||
|
||||
commandEvent * testCommandEvent = malloc(sizeof(commandEvent));
|
||||
testCommandEvent->next = NULL;
|
||||
testCommandEvent->caller = NULL;
|
||||
testCommandEvent->command = malloc(5 * sizeof(char));
|
||||
testCommandEvent->arguments = malloc(15 * sizeof(char));
|
||||
strcpy(testCommandEvent->command, "Test");
|
||||
strcpy(testCommandEvent->arguments, "Test Arguments");
|
||||
|
||||
// Add them to the queue:
|
||||
printf("-=[ Adding items to the queue ]=-:\n");
|
||||
printf("- First item, Item count should be 1. Front and Back should be equal.\n");
|
||||
pushQueue(testQueue, testInputMessage, INPUT_MESSAGE);
|
||||
assert(testQueue->itemCount == 1);
|
||||
assert(testQueue->front == testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Second item, Item count should be 2. Front and Back should be not equal.\n");
|
||||
pushQueue(testQueue, testOutputMessage, OUTPUT_MESSAGE);
|
||||
assert(testQueue->itemCount == 2);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Third item, Item count should be 3. Front and Back should be not equal.\n");
|
||||
pushQueue(testQueue, testCommandEvent, COMMAND);
|
||||
assert(testQueue->itemCount == 3);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("-=[ Checking items and popping from queue ]=-:\n");
|
||||
printf("- First item peeked should point to testInputMessage.\n");
|
||||
assert(peekQueue(testQueue)->data.inputMessage == testInputMessage);
|
||||
printf("Peeked data is located at: %p, testInputMessage is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.inputMessage, testInputMessage);
|
||||
|
||||
printf("- Popping first item, Item count should be 2, Front and Back should not be equal.\n");
|
||||
popQueue(testQueue);
|
||||
assert(testQueue->itemCount == 2);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Second item peeked should point to testOutputMessage.\n");
|
||||
assert(peekQueue(testQueue)->data.outputMessage == testOutputMessage);
|
||||
printf("Peeked data is located at: %p, testOutputMessage is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.outputMessage, testOutputMessage);
|
||||
|
||||
printf("- Popping second item, Item count should be 1, Front and Back should be equal.\n");
|
||||
popQueue(testQueue);
|
||||
assert(testQueue->itemCount == 1);
|
||||
assert(testQueue->front == testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Third item peeked should point to testCommandEvent.\n");
|
||||
assert(peekQueue(testQueue)->data.command == testCommandEvent);
|
||||
printf("Peeked data is located at: %p, testCommandEvent is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.command, testCommandEvent);
|
||||
|
||||
printf("- Popping third item:\n");
|
||||
popQueue(testQueue);
|
||||
|
||||
printf("- Item count should be 0:\n");
|
||||
assert(testQueue->itemCount == 0);
|
||||
printf("Item count is %d.\n\n", testQueue->itemCount);
|
||||
|
||||
printf("- Lock should be false:\n");
|
||||
assert(testQueue->lock == false);
|
||||
printf("Lock is %s.\n\n", formatBoolean(testQueue->lock));
|
||||
|
||||
printf("- Front should be (nil):\n");
|
||||
assert(testQueue->front == NULL);
|
||||
printf("Front is %p.\n\n", testQueue->front);
|
||||
|
||||
printf("- Back should be (nil):\n");
|
||||
assert(testQueue->back == NULL);
|
||||
printf("Back is %p.\n\n", testQueue->front);
|
||||
}
|
Loading…
Reference in New Issue