diff --git a/LICENSE b/LICENSE deleted file mode 100644 index be3f7b2..0000000 --- a/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/Makefile b/Makefile deleted file mode 100644 index e1478ab..0000000 --- a/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -CC = gcc -clientsrc = $(wildcard src/*.c) \ - src/client/SilverMUDClient.c -clientobj = $(clientsrc:.c=.o) -serversrc = $(wildcard src/*.c) \ - src/server/SilverMUDServer.c -serverobj = $(serversrc:.c=.o) -CLIENTLDFLAGS= -lpthread -lncurses -lgnutls -SERVERLDFLAGS= -lpthread -lncurses -lgnutls -SilverMUDClient: $(clientobj) - gcc -o $@ $^ $(CLIENTLDFLAGS) - -SilverMUDServer: $(serverobj) - gcc -o $@ $^ $(SERVERLDFLAGS) - -SilverMUDClientDebug: $(clientobj) - gcc -pg $^ $(CLIENTLDFLAGS) -o $@ - -SilverMUDServerDebug: $(serverobj) - gcc -pg $^ $(SERVERLDFLAGS) -o $@ - -.PHONY: clean -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 -D debug -debug: clean SilverMUDClientDebug SilverMUDServerDebug diff --git a/README.org b/README.org deleted file mode 100644 index 8fc4d46..0000000 --- a/README.org +++ /dev/null @@ -1,91 +0,0 @@ -#+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 -** 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 =, 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 | 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: - -* 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: diff --git a/src/areadata.c b/src/areadata.c deleted file mode 100644 index 50a61b9..0000000 --- a/src/areadata.c +++ /dev/null @@ -1,72 +0,0 @@ -// areadata.c: Implements functions for playerAreas and playerPaths in SilverMUD: -// Barra Ó Catháin, 2022. -#include -#include "areadata.h" -#include "playerdata.h" -#include "linkedlist.h" - -// ==================== -// -=[ Area/Paths: ]=-: -// ==================== - -// Create an area given a name and description: -playerArea * createArea(char * nameString, char * descriptionString) -{ - // Allocate and zero memory for the new area: - playerArea * createdArea = calloc(1, sizeof(playerArea)); - - // Copy the strings into the newly created area: - strncpy(createdArea->areaName, nameString, 32 - 1); - strncpy(createdArea->areaDescription, descriptionString, MAX - 36); - - // Properly null-terminate the strings: - createdArea->areaName[31] = '\0'; - createdArea->areaDescription[MAX - 36] = '\0'; - - // Create a list for the paths in the area: - createdArea->pathList = createList(PATH); - - // Return the pointer: - return createdArea; -} - -// Create a path between two areas given two areas and two strings: -int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription) -{ - // Allocate the new paths: - playerPath * fromPath = malloc(sizeof(playerPath)); - playerPath * toPath = malloc(sizeof(playerPath)); - - // Setup the from path: - strncpy(fromPath->pathName, fromDescription, 32 - 1); - fromPath->pathName[31] = '\0'; - fromPath->areaToJoin = toArea; - - // Setup the to path: - strncpy(toPath->pathName, toDescription, 32 - 1); - toPath->pathName[31] = '\0'; - toPath->areaToJoin = fromArea; - - // Add to the lists: - addToList(fromArea->pathList, fromPath, PATH); - addToList(toArea->pathList, toPath, PATH); - - return 0; -} - -// Create a one-way path between two areas given two areas and a string: -int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description) -{ - // Allocate the new paths: - playerPath * path = calloc(1, sizeof(playerPath)); - - // Setup the path: - strncpy(path->pathName, description, 32 - 1); - path->pathName[31] = '\0'; - path->areaToJoin = toArea; - - // Add to the list: - addToList(fromArea->pathList, path, PATH); - - return 0; -} diff --git a/src/areadata.h b/src/areadata.h deleted file mode 100644 index 327db34..0000000 --- a/src/areadata.h +++ /dev/null @@ -1,49 +0,0 @@ -// areadata.h: Contains data structures and functions for playerAreas and playerPaths in SilverMUD: -// Barra Ó Catháin, 2022. -#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]; -}; - -// 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, adding it to the both area's list of paths: -int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription); - -// 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); */ - -/* int savePathList(pathNode * listToSave); */ - -/* int loadAreaList(areaNode * listToLoad); */ - -/* int loadPathList(pathNode * listToLoad); */ - -#endif diff --git a/src/client/SilverMUDClient.c b/src/client/SilverMUDClient.c deleted file mode 100644 index 2822f68..0000000 --- a/src/client/SilverMUDClient.c +++ /dev/null @@ -1,360 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../queue.h" -#include "../constants.h" -#include "../playerdata.h" -#include "../texteffects.h" -#include "../inputoutput.h" - -// 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: -typedef struct sockaddr sockaddr; - -// A globally available exit boolean: -bool shouldExit = false; - -// A function for managing the sending thread: -void * messageSender(void * 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(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); - } - - // Ignore empty messages: - if (sendBuffer.messageContent[0] == '\n') - { - continue; - } - - // Send the message to the log if logging is enabled: - if (loggingFlag == true) - { - fputs(sendBuffer.messageContent, loggingStream); - fputs("\n", loggingStream); - fflush(loggingStream); - } - - // Send the message off to the server: - 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; - int screenWidth = getmaxx(threadParameters->window); - - // Repeatedly take messages from the server and print them to the chat log window: - while (!shouldExit) - { - // Get the next message: - returnValue = messageReceive(tlsSession, &receiveBuffer); - - // Check we haven't been disconnected: - if (returnValue == -10 || returnValue == 0) - { - shouldExit = true; - } - - // Check if it's a server message: - else if (receiveBuffer.senderName[0] == '\0') - { - // Check if it's a command to disconnect: - if (receiveBuffer.messageContent[0] == '\0') - { - shouldExit = true; - pthread_exit(NULL); - } - - // 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 --====<>====--", characterDelay, window, true); - serverMessage = 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 - { - // Fit the string to the screen: - wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1, - screenWidth - strlen(receiveBuffer.senderName) - 2); - - // If the user has requested logging, insert the message into the file: - if (loggingFlag == true) - { - fputs(receiveBuffer.senderName, loggingStream); - fputs(": ", loggingStream); - fputs(receiveBuffer.messageContent, loggingStream); - fflush(loggingStream); - } - - // If we're in a block of server messages, end it: - if (serverMessage == true) - { - slowPrintNcurses("\n --====<>====-- \n", characterDelay, window, true); - serverMessage = 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); -} - -int main(int argc, char ** argv) -{ - int socketFileDesc; - struct sockaddr_in serverAddress; - pthread_t sendingThread; - pthread_t receivingThread; - int port = 5000; - int currentopt = 0; - int characterDelay = 4000; - char chatLogPath[PATH_MAX + 1]; - char gameLogPath[PATH_MAX + 1]; - char ipAddress[32] = "127.0.0.1"; - FILE * chatLog = NULL, * gameLog = NULL; - bool chatLogging = false, gameLogging = false; - - // Print welcome message: - 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': - { - memcpy(ipAddress, optarg, 32); - break; - } - case 'c': - { - memcpy(chatLogPath, optarg, PATH_MAX + 1); - chatLog = fopen(chatLogPath, "a+"); - if (chatLog == NULL) - { - chatLogging = false; - } - else - { - chatLogging = true; - } - break; - } - case 'g': - { - memcpy(gameLogPath, optarg, PATH_MAX + 1); - gameLog = fopen(gameLogPath, "a+"); - if (gameLog == NULL) - { - gameLogging = false; - } - else - { - gameLogging = true; - } - break; - } - case 'p': - { - port = atoi(optarg); - break; - } - case 'd': - { - characterDelay = atoi(optarg); - break; - } - case '?': - { - return 1; - break; - } - } - } - - // Give me a socket, and make sure it's working: - socketFileDesc = socket(AF_INET, SOCK_STREAM, 0); - if (socketFileDesc == -1) - { - printf("Socket creation failed.\n"); - exit(EXIT_FAILURE); - } - else - { - slowPrint("Socket successfully created.\n", characterDelay); - } - - // 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); - - // Connect the server and client sockets, Kronk: - if (connect(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress)) != 0) - { - slowPrint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", characterDelay); - exit(0); - } - - // Setup a GnuTLS session and initialize it: - gnutls_session_t tlsSession = NULL; - if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0) - { - exit(EXIT_FAILURE); - } - - // Setup the private credentials for our GnuTLS session: - gnutls_anon_client_credentials_t clientkey = NULL; - gnutls_anon_allocate_client_credentials(&clientkey); - gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientkey); - - // Bind the open socket to the TLS session: - gnutls_transport_set_int(tlsSession, socketFileDesc); - gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL); - - // 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: - int returnValue = -1; - do - { - returnValue = gnutls_handshake(tlsSession); - } - while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0); - - // Setup Ncurses: - initscr(); - - // Create two pointers to structs to pass arguments to the threads: - threadparameters * logArea; - threadparameters * messageArea; - - logArea = malloc(sizeof(*logArea)); - messageArea = malloc(sizeof(*messageArea)); - - // Make the windows for the structs, and pass the socket descriptor: - logArea->window = newwin(LINES - 5, COLS - 2, 1, 1); - logArea->tlsSession = tlsSession; - logArea->loggingFlag = chatLogging; - logArea->characterDelay = characterDelay; - if (chatLog != NULL) - { - logArea->loggingStream = chatLog; - } - messageArea->window = newwin(3, COLS - 2, LINES - 4, 1); - messageArea->tlsSession = tlsSession; - messageArea->loggingFlag = gameLogging; - - // Set the appropriate log pointers: - if (gameLog != NULL) - { - messageArea->loggingStream = gameLog; - } - - // Set the two windows to scroll: - scrollok(logArea->window, true); - scrollok(messageArea->window, true); - - // Run a thread to send messages, and use another to recieve: - pthread_create(&sendingThread, NULL, messageSender, messageArea); - pthread_create(&receivingThread, NULL, messageReceiver, logArea); - - // Wait for /EXIT: - pthread_join(receivingThread, NULL); - - // Close the threads: - pthread_cancel(sendingThread); - - // Close the session and socket: - gnutls_bye(tlsSession, GNUTLS_SHUT_WR); - close(socketFileDesc); - - // Free the structs: - free(logArea); - free(messageArea); - - // Close the log files: - if (gameLog != NULL) - { - fclose(gameLog); - } - if (chatLog != NULL) - { - fclose(chatLog); - } - - // Unsetup Ncurses: - endwin(); - - // Say goodbye: - slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", characterDelay); -} - diff --git a/src/constants.h b/src/constants.h deleted file mode 100644 index bb598d8..0000000 --- a/src/constants.h +++ /dev/null @@ -1,9 +0,0 @@ -// Constants.h: Contains configurable constants for SilverMUD. -// Barry Kane, 2022. -#ifndef CONSTANTS_H -#define CONSTANTS_H -#define PORT 5000 -#define MAX 2048 -#define PLAYERCOUNT 64 -#define MAXQUEUELENGTH 2048 -#endif diff --git a/src/gamelogic.c b/src/gamelogic.c deleted file mode 100644 index a612ea9..0000000 --- a/src/gamelogic.c +++ /dev/null @@ -1,790 +0,0 @@ -// gamelogic.c: Contains function definitons for dealing with the game's logic. -// Barry Kane, 2022. -#include -#include -#include -#include -#include -#include "queue.h" -#include "constants.h" -#include "gamelogic.h" -#include "playerdata.h" -#include "linkedlist.h" -#include "inputoutput.h" - -// ======================= -// -=[ Main Game Loop ]=-: -// ======================= - -// Thread function which runs the main game loop, given the needed parameters: -void * gameLogicHandler(void * parameters) -{ - gameLogicParameters * threadParameters = parameters; - inputMessage * currentInput = NULL; - queue * commandQueue = createQueue(); - while (true) - { - // Evaluate remaining commands: - while (commandQueue->itemCount != 0) - { - evaluateNextCommand(threadParameters, commandQueue); - } - - // Wait if there is nothing to do: - if (threadParameters->inputQueue->itemCount == 0) - { - pthread_cond_wait(&threadParameters->inputQueue->condition, &threadParameters->inputQueue->mutex); - } - - // Check for new messages and pop them off the queue: - if (threadParameters->inputQueue->itemCount != 0) - { - while (threadParameters->inputQueue->lock == true); - threadParameters->inputQueue->lock = true; - 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] == '/') - { - queueMessagedCommand(commandQueue, currentInput); - } - - 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); - 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; - } - - // 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) - { - recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex]; - recipientIndex++; - } - } - - // 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; - popQueue(threadParameters->inputQueue); - } - } - pthread_exit(NULL); -} - -// Evaluate the next commandEvent in a queue: -void queueMessagedCommand(queue * queue, inputMessage * messageToQueue) -{ - // Prepare the new commandEvent: - commandEvent * newCommand = calloc(1, sizeof(commandEvent)); - newCommand->command = calloc(16, sizeof(char)); - newCommand->arguments = calloc(MAX, sizeof(char)); - newCommand->caller = messageToQueue->sender; - - // Seperate the command from it's arguments: - strtok(messageToQueue->content->messageContent, " "); - - // Copy the command and arguments to the new commandEvent: - 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); - } - - pushQueue(queue, newCommand, COMMAND); -} - -// 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)); - newCommand->command = calloc(16, sizeof(char)); - newCommand->arguments = calloc(MAX, sizeof(char)); - newCommand->caller = callingPlayer; - - // Copy the command and arguments: - strncpy(newCommand->command, command, commandLength); - if (argumentsLength > 0) - { - strncpy(newCommand->arguments, arguments, argumentsLength); - } - // Ensure the arguments are safe to parse, without adding newlines: - userNameSanatize(newCommand->command, 16); - - pushQueue(queue, newCommand, COMMAND); -} - -// Evaluate the next commandEvent in a queue: -int evaluateNextCommand(gameLogicParameters * parameters, queue * queue) -{ - commandEvent * currentCommand = peekQueue(queue)->data.command; - while (queue->lock); - queue->lock = true; - if (currentCommand == NULL) - { - return -1; - } - - // Hash the command and execute the relevant functionality: - switch (hashCommand(currentCommand->command, strlen(currentCommand->command))) - { - // Look command: Returns the description of the current area and paths: - case 5626697: - { - char formattedString[64]; - userMessage * lookMessage = calloc(1, sizeof(userMessage)); - lookMessage->senderName[0] = '\0'; - strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaName, 33); - strncat(lookMessage->messageContent, "\n", 2); - strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaDescription, MAX - 35); - - // 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)); - - // 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) - { - for(size_t index = 0; index < currentCommand->caller->currentArea->pathList->itemCount; index++) - { - if ((charCount + 64) >= MAX) - { - 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; - } - // Allocate another outputMessage for the queue: - lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE); - } - free(lookMessage); - - break; - } - - // Stat command: Displays the current character's sheet. - case 5987604: - { - char * formattedString = calloc(121, sizeof(char)); - userMessage * statMessage = calloc(1, sizeof(userMessage)); - statMessage->senderName[0] = '\0'; - // Basic status: Name, level, location. - snprintf(formattedString, 120, "%s, Level %d | %s\n", currentCommand->caller->playerName, - currentCommand->caller->stats->level, currentCommand->caller->currentArea->areaName); - strncat(statMessage->messageContent, formattedString, 120); - - // Current stats: Health and WISED. - snprintf(formattedString, 120, - "Health: %d/%d\nStats:\n\tWits: %2d | Intellect: %2d | Strength: %2d | Endurance: %2d | Dexerity: %2d \n", - currentCommand->caller->stats->currentHealth, currentCommand->caller->stats->maxHealth, - currentCommand->caller->stats->wits, currentCommand->caller->stats->intellect, - currentCommand->caller->stats->strength, currentCommand->caller->stats->endurance, - currentCommand->caller->stats->dexerity); - strncat(statMessage->messageContent, formattedString, 120); - - // Levelling stats: Current XP, and spec points. - if (currentCommand->caller->stats->specPoints > 0 || currentCommand->caller->stats->skillPoints > 0) - { - snprintf(formattedString, 120, "Current Experience: %ld | Spec Points Available: %d | Skill Points Available: %d", - currentCommand->caller->stats->experience, currentCommand->caller->stats->specPoints, currentCommand->caller->stats->skillPoints); - } - else - { - snprintf(formattedString, 120, "Current Experience: %ld", currentCommand->caller->stats->experience); - } - strncat(statMessage->messageContent, formattedString, 120); - - // Allocate an outputMessage for the queue: - outputMessage * statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE); - - bzero(statMessage->messageContent, sizeof(char) * MAX); - if (currentCommand->caller->skills->head != NULL) - { - size_t skillIndex = 0; - int charCount = 0; - bool addNewline = false; - playerSkill * skill; - while (skillIndex < currentCommand->caller->skills->itemCount) - { - 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) - { - // 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) - { - strncat(statMessage->messageContent, "|\n", 3); - charCount++; - addNewline = false; - } - else - { - addNewline = true; - } - } - // 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); - - break; - } - - // Spec command: Assign spec points to stats: - case 5982259: - { - userMessage * specMessage = calloc(1, sizeof(userMessage)); - specMessage->senderName[0] = '\0'; - char * formattedString = calloc(121, sizeof(char)); - if (currentCommand->caller->stats->specPoints > 0) - { - int selectedAmount = 0; - strtok(currentCommand->arguments, " "); - selectedAmount = atoi(¤tCommand->arguments[strlen(currentCommand->arguments) + 1]); - coreStat selectedStat = getCoreStatFromString(currentCommand->arguments, 16); - if (selectedAmount > 0 && (currentCommand->caller->stats->specPoints - selectedAmount) >= 0) - { - switch (selectedStat) - { - case WITS: - { - currentCommand->caller->stats->wits += selectedAmount; - strncat(specMessage->messageContent, "Increased wits.", 16); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case INTELLECT: - { - currentCommand->caller->stats->intellect += selectedAmount; - strncat(specMessage->messageContent, "Increased intellect.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case STRENGTH: - { - currentCommand->caller->stats->strength += selectedAmount; - strncat(specMessage->messageContent, "Increased strength.", 20); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case ENDURANCE: - { - currentCommand->caller->stats->endurance += selectedAmount; - strncat(specMessage->messageContent, "Increased endurance.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case DEXERITY: - { - currentCommand->caller->stats->dexerity += selectedAmount; - strncat(specMessage->messageContent, "Increased dexerity.", 21); - currentCommand->caller->stats->specPoints -= selectedAmount; - break; - } - case INVALID: - { - strncat(specMessage->messageContent, "Invalid stat.", 21); - } - } - } - else - { - strncat(specMessage->messageContent, "You have entered an invalid amount of spec points.", 51); - } - } - else - { - strncat(specMessage->messageContent, "You have no spec points available.", 35); - } - - // 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; - queueCommand(queue, "stat", "", 5, 0, currentCommand->caller); - queue->lock = true; - - // Free the finished message: - free(specMessage); - free(formattedString); - - break; - } - - // Try command: Attempt to use a stat or skill on an object: - case 163143: - { - // Allocate the userMessage to send: - userMessage * tryMessage = malloc(sizeof(userMessage)); - tryMessage->senderName[0] = '\0'; - - // 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); - - break; - } - - // Move command: Moves the caller to a different area given a path name or number: - case 5677603: - { - char requestedPath[32]; - if (strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area) - { - memcpy(requestedPath, currentCommand->arguments, 32); - userNameSanatize(requestedPath, 32); - requestedPath[31] = '\0'; - if (movePlayerToArea(currentCommand->caller, requestedPath) == 0) - { - // Call the look command after moving. It's fine to unlock, because the loop won't - // continue until the command is queued: - queue->lock = false; - queueCommand(queue, "look", "", 5, 0, currentCommand->caller); - queue->lock = true; - } - } - - break; - } - - // Skill command: Allows you to put skill points into skills: - case 221096235: - { - userMessage * skillMessage = calloc(1, sizeof(userMessage)); - skillMessage->senderName[0] = '\0'; - if ((currentCommand->caller->stats->skillPoints - 1) >= 0) - { - int returnValue = takeSkill(parameters->globalSkillList, currentCommand->arguments, - strlen(currentCommand->arguments), currentCommand->caller); - switch(returnValue) - { - case -1: - { - strcpy(skillMessage->messageContent, "Not a valid skill."); - break; - } - case 0: - { - strcpy(skillMessage->messageContent, "Took "); - strcat(skillMessage->messageContent, currentCommand->arguments); - strcat(skillMessage->messageContent, "."); - currentCommand->caller->stats->skillPoints--; - break; - } - } - } - else - { - strcpy(skillMessage->messageContent, "You don't have enough skill points to take this skill.\n"); - } - - // Allocate an outputMessage for the queue: - outputMessage * skillOutputMessage = createTargetedOutputMessage(skillMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, skillOutputMessage, OUTPUT_MESSAGE); - - free(skillMessage); - break; - } - - // Listskills commands: List all available skills on the server: - case 2395990522: - { - userMessage * listMessage = calloc(1, sizeof(userMessage)); - char * formattedString = calloc(121, sizeof(char)); - int charCount = 0; - size_t skillIndex = 0; - bool addNewline = false; - playerSkill * currentSkill; - while (skillIndex < parameters->globalSkillList->itemCount) - { - currentSkill = getFromList(parameters->globalSkillList, skillIndex)->skill; - snprintf(formattedString, 120, "| %-31s ", currentSkill->skillName); - charCount += 43; - strncat(listMessage->messageContent, formattedString, 120); - if ((charCount + 46) >= MAX) - { - // 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) - { - strncat(listMessage->messageContent, "|\n", 3); - charCount++; - addNewline = false; - } - else - { - addNewline = true; - } - skillIndex++; - } - // 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); - break; - } - - // Talk command: Allows the player to begin a chat session with another player: - case 601264: - { - userMessage * talkMessage = malloc(sizeof(userMessage)); - talkMessage->senderName[0] = '\0'; - - // Temporary message until we can implement objects, events, and challenges. - strcpy(talkMessage->messageContent, "The talk command is currently not implemented. Implement it if you want to use it.\n"); - - // Allocate an outputMessage for the queue: - outputMessage * talkOutputMessage = createTargetedOutputMessage(talkMessage, ¤tCommand->caller, 1); - - // Queue the outputMessage: - pushQueue(parameters->outputQueue, talkOutputMessage, OUTPUT_MESSAGE); - - // Free the userMessage: - free(talkMessage); - - break; - } - - // Exit command: Sends an "empty" exit message to disconnect a client: - case 5284234: - { - // 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'; - - // 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); - - break; - } - - // Join command: Allows the player to join the game given a name: - // TODO: Implement login/character creation. Will be a while: - case 5525172: - { - 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') - { - validName = false; - } - if (strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0) - { - validName = false; - } - } - if (validName) - { - strncpy(currentCommand->caller->playerName, currentCommand->arguments, 16); - currentCommand->caller->currentArea = 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; - queueCommand(queue, "look", "", 5, 0, currentCommand->caller); - queue->lock = true; - } - } - break; - } - } - - // Remove the current command and unlock the queue: - currentCommand = NULL; - queue->lock = false; - popQueue(queue); - return 0; -} - -// 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) - { - return ERROR; - } - chance = 100 - chance; - - // Calculate the modifier: - int modifier = 0; - switch(statToCheck) - { - case WITS: - { - modifier = player->stats->wits * 4; - break; - } - case INTELLECT: - { - modifier = player->stats->intellect * 4; - break; - } - case STRENGTH: - { - modifier = player->stats->strength * 4; - break; - } - case ENDURANCE: - { - modifier = player->stats->endurance * 4; - break; - } - case DEXERITY: - { - modifier = player->stats->dexerity * 4; - break; - } - default: - { - return ERROR; - } - } - int attempt = (random() % 100) + modifier; - if (attempt >= chance) - { - if (attempt >= 98) - { - return CRITICAL_SUCCESS; - } - else - { - return SUCCESS; - } - } - else - { - if (attempt <= 2) - { - return CRITICAL_FAILURE; - } - else - { - return FAILURE; - } - } -} - -// 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) - { - return ERROR; - } - chance = 100 - chance; - - // Check if the player has the given skill: - bool playerHasSkill = false; - size_t playerIndex = 0; - while (playerIndex < player->skills->itemCount) - { - if (strncmp(skillName, getFromList(player->skills, playerIndex)->skill->skillName, skillNameLength) != 0) - { - playerHasSkill = true; - break; - } - playerIndex++; - } - - // If the player doesn't have the skill, check if it's in the game and is trained: - bool trainedSkill = false; - size_t globalIndex = 0; - while (globalIndex < globalSkillList->itemCount) - { - if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) != 0) - { - trainedSkill = getFromList(globalSkillList, globalIndex)->skill->trainedSkill; - break; - } - globalIndex++; - } - - // Calculate the modifier: - int modifier = 0; - if (trainedSkill) - { - modifier = -100; - } - else if (playerHasSkill) - { - modifier = getFromList(player->skills, playerIndex)->skill->skillModifier * 4; - } - - // Attempt the check: - int attempt = (random() % 100) + modifier; - if (attempt >= chance) - { - if (attempt >= 98) - { - return CRITICAL_SUCCESS; - } - else - { - return SUCCESS; - } - } - else - { - if (attempt <= 2) - { - return CRITICAL_FAILURE; - } - else - { - return FAILURE; - } - } -} - -// Move a player along a path in their current area: -int movePlayerToArea(playerInfo * player, char * requestedPath) -{ - // Check if a number was given first: - size_t selected = atoi(requestedPath); - if (selected != 0 && !(selected > player->currentArea->pathList->itemCount)) - { - if (getFromList(player->currentArea->pathList, selected - 1)->path != NULL && - getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin != NULL) - { - player->currentArea = getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin; - return 0; - } - else - { - return 1; - } - } - - // Otherwise search for the description: - for (size_t index = 0; index < player->currentArea->pathList->itemCount; index++) - { - if (strncmp(getFromList(player->currentArea->pathList, index)->path->pathName, - requestedPath, 32) == 0) - { - player->currentArea = getFromList(player->currentArea->pathList, index)->path->areaToJoin; - return 0; - } - } - return 1; -} - -// A hash function for distinguishing commands for the game logic handler: -unsigned int hashCommand(char * command, unsigned int commandLength) -{ - unsigned int hash = 0; - char * currentCharacter = command; - - for (unsigned int index = 0; index < commandLength && *currentCharacter != '\0'; currentCharacter++) - { - hash = 37 * hash + *currentCharacter; - } - - return hash; -} diff --git a/src/gamelogic.h b/src/gamelogic.h deleted file mode 100644 index 2fd9b8d..0000000 --- a/src/gamelogic.h +++ /dev/null @@ -1,84 +0,0 @@ -// 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" - -// ======================== -// -=[ Data Structures ]=-: -// ======================== - -// An event for storing the information needed to evaluate a command: -typedef struct commandEvent commandEvent; -typedef struct commandEvent -{ - playerInfo * caller; - commandEvent * next; - char * command; - char * arguments; -} commandEvent; - -// A data-structure containing the needed parameters for the main game loop: -typedef struct gameLogicParameters -{ - // Players: - int * playerCount; - playerInfo * connectedPlayers; - - // Queues: - queue * inputQueue; - queue * outputQueue; - - // Lists: - list * areaList; - list * globalSkillList; -} gameLogicParameters; - -// ======================== -// -=[ Functions ]=-: -// ======================== - -// Thread function which runs the main game loop, given the needed parameters: -void * gameLogicHandler(void * parameters); - -// 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 in a queue: -int evaluateNextCommand(gameLogicParameters * parameters, queue * queue); - -// Enqueue a command to a queue: -void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength, - playerInfo * callingPlayer); - -// A hash function for distinguishing commands for the game logic handler: -unsigned int hashCommand(char * command, unsigned int commandLength); - -// ============================ -// -=[ Gameplay Primitives ]=-: -// ============================ - -// The possible outcomes of a check or challenge: -typedef enum outcome -{ - CRITICAL_FAILURE, - FAILURE, - SUCCESS, - CRITICAL_SUCCESS, - ERROR -} outcome; - -// 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 for the given player, returning an outcome: -outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList); - -#endif diff --git a/src/inputoutput.c b/src/inputoutput.c deleted file mode 100644 index b8e4136..0000000 --- a/src/inputoutput.c +++ /dev/null @@ -1,173 +0,0 @@ -// inputoutput.c: Implementation of input/output library for SilverMUD. -// Barry Kane, 2022. -#include -#include -#include -#include -#include -#include -#include - -#include "queue.h" -#include "constants.h" -#include "playerdata.h" -#include "inputoutput.h" - -// Sends a message to a given TLS session, wraps the calls to gnutls_write: -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, - sizeof(((userMessage*)0)->messageContent)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - return returnValue; -} - -// Recieves a message from a given TLS session, wraps the calls to gnutls_read: -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, - sizeof(((userMessage*)0)->messageContent)); - } while (returnValue == GNUTLS_E_AGAIN || returnValue == GNUTLS_E_INTERRUPTED); - - return returnValue; -} - -// Allocate and initialize an outputMessage targeted to a variable amount of players: -outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientsCount) -{ - // Allocate a new output message: - outputMessage * newOutputMessage = malloc(sizeof(outputMessage)); - newOutputMessage->content = malloc(sizeof(userMessage)); - - // 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; - - while (true) - { - // If there's nothing to do, put the thread to sleep: - if (outputQueue->itemCount == 0) - { - pthread_cond_wait(&outputQueue->condition, &outputQueue->mutex); - } - - // Run through the output queue and send all unsent messages: - while (outputQueue->itemCount != 0) - { - // Wait until the queue unlocks: - while (outputQueue->lock); - - // Lock the queue: - outputQueue->lock = true; - - // Get a message off the queue: - outputMessage * message = peekQueue(outputQueue)->data.outputMessage; - - // Unlock the queue: - outputQueue->lock = false; - - // If the first target is set to NULL, it's intended for all connected: - if (message->recipientsCount == 0) - { - for (int index = 0; index < PLAYERCOUNT; index++) - { - messageSend(tlssessions[index], message->content); - } - } - - // Otherwise, send it only to the targeted players: - else - { - int 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); - } - } -} - -// Sanatize user input to ensure it's okay to process: -void userInputSanatize(char * inputString, int length) -{ - for (int index = 0; index <= length; index++) - { - // If it's not a printable character, it has no business being here: - if(!isprint(inputString[index])) - { - inputString[index] = '\n'; - inputString[index + 1] = '\0'; - 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'; -} diff --git a/src/inputoutput.h b/src/inputoutput.h deleted file mode 100644 index c7d6fc0..0000000 --- a/src/inputoutput.h +++ /dev/null @@ -1,75 +0,0 @@ -// inputoutput.h: Header file contatning function prototypes and datastructures -// for dealing with input and output. -// Barry Kane, 2022. -#ifndef INPUTOUTPUT_H -#define INPUTOUTPUT_H -#include -#include -#include -#include - -#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; - -// 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); - -// Receives a message from a given TLS session, wraps the calls to gnutls_read: -int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage); - -// Create a targetedOutput message to be delivered to the players pointed to in recipients: -outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientCount); - -// A function for the output thread, which sends queued messages: -void * outputThreadHandler(void * parameters); - -// Sanatize user input to ensure it's okay to process: -void userInputSanatize(char * inputString, int length); - -// Sanatize user names so they display correctly: -void userNameSanatize(char * inputString, int length); - -#endif - diff --git a/src/linkedlist.c b/src/linkedlist.c deleted file mode 100644 index 59e7bab..0000000 --- a/src/linkedlist.c +++ /dev/null @@ -1,479 +0,0 @@ -// linkedlist.h: Function definitions for the list type for SilverMUD. -// Barry Kane, 2022. -#include -#include -#include -#include -#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; -} - diff --git a/src/linkedlist.h b/src/linkedlist.h deleted file mode 100644 index b43e9ba..0000000 --- a/src/linkedlist.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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 diff --git a/src/playerdata.c b/src/playerdata.c deleted file mode 100644 index 56bb4ed..0000000 --- a/src/playerdata.c +++ /dev/null @@ -1,225 +0,0 @@ -// playerdata.c: Contains functions definitions for working with player data. -// Barry Kane, 2021 -#include -#include -#include -#include -#include -#include "constants.h" -#include "playerdata.h" - -// Create a new skill and add it to the global skill list: -listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill) -{ - if (skillNameLength >= 32) - { - fprintf(stderr, "Skill name is too long. Please shorten the name and try again.\n"); - return NULL; - } - playerSkill * newSkill = malloc(sizeof(playerSkill)); - - strncpy(newSkill->skillName, skillName, 31); - newSkill->skillName[31] = '\0'; - - newSkill->skillPoints = 0; - newSkill->skillModifier = 0; - newSkill->trainedSkill = trainedSkill; - - // Add the skill to a node in the list: - return(addToList(globalSkillList, newSkill, SKILL)); -} - -// Take a skill and add it to the player's skill list: -int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer) -{ - // Check if the skill exists in the game: - size_t globalIndex = 0; - bool skillExists = false; - while (globalIndex < globalSkillList->itemCount) - { - if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) == 0) - { - skillExists = true; - break; - } - globalIndex++; - } - - if (!skillExists) - { - 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; - } - playerIndex++; - } - if (playerHasSkill) - { - getFromList(targetPlayer->skills, playerIndex)->skill->skillPoints++; - } - - // Copy the skill into the player's skill list: - else - { - 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; -} - - -// Take a string containing a core stat name and return the core stat: -coreStat getCoreStatFromString(char * inputString, int stringLength) -{ - // Check we've got a long enough string to fit a stat: - if (stringLength < 4) - { - return INVALID; - } - - // Lowercase the string: - char * string = malloc(sizeof(char) * stringLength); - for(int index = 0; index < stringLength; index++) - { - string[index] = tolower(inputString[index]); - } - - // If we have a string that's at most just the stat name plus a null character, or - // a dirtier string, we can check in a better order and ignore impossibilites: - if (stringLength < 9) - { - if (stringLength <= 4) - { - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - // Hopefully one of the seven letter long ones: - else if (stringLength <= 7) - { - if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - // Hopefully one of the 8 letter long stats: - else - { - if (strncmp(string, "intellect", 8) == 0) - { - free(string); - return INTELLECT; - } - else if (strncmp(string, "endurance", 8) == 0) - { - free(string); - return ENDURANCE; - } - else if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else - { - free(string); - return INVALID; - } - } - } - // Worst case, it's definitely a dirty string, compare them all: - else - { - if (strncmp(string, "wits", 4) == 0) - { - free(string); - return WITS; - } - else if (strncmp(string, "intellect", 8) == 0) - { - free(string); - return INTELLECT; - } - else if (strncmp(string, "strength", 7) == 0) - { - free(string); - return STRENGTH; - } - else if (strncmp(string, "endurance", 8) == 0) - { - free(string); - return ENDURANCE; - } - else if (strncmp(string, "dexerity", 7) == 0) - { - free(string); - return DEXERITY; - } - else - { - free(string); - return INVALID; - } - } -} - -// Deallocate a player's information including the skill lists and stats: -int deallocatePlayer(playerInfo * playerToDeallocate) -{ - // Deallocate the skill list: - destroyList(&(playerToDeallocate->skills)); - - // Deallocate the stat block: - free(playerToDeallocate->stats); - - // Deallocate the player: - free(playerToDeallocate); - - return 0; -} diff --git a/src/playerdata.h b/src/playerdata.h deleted file mode 100644 index f703682..0000000 --- a/src/playerdata.h +++ /dev/null @@ -1,82 +0,0 @@ -// playerdata.h: Header file containing data structures for player data and function -// prototypes for interacting with said data. -#ifndef PLAYERDATA_H -#define PLAYERDATA_H -#include -#include -#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: - int level; - long experience; - - // Health: - int currentHealth; - int maxHealth; - - // Core Stats: - int wits; - int intellect; - int strength; - int endurance; - int dexerity; - - // Character Building: - int specPoints; - int skillPoints; -} statBlock; - -// Information about a skill, including skill levels and modifiers for the player: -typedef struct playerSkill -{ - char skillName[32]; - int skillPoints; - int skillModifier; - bool trainedSkill; -} playerSkill; - -// Information about a single player's character: -typedef struct playerInfo -{ - char playerName[32]; - playerArea * currentArea; - statBlock * stats; - list * skills; -} playerInfo; - -// An enum of the main stats of the game: -typedef enum coreStat -{ - WITS, - INTELLECT, - STRENGTH, - ENDURANCE, - DEXERITY, - INVALID -} coreStat; - -// Create a new skill and add it to the global skill list: -listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill); - -// Take a skill and add it to the player's skill list: -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's information including the skill lists and stats: -int deallocatePlayer(playerInfo * playerToDeallocate); - -#endif diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index 74ba8e8..0000000 --- a/src/queue.c +++ /dev/null @@ -1,220 +0,0 @@ -// queue.c: Implements the queue data type and associated functions for SilverMUD. -// Barry Kane, 2022 -#include -#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); -} diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index 3217698..0000000 --- a/src/queue.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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 diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c deleted file mode 100644 index a1dfe49..0000000 --- a/src/server/SilverMUDServer.c +++ /dev/null @@ -1,359 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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) -{ - printf("Caught signal %d.\n", signal); - _exit(EXIT_SUCCESS); -} - -int main(int argc, char ** argv) -{ - time_t currentTime; - unsigned delay = 800; - int socketFileDesc, connectionFileDesc, length, clientsAmount, - socketCheck, activityCheck, returnVal; - fd_set connectedClients; - pthread_t gameLogicThread, outputThread; - int clientSockets[PLAYERCOUNT]; - userMessage sendBuffer, receiveBuffer; - playerInfo connectedPlayers[PLAYERCOUNT]; - char testString[32] = "Hehe."; - struct sockaddr_in serverAddress, clientAddress; - char motd[2048] = "Please login with the /join command."; - queue * inputQueue = createQueue(), * outputQueue = createQueue(); - - // Parse command-line options: - int currentopt = 0; - while ((currentopt = getopt(argc, argv, "d:m:")) != -1) - { - switch(currentopt) - { - case 'd': - { - delay = atoi(optarg); - break; - } - case 'm': - { - strncpy(motd, optarg, strlen(optarg) + 1); - break; - } - } - } - - // Set the handler for SIGINT: - signal(2, sigintHandler); - - // -==[ TEST GAME-STATE INITIALIZATION ]==- - // Initialize test areas: - list * areas = createList(AREA); - addToList(areas, createArea("Login Area", motd), 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(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); - createSkill(globalSkillList, "Lockpicking", 12, true); - createSkill(globalSkillList, "Programming", 12, true); - createSkill(globalSkillList, "Sensor Reading", 14, false); - createSkill(globalSkillList, "Starship Piloting", 17, true); - createSkill(globalSkillList, "Mechanical Repair", 17, true); - - // Initialize playerdata: - for (int index = 0; index < PLAYERCOUNT; index++) - { - sprintf(testString, "UNNAMED %d", index); - // OH NO IT'S NOT MEMORY SAFE BETTER REWRITE IT IN RUST - // But wait, we know the string won't be too big, so it's fine. - strcpy(connectedPlayers[index].playerName, testString); - 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 = createList(SKILL); - } - - // -==[ TEST GAME-STATE INITIALIZATION END ]==- - - // Give an intro: Display the Silverkin Industries logo and splash text. - 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)); - - // Initialize the sockets to 0, so we don't crash. - for (int index = 0; index < PLAYERCOUNT; index++) - { - clientSockets[index] = 0; - } - - // Get a socket and make sure we actually get one. - socketFileDesc = socket(AF_INET, SOCK_STREAM, 0); - if (socketFileDesc == -1) - { - fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - - else - { - slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - - bzero(&serverAddress, sizeof(serverAddress)); - - // Assign IP and port: - serverAddress.sin_family = AF_INET; - serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - serverAddress.sin_port = htons(PORT); - - // Binding newly created socket to given IP, and checking it works: - if ((bind(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress))) != 0) - { - fprintf(stderr, "\tSocket Binding is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(0); - } - - else - { - slowPrint("\tSocket Binding is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - - // Let's start listening: - if ((listen(socketFileDesc, PLAYERCOUNT)) != 0) - { - fprintf(stderr, "\tServer Listener is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(EXIT_FAILURE); - } - else - { - slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", delay); - } - length = sizeof(clientAddress); - - // Declare the needed variables for TLS sessions: - gnutls_session_t tlssessions[PLAYERCOUNT]; - gnutls_anon_server_credentials_t serverkey = NULL; - gnutls_anon_allocate_server_credentials(&serverkey); - gnutls_anon_set_server_known_dh_params(serverkey, GNUTLS_SEC_PARAM_MEDIUM); - - // Initialize all the TLS sessions to NULL: We use this to check if it's an "empty connection." - for (int index = 0; index < PLAYERCOUNT; index++) - { - tlssessions[index] = NULL; - if (gnutls_init(&tlssessions[index], GNUTLS_SERVER) < 0) - { - fprintf(stderr, "\tTLS Sessions Initialization is:\t\033[33;40mRED.\033[0m Aborting launch.\n"); - exit(EXIT_FAILURE); - } - gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL); - gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey); - gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - } - slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", delay); - - // Prepare the game logic thread: - gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters)); - gameLogicThreadParameters->connectedPlayers = connectedPlayers; - gameLogicThreadParameters->playerCount = &clientsAmount; - gameLogicThreadParameters->globalSkillList = globalSkillList; - gameLogicThreadParameters->outputQueue = outputQueue; - gameLogicThreadParameters->inputQueue = inputQueue; - gameLogicThreadParameters->areaList = areas; - pthread_create(&gameLogicThread, NULL, &gameLogicHandler, gameLogicThreadParameters); - - slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - - // Prepare the output queue thread: - outputThreadParameters * outputParameters = malloc(sizeof(outputThreadParameters)); - outputParameters->outputQueue = outputQueue; - outputParameters->tlssessions = tlssessions; - outputParameters->connectedPlayers = connectedPlayers; - pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters); - - slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay); - slowPrint("=====\n", delay); - - while(true) - { - // Clear the set of file descriptors and add the master socket: - FD_ZERO(&connectedClients); - FD_SET(socketFileDesc, &connectedClients); - clientsAmount = socketFileDesc; - - // Find all sockets that are still working and place them in the set: - for(int index = 0; index < PLAYERCOUNT; index++) - { - // Just get the one we're working with to another name: - socketCheck = clientSockets[index]; - - // If it's working, bang it into the list: - if(socketCheck > 0) - { - FD_SET(socketCheck, &connectedClients); - } - // The amount of clients is needed for select(): - if(socketCheck > clientsAmount) - { - clientsAmount = socketCheck; - } - } - - // See if a connection is ready to be interacted with: - activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL); - - // Check if select() worked: - if ((activityCheck < 0) && (errno != EINTR)) - { - fprintf(stderr, "Error in select(), retrying.\n"); - } - - // If it's the master socket selected, there is a new connection: - if (FD_ISSET(socketFileDesc, &connectedClients)) - { - if ((connectionFileDesc = accept(socketFileDesc, (struct sockaddr *)&clientAddress, (socklen_t*)&length)) < 0) - { - fprintf(stderr, "Failed to accept connection. Aborting.\n"); - exit(EXIT_FAILURE); - } - // See if we can put in the client: - for (int index = 0; index < PLAYERCOUNT; index++) - { - // When there is an empty slot, pop it in: - if (clientSockets[index] == 0) - { - clientSockets[index] = connectionFileDesc; - printf("Adding to list of sockets as %d.\n", index); - gnutls_transport_set_int(tlssessions[index], clientSockets[index]); - do - { - returnVal = gnutls_handshake(tlssessions[index]); - } - while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0); - - // Send a greeting message: - strcpy(sendBuffer.senderName, ""); - strcpy(sendBuffer.messageContent, "Welcome to the server!"); - messageSend(tlssessions[index], &sendBuffer); - strcpy(receiveBuffer.messageContent, "/look"); - - // 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; - } - } - } - // Otherwise, it's a client we need to interact with: - else - { - for (int index = 0; index < PLAYERCOUNT; index++) - { - socketCheck = clientSockets[index]; - - if(FD_ISSET(socketCheck, &connectedClients)) - { - int returnVal = messageReceive(tlssessions[index], &receiveBuffer); - // If player has disconnected: - if(returnVal == -10 || returnVal == 0) - { - // Close the session: - gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR); - gnutls_deinit(tlssessions[index]); - shutdown(clientSockets[index], 2); - close(clientSockets[index]); - clientSockets[index] = 0; - tlssessions[index] = NULL; - - // Clear out the old player state so that a new one may join: - sprintf(testString, "UNNAMED %d", index); - strcpy(connectedPlayers[index].playerName, testString); - connectedPlayers[index].currentArea = getFromList(areas, 0)->area; - - // Prepare a fresh SSL session for the next new player: - gnutls_init(&tlssessions[index], GNUTLS_SERVER); - gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL); - gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey); - gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - } - // Otherwise, they've sent a message: - else - { - // 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); - } - } - } - } - } - pthread_cancel(gameLogicThread); - pthread_cancel(outputThread); - exit(EXIT_SUCCESS); -} diff --git a/src/texteffects.c b/src/texteffects.c deleted file mode 100644 index 7cdaf57..0000000 --- a/src/texteffects.c +++ /dev/null @@ -1,121 +0,0 @@ -// texteffects.c: Implementation of text effect library for SilverMUD. -// Barry Kane, 2021. -#include -#include -#include -#include - -// 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') - { - putchar(stringToPrint[characterIndex]); - // Flush the buffer so there's no line buffering. - fflush(stdout); - usleep(delay); - characterIndex++; - } -} - -// The same, altered to work with ncurses: -void slowPrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded) -{ - int characterIndex = 0; - if (bolded) - { - wattron(window, A_BOLD); - } - while (stringToPrint[characterIndex] != '\0') - { - waddch(window, stringToPrint[characterIndex]); - // Refresh the ncurses screen. - wrefresh(window); - usleep(delay); - characterIndex++; - } - if (bolded) - { - wattroff(window, A_BOLD); - } - wrefresh(window); -} - -// 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') - { - for(unsigned char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++) - { - putchar(stringToPrint[currentCharacter]); - fflush(stdout); - usleep(delay); - putchar(8); - fflush(stdout); - } - putchar(stringToPrint[characterIndex]); - characterIndex++; - } -} - -// The same, altered to work with ncurses: -void bruteforcePrintNcurses(const char * stringToPrint, int delay, WINDOW * window, bool bolded) -{ - int characterIndex = 0; - if (bolded) - { - wattron(window, A_BOLD); - } - while (stringToPrint[characterIndex] != '\0') - { - for(char currentCharacter = 32; currentCharacter <= stringToPrint[characterIndex]; currentCharacter++) - { - waddch(window, currentCharacter); - wrefresh(window); - usleep(delay); - waddch(window, 8); - wrefresh(window); - } - waddch(window, stringToPrint[characterIndex]); - characterIndex++; - } - if (bolded) - { - wattroff(window, A_BOLD); - } - wrefresh(window); -} - -// 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') - { - characterCount = 0; - } - else - { - characterCount++; - } - if (characterCount == screenWidth) - { - while (!isspace(stringToWrap[index]) && index > 0) - { - index--; - } - if (index == 0) - { - return; - } - stringToWrap[index] = '\n'; - index++; - characterCount = 0; - } - } -} diff --git a/src/texteffects.h b/src/texteffects.h deleted file mode 100644 index 37292c4..0000000 --- a/src/texteffects.h +++ /dev/null @@ -1,38 +0,0 @@ -// texteffects.h: Header file for the texteffects library for SilverMUD. -// Barry Kane, 2021. -#ifndef TEXTEFFECTS_H_ -#define TEXTEFFECTS_H_ -#include -#include - -// 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(const char * stringToPrint, int delay, WINDOW * window, bool bolded); - -// 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(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. -const char * logostring = - " ///////\n" - " //////////////////////////////////////////\n" - " ///////////////////////////////////////////////////////////\n" - " ////////// ////////////////////////////\n" - " ### # # # # ##### ### # # # # # /////////////////\n" - " ## # # # # ## # # ### # ## # //////////////\n" - " ## # # # # # ### # # # # # # /////////\n" - " ### # ### # ##### # # # # # # # ///////\n" - " # ## # ##### # # ### ### ### # ##### ### //////\n" - " # # # # # # # # ## # # # # ## ## ////\n" - " # # # # # # # # ## # ### # # ## //\n" - " # # ### ##### ##### ### # # # # #### ### /\n"; - -#endif diff --git a/tests/list-test.c b/tests/list-test.c deleted file mode 100644 index 94f3db1..0000000 --- a/tests/list-test.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../src/linkedlist.h" -#include - -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() -{ - 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++) - { - 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(""); -} - diff --git a/tests/queue-test.c b/tests/queue-test.c deleted file mode 100644 index d248f9c..0000000 --- a/tests/queue-test.c +++ /dev/null @@ -1,132 +0,0 @@ -// Test for the queue type in SilverMUD: -#include -#include -#include -#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); -}