diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..be3f7b2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ 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/README.org b/README.org
new file mode 100644
index 0000000..8fc4d46
--- /dev/null
+++ b/README.org
@@ -0,0 +1,91 @@
+#+LATEX_HEADER: \RequirePackage[left=0.3in,top=0.3in,right=0.3in,bottom=0.3in, a4paper]{geometry}
+* SilverMUD: The Hackable Terminal-Top Roleplaying Game.
+SilverMUD is a tool for creating engaging and communal stories, all over the
+world through the internet. It's designed to give a gamemaster the same power
+to improvise that they have at the table, through simple programming and
+easy-to-understand structures.
+
+* Player's Guide
+** 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/gamelogic.c b/src/gamelogic.c
index fdb0d2b..a612ea9 100644
--- a/src/gamelogic.c
+++ b/src/gamelogic.c
@@ -108,7 +108,7 @@ void queueMessagedCommand(queue * queue, inputMessage * messageToQueue)
// 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));
+ MAX - (strlen(newCommand->command) + 2));
// Ensure the arguments are safe to parse, without adding newlines:
userNameSanatize(newCommand->command, 16);
@@ -158,397 +158,442 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * queue)
{
return -1;
}
- // Try command: Attempt to use a stat or skill on an object:
- if (strncmp(currentCommand->command, "try", 3) == 0)
+
+ // Hash the command and execute the relevant functionality:
+ switch (hashCommand(currentCommand->command, strlen(currentCommand->command)))
{
- 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);
- }
- // Exit command: Sends an "empty" exit message to disconnect a client:
- if (strncmp(currentCommand->command, "exit", 4) == 0)
- {
- // Allocate a userMessage containing null characters as the first char in both fields:
- userMessage * exitMessage = malloc(sizeof(userMessage));
- exitMessage->senderName[0] = '\0';
- exitMessage->messageContent[0] = '\0';
-
- // Allocate an outputMessage for the queue:
- outputMessage * exitOutputMessage = createTargetedOutputMessage(exitMessage, ¤tCommand->caller, 1);
-
- // Queue the outputMessage:
- pushQueue(parameters->outputQueue, exitOutputMessage, OUTPUT_MESSAGE);
-
- // Free the userMessage
- free(exitMessage);
- }
-
- // Move command: Moves the caller to a different area given a path name or number:
- if (strncmp(currentCommand->command, "move", 4) == 0)
- {
- char requestedPath[32];
- if (strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area)
+ // Look command: Returns the description of the current area and paths:
+ case 5626697:
{
- 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;
- }
- }
- }
+ 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);
- // Look command: Returns the description of the current area and paths:
- if (strncmp(currentCommand->command, "look", 4) == 0)
- {
- char formattedString[64];
- userMessage * lookMessage = calloc(1, sizeof(userMessage));
- lookMessage->senderName[0] = '\0';
- strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaName, 33);
- strncat(lookMessage->messageContent, "\n", 2);
- strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaDescription, MAX - 35);
+ // Allocate an outputMessage for the queue:
+ outputMessage * lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
- // Allocate an outputMessage for the queue:
- outputMessage * lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
-
- // Queue the outputMessage:
- pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
-
- //queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
- bzero(lookMessage, sizeof(userMessage));
-
- // 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);
- }
- // Join command: Allows the player to join the game given a name:
- // TODO: Implement login/character creation. Will be a while:
- if (strncmp(currentCommand->command, "join", 4) == 0)
- {
- if (currentCommand->caller->currentArea == 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;
- }
- }
- }
- // Talk command: Allows the player to begin a chat session with another player:
- if (strncmp(currentCommand->command, "talk", 4) == 0)
- {
- // TODO: Implement.
- }
- // Stat command: Displays the current character's sheet.
- if (strncmp(currentCommand->command, "stat", 4) == 0)
- {
- char * formattedString = calloc(121, sizeof(char));
- userMessage * statMessage = calloc(1, sizeof(userMessage));
- statMessage->senderName[0] = '\0';
- // Basic status: Name, level, location.
- snprintf(formattedString, 120, "%s, Level %d | %s\n", currentCommand->caller->playerName,
- currentCommand->caller->stats->level, currentCommand->caller->currentArea->areaName);
- strncat(statMessage->messageContent, formattedString, 120);
- // Current stats: Health and WISED.
- snprintf(formattedString, 120,
- "Health: %d/%d\nStats:\n\tWits: %2d | Intellect: %2d | Strength: %2d | Endurance: %2d | Dexerity: %2d \n",
- currentCommand->caller->stats->currentHealth, currentCommand->caller->stats->maxHealth,
- currentCommand->caller->stats->wits, currentCommand->caller->stats->intellect,
- currentCommand->caller->stats->strength, currentCommand->caller->stats->endurance,
- currentCommand->caller->stats->dexerity);
- strncat(statMessage->messageContent, formattedString, 120);
+ //queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
+ bzero(lookMessage, sizeof(userMessage));
- // 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);
+ // Loop through the paths and send the appropriate amount of messages:
+ int charCount = 13;
+ strncat(lookMessage->messageContent, "You can go:", 13);
- 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)
+ if (currentCommand->caller->currentArea->pathList->itemCount > 0)
{
- skill = getFromList(currentCommand->caller->skills, skillIndex)->skill;
- skillIndex++;
- snprintf(formattedString, 120, "| %2d | %31s ", skill->skillPoints, skill->skillName);
+ 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(statMessage->messageContent, formattedString, 120);
- if ((charCount + 43) >= MAX)
+ strncat(listMessage->messageContent, formattedString, 120);
+ if ((charCount + 46) >= MAX)
{
// Allocate an outputMessage for the queue:
- statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
-
+ outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1);
+
// Queue the outputMessage:
- pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
- bzero(statMessage, sizeof(userMessage));
+ pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE);
+
+ bzero(listMessage, sizeof(userMessage));
charCount = 0;
- break;
+ addNewline = false;
}
else if (addNewline)
{
- strncat(statMessage->messageContent, "|\n", 3);
+ strncat(listMessage->messageContent, "|\n", 3);
charCount++;
addNewline = false;
}
else
{
addNewline = true;
- }
+ }
+ skillIndex++;
}
// Allocate an outputMessage for the queue:
- statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
+ 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, statOutputMessage, OUTPUT_MESSAGE);
- }
- free(statMessage);
- free(formattedString);
- }
-
- // Spec command: Assign spec points to stats:
- if (strncmp(currentCommand->command, "spec", 4) == 0)
- {
- userMessage * specMessage = calloc(1, sizeof(userMessage));
- specMessage->senderName[0] = '\0';
- char * formattedString = calloc(121, sizeof(char));
- if (currentCommand->caller->stats->specPoints > 0)
- {
- int selectedAmount = 0;
- strtok(currentCommand->arguments, " ");
- selectedAmount = atoi(¤tCommand->arguments[strlen(currentCommand->arguments) + 1]);
- coreStat selectedStat = getCoreStatFromString(currentCommand->arguments, 16);
- if (selectedAmount > 0 && (currentCommand->caller->stats->specPoints - selectedAmount) >= 0)
- {
- switch (selectedStat)
- {
- case WITS:
- {
- currentCommand->caller->stats->wits += selectedAmount;
- strncat(specMessage->messageContent, "Increased wits.", 16);
- currentCommand->caller->stats->specPoints -= selectedAmount;
- break;
- }
- case INTELLECT:
- {
- currentCommand->caller->stats->intellect += selectedAmount;
- strncat(specMessage->messageContent, "Increased intellect.", 21);
- currentCommand->caller->stats->specPoints -= selectedAmount;
- break;
- }
- case STRENGTH:
- {
- currentCommand->caller->stats->strength += selectedAmount;
- strncat(specMessage->messageContent, "Increased strength.", 20);
- currentCommand->caller->stats->specPoints -= selectedAmount;
- break;
- }
- case ENDURANCE:
- {
- currentCommand->caller->stats->endurance += selectedAmount;
- strncat(specMessage->messageContent, "Increased endurance.", 21);
- currentCommand->caller->stats->specPoints -= selectedAmount;
- break;
- }
- case DEXERITY:
- {
- currentCommand->caller->stats->dexerity += selectedAmount;
- strncat(specMessage->messageContent, "Increased dexerity.", 21);
- currentCommand->caller->stats->specPoints -= selectedAmount;
- break;
- }
- case INVALID:
- {
- strncat(specMessage->messageContent, "Invalid stat.", 21);
- }
- }
- }
- else
- {
- strncat(specMessage->messageContent, "You have entered an invalid amount of spec points.", 51);
- }
- }
- else
- {
- strncat(specMessage->messageContent, "You have no spec points available.", 35);
+ pushQueue(parameters->outputQueue, talkOutputMessage, OUTPUT_MESSAGE);
+
+ // Free the userMessage:
+ free(talkMessage);
+
+ break;
}
- // 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);
- }
- if (strncmp(currentCommand->command, "skill", 5) == 0)
- {
- userMessage * skillMessage = calloc(1, sizeof(userMessage));
- skillMessage->senderName[0] = '\0';
- if ((currentCommand->caller->stats->skillPoints - 1) >= 0)
+ // Exit command: Sends an "empty" exit message to disconnect a client:
+ case 5284234:
{
- int returnValue = takeSkill(parameters->globalSkillList, currentCommand->arguments,
- strlen(currentCommand->arguments), currentCommand->caller);
- switch(returnValue)
+ // 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)
{
- case -1:
+ bool validName = true;
+ for(int index = 0; index < *parameters->playerCount; index++)
{
- strcpy(skillMessage->messageContent, "Not a valid skill.");
- break;
+ if (currentCommand->arguments[0] == '\0')
+ {
+ validName = false;
+ }
+ if (strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0)
+ {
+ validName = false;
+ }
}
- case 0:
+ if (validName)
{
- strcpy(skillMessage->messageContent, "Took ");
- strcat(skillMessage->messageContent, currentCommand->arguments);
- strcat(skillMessage->messageContent, ".");
- currentCommand->caller->stats->skillPoints--;
- break;
+ 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;
}
- 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);
- }
- if (strncmp(currentCommand->command, "listskills", 10) == 0)
- {
- 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);
- }
// Remove the current command and unlock the queue:
currentCommand = NULL;
queue->lock = false;
@@ -706,7 +751,7 @@ int movePlayerToArea(playerInfo * player, char * 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)
+ getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin != NULL)
{
player->currentArea = getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin;
return 0;
@@ -721,12 +766,25 @@ int movePlayerToArea(playerInfo * player, char * requestedPath)
for (size_t index = 0; index < player->currentArea->pathList->itemCount; index++)
{
if (strncmp(getFromList(player->currentArea->pathList, index)->path->pathName,
- requestedPath, 32) == 0)
+ requestedPath, 32) == 0)
{
- printf("%s: %s\n", player->playerName, getFromList(player->currentArea->pathList, index)->path->pathName);
player->currentArea = getFromList(player->currentArea->pathList, index)->path->areaToJoin;
return 0;
}
}
return 1;
}
+
+// 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
index 1e717c5..2fd9b8d 100644
--- a/src/gamelogic.h
+++ b/src/gamelogic.h
@@ -55,6 +55,9 @@ int evaluateNextCommand(gameLogicParameters * parameters, queue * 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 ]=-:
// ============================
diff --git a/src/server/SilverMUDServer.c b/src/server/SilverMUDServer.c
index 51aee27..a1dfe49 100644
--- a/src/server/SilverMUDServer.c
+++ b/src/server/SilverMUDServer.c
@@ -36,7 +36,7 @@ void sigintHandler(int signal)
int main(int argc, char ** argv)
{
time_t currentTime;
- unsigned delay = 4000;
+ unsigned delay = 800;
int socketFileDesc, connectionFileDesc, length, clientsAmount,
socketCheck, activityCheck, returnVal;
fd_set connectedClients;
@@ -46,19 +46,25 @@ int main(int argc, char ** argv)
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:")) != -1)
+ while ((currentopt = getopt(argc, argv, "d:m:")) != -1)
{
switch(currentopt)
{
- case 'd':
- {
- delay = atoi(optarg);
- break;
- }
+ case 'd':
+ {
+ delay = atoi(optarg);
+ break;
+ }
+ case 'm':
+ {
+ strncpy(motd, optarg, strlen(optarg) + 1);
+ break;
+ }
}
}
@@ -68,7 +74,7 @@ int main(int argc, char ** argv)
// -==[ TEST GAME-STATE INITIALIZATION ]==-
// Initialize test areas:
list * areas = createList(AREA);
- addToList(areas, createArea("Login Area", "Please login with the /join command."), AREA);
+ addToList(areas, createArea("Login Area", motd), AREA);
// Create the areas:
addToList(areas, createArea("Octal One - Docking Bay Alpha",