Starting again, see old branch for old codebase.
This commit is contained in:
parent
fe444ac8c8
commit
2059aa0b21
|
@ -1,15 +0,0 @@
|
|||
# Object files:
|
||||
*.o
|
||||
|
||||
# Binaries:
|
||||
SilverMUDClient
|
||||
SilverMUDServer
|
||||
SilverMUDClientDebug
|
||||
SilverMUDServerDebug
|
||||
|
||||
# Profiling Artifacts:
|
||||
gmon.out
|
||||
|
||||
# LSP whatnot:
|
||||
.cache
|
||||
compile_commands.json
|
661
LICENSE
661
LICENSE
|
@ -1,661 +0,0 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/>.
|
39
Makefile
39
Makefile
|
@ -1,39 +0,0 @@
|
|||
# Compiler and linker flags needed to link to the needed libraries:
|
||||
CFLAGS = `pkg-config --cflags guile-3.0`
|
||||
LDFLAGS= -lpthread -lncurses -lgnutls `pkg-config --libs guile-3.0`
|
||||
|
||||
# Files needed to compile the client:
|
||||
clientsrc = $(wildcard src/*.c) src/client/SilverMUDClient.c
|
||||
clientobj = $(clientsrc:.c=.o)
|
||||
|
||||
# Files needed to compile the server:
|
||||
serversrc = $(wildcard src/*.c) src/server/SilverMUDServer.c
|
||||
serverobj = $(serversrc:.c=.o)
|
||||
|
||||
# Default target: Compile the client and server with aggressive optimizations and a big stack of warnings:
|
||||
all: CFLAGS += -Wall -Wextra -Ofast
|
||||
all: SilverMUDClient SilverMUDServer
|
||||
|
||||
|
||||
# Debug target: Compile the client and server with profiling, debug information, debug optimization, and the
|
||||
# preprocessor flag "debug" set.
|
||||
debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug
|
||||
debug: SilverMUDClientDebug SilverMUDServerDebug
|
||||
|
||||
SilverMUDClient: $(clientobj)
|
||||
cc $^ $(LDFLAGS) -o $@
|
||||
|
||||
SilverMUDServer: $(serverobj)
|
||||
cc $^ $(LDFLAGS) -o $@
|
||||
|
||||
SilverMUDClientDebug: $(clientobj)
|
||||
cc -pg $^ $(LDFLAGS) -o $@
|
||||
|
||||
SilverMUDServerDebug: $(serverobj)
|
||||
cc -pg $^ $(LDFLAGS) -o $@
|
||||
|
||||
# Start from a clean slate:
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug gmon.out
|
||||
|
111
README.org
111
README.org
|
@ -1,111 +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:
|
||||
#+begin_example
|
||||
SilverMUDClient -i <ip-address-of-game-server>
|
||||
#+end_example
|
||||
To connect to a server, use the command-line option =-i=, and the IP address of
|
||||
the server
|
||||
#+begin_example
|
||||
SilverMUDClient -i <ip-address-of-game-server> -p <port-of-game-server>
|
||||
#+end_example
|
||||
If the server admin is hosting the server on a port other than the
|
||||
default port of 5000, you can use the =-p= option with the number of the port.
|
||||
|
||||
If the connection is successful, you will be placed in the server's login
|
||||
area. Type =/join <player name>=, where player name is a name of your choosing,
|
||||
and you will be placed in the spawn area, ready to play.
|
||||
|
||||
*** Other Launch Options:
|
||||
|
||||
** The Basic Commands
|
||||
SilverMUD is played through a set of very simple commands. To use a command,
|
||||
type a forward-slash (/) followed immediately by the command name. The command
|
||||
can be upper or lower-case.
|
||||
|
||||
| Command | Arguments | Effect |
|
||||
|---------+------------------------------------------+---------------------------------------------------------|
|
||||
| JOIN | 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. |
|
||||
| TALK | Character Name | Begins a conversation with another player. |
|
||||
| SHOUT | None | Messages the current area, ignoring any conversations. |
|
||||
|
||||
* Gamemaster's Guide
|
||||
** Running the Server:
|
||||
|
||||
* Developer's Guide
|
||||
** Build Prerequisites:
|
||||
SilverMUD has the following dependencies:
|
||||
- GnuTLS
|
||||
- ncurses
|
||||
- GNU Guile
|
||||
|
||||
** 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.
|
||||
|
||||
For the purposes of this guide, we will use this terminology:
|
||||
- () :: These are parentheses.
|
||||
- [] :: These are brackets.
|
||||
- {} :: These are braces.
|
||||
|
||||
*** Formatting:
|
||||
**** Casing:
|
||||
- Variables :: Variables should be in camelCase, with the exception being
|
||||
variables used to store SCM values, in which case, they should in be
|
||||
snake_case.
|
||||
|
||||
- Types :: User defined types should be in PascalCase.
|
||||
|
||||
**** 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:
|
|
@ -1,9 +0,0 @@
|
|||
(define (create-two-way-path from-path-description to-path-description from-area to-area arealist)
|
||||
(begin
|
||||
(create-path from-path-description from-area to-area arealist)
|
||||
(create-path to-path-description to-area from-area arealist)))
|
||||
|
||||
;; Send a quick message to everyone in the game as the server:
|
||||
(define (shout message)
|
||||
"Send a quick message to everyone in the game as the server."
|
||||
(message-everyone "SERVER" message output-queue))
|
|
@ -1,72 +0,0 @@
|
|||
// areadata.c: Implements functions for playerAreas and playerPaths in SilverMUD:
|
||||
// Barra Ó Catháin, 2022.
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
|
@ -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"
|
||||
|
||||
// ====================
|
||||
// -=[ Area/Paths: ]=-:
|
||||
// ====================
|
||||
|
||||
// Let the compiler know that we're going to define these types:
|
||||
typedef struct playerPath playerPath;
|
||||
typedef struct playerArea playerArea;
|
||||
typedef struct list list;
|
||||
|
||||
// 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
|
|
@ -1,382 +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 <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <ncurses.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../queue.h"
|
||||
#include "../constants.h"
|
||||
#include "../playerdata.h"
|
||||
#include "../texteffects.h"
|
||||
#include "../inputoutput.h"
|
||||
|
||||
// A struct for bundling all needed 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;
|
||||
char * prompt;
|
||||
} 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;
|
||||
char * prompt = threadParameters->prompt;
|
||||
userMessage sendBuffer;
|
||||
|
||||
// Repeatedly get input from the user, place it in a userMessage, and send it to the server:
|
||||
while (!shouldExit)
|
||||
{
|
||||
usleep(200000);
|
||||
// Clear the window:
|
||||
waddstr(window, "\n\n\n");
|
||||
|
||||
// Print the prompt:
|
||||
waddstr(window, prompt);
|
||||
|
||||
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);
|
||||
memset(&sendBuffer, 0, sizeof(char) * MAX);
|
||||
}
|
||||
|
||||
// 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 the server wants to change the prompt:
|
||||
if (receiveBuffer.senderName[1] != '\0')
|
||||
{
|
||||
strncpy(threadParameters->prompt, &receiveBuffer.senderName[1], 63);
|
||||
threadParameters->prompt[63] = '\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 up the string to hold the current "prompt" that the server has sent:
|
||||
messageArea->prompt = calloc(32, sizeof(char));
|
||||
strcpy(messageArea->prompt, " Login > ");
|
||||
logArea->prompt = messageArea->prompt;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -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
|
968
src/gamelogic.c
968
src/gamelogic.c
|
@ -1,968 +0,0 @@
|
|||
// gamelogic.c: Contains function definitons for dealing with the game's logic.
|
||||
// Barry Kane, 2022.
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <libguile.h>
|
||||
#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();
|
||||
scm_init_guile();
|
||||
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';
|
||||
|
||||
if(currentInput->sender->talkingWith == NULL)
|
||||
{
|
||||
// Allocate an array of playerInfo to store the current players in the area for the output message:
|
||||
playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*));
|
||||
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate an array of two playerInfo to store the pointers to the players in the conversation:
|
||||
playerInfo ** recipients = calloc(2, (sizeof(playerInfo*)));
|
||||
|
||||
// Find which player is first in the player list:
|
||||
bool senderIsFirst = false;
|
||||
|
||||
for(int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++)
|
||||
{
|
||||
if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender)
|
||||
{
|
||||
senderIsFirst = true;
|
||||
break;
|
||||
}
|
||||
if(&threadParameters->connectedPlayers[playerIndex] == currentInput->sender->talkingWith)
|
||||
{
|
||||
senderIsFirst = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the proper recipients:
|
||||
recipients[0] = (senderIsFirst) ? currentInput->sender : currentInput->sender->talkingWith;
|
||||
recipients[1] = (senderIsFirst) ? currentInput->sender->talkingWith : currentInput->sender;
|
||||
|
||||
// There's only one recipient:
|
||||
int recipientIndex = 2;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
memset(currentInput, 0, sizeof(inputMessage));
|
||||
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;
|
||||
}
|
||||
|
||||
// Switch to the relevant command based on the hash:
|
||||
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);
|
||||
memset(lookMessage, 0, 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);
|
||||
|
||||
memset(lookMessage, 0, 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);
|
||||
}
|
||||
|
||||
// Clear the message:
|
||||
memset(lookMessage, 0, sizeof(userMessage));
|
||||
if(currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area)
|
||||
{
|
||||
// Show the players in the area:
|
||||
charCount = 23;
|
||||
strncat(lookMessage->messageContent, "These players are here:", 24);
|
||||
|
||||
int playerNumber = 1;
|
||||
for(int index = 0; index < *(parameters->playerCount); index++)
|
||||
{
|
||||
if (parameters->connectedPlayers[index].currentArea == currentCommand->caller->currentArea)
|
||||
{
|
||||
if ((charCount + 38) >= MAX)
|
||||
{
|
||||
lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
||||
memset(lookMessage, 0, sizeof(userMessage));
|
||||
charCount = 0;
|
||||
}
|
||||
snprintf(formattedString, 45, "\n%02d. %31s", playerNumber++,
|
||||
parameters->connectedPlayers[index].playerName);
|
||||
strncat(lookMessage->messageContent, formattedString, 64);
|
||||
charCount += 38;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
memset(statMessage->messageContent, 0, 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);
|
||||
memset(statMessage, 0, 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 = calloc(1, (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);
|
||||
|
||||
memset(listMessage, 0, 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;
|
||||
}
|
||||
// Shout command: Allows the player to talk to everyone in the area if they are in a conversation.
|
||||
case 220952831:
|
||||
{
|
||||
// Allocate an array of playerInfo to store the current players in the area for the output message:
|
||||
playerInfo ** recipients = calloc(PLAYERCOUNT, sizeof(playerInfo*));
|
||||
|
||||
// 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 < *parameters->playerCount; playerIndex++)
|
||||
{
|
||||
if (parameters->connectedPlayers[playerIndex].currentArea == currentCommand->caller->currentArea)
|
||||
{
|
||||
recipients[recipientIndex] = ¶meters->connectedPlayers[playerIndex];
|
||||
recipientIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a userMessage to be filled with the data from the command's arguments and caller:
|
||||
userMessage * shoutMessage = calloc(1, sizeof(userMessage));
|
||||
|
||||
// Copy in the data and terminate it:
|
||||
strncpy(shoutMessage->senderName, currentCommand->caller->playerName, 32);
|
||||
shoutMessage->senderName[31] = '\0';
|
||||
strncpy(shoutMessage->messageContent, currentCommand->arguments, MAX);
|
||||
shoutMessage->messageContent[MAX - 1] = '\0';
|
||||
strncat(shoutMessage->messageContent, "\n", MAX);
|
||||
|
||||
// Create the outputMessage for the queue:
|
||||
outputMessage * shoutOutputMessage = createTargetedOutputMessage(shoutMessage, recipients, recipientIndex);
|
||||
|
||||
// Push the message onto the output queue:
|
||||
pushQueue(parameters->outputQueue, shoutOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Free the array:
|
||||
free(recipients);
|
||||
|
||||
break;
|
||||
}
|
||||
// Talk command: Allows the player to begin a chat session with another player:
|
||||
case 6012644:
|
||||
{
|
||||
userMessage * talkMessage = calloc(1, sizeof(userMessage));
|
||||
talkMessage->senderName[0] = '\0';
|
||||
|
||||
// If there's no name specified, end the current chat sessions.
|
||||
if (currentCommand->arguments[0] == '\0' || currentCommand->arguments == NULL)
|
||||
{
|
||||
currentCommand->caller->talkingWith = NULL;
|
||||
strcpy(talkMessage->messageContent, "Conversation ended.");
|
||||
strncat(&talkMessage->senderName[1], " > ", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int playerIndex = 0; playerIndex < *parameters->playerCount; playerIndex++)
|
||||
{
|
||||
if(strncmp(currentCommand->arguments, parameters->connectedPlayers[playerIndex].playerName, 31) == 0)
|
||||
{
|
||||
currentCommand->caller->talkingWith = &(parameters->connectedPlayers[playerIndex]);
|
||||
|
||||
// Fill out the message to inform the receiving user what is happening:
|
||||
strncpy(talkMessage->messageContent, currentCommand->caller->playerName, 32);
|
||||
strcat(talkMessage->messageContent, " is talking to you.");
|
||||
|
||||
playerInfo ** recipients = calloc(1, (sizeof(playerInfo*)));
|
||||
recipients[0] = &(parameters->connectedPlayers[playerIndex]);
|
||||
|
||||
// Allocate an outputMessage for the receiving user:
|
||||
outputMessage * talkReceiverMessage = createTargetedOutputMessage(talkMessage, recipients, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, talkReceiverMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Prep the message to the calling user.
|
||||
memcpy(&talkMessage->senderName[1], currentCommand->arguments, sizeof(char) * 27);
|
||||
strncat(&talkMessage->senderName[1], " > ", 4);
|
||||
strcpy(talkMessage->messageContent, "Conversation begun with: ");
|
||||
strcat(talkMessage->messageContent, parameters->connectedPlayers[playerIndex].playerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(talkMessage->messageContent[0] == '\0')
|
||||
{
|
||||
strcpy(talkMessage->messageContent, "There is no player by that name connected.");
|
||||
}
|
||||
|
||||
// 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 = calloc(1, (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;
|
||||
|
||||
// Allocate a userMessage containing null characters as the first char in both fields:
|
||||
userMessage * joinMessage = calloc(1, (sizeof(userMessage)));
|
||||
memcpy(joinMessage->senderName, "\0 > \0", 5);
|
||||
strcpy(joinMessage->messageContent, "Logged in successfully.");
|
||||
|
||||
// Allocate an outputMessage for the queue:
|
||||
outputMessage * joinOutputMessage = createTargetedOutputMessage(joinMessage, ¤tCommand->caller, 1);
|
||||
|
||||
// Queue the outputMessage:
|
||||
pushQueue(parameters->outputQueue, joinOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Free the userMessage
|
||||
free(joinMessage);
|
||||
|
||||
// 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:
|
||||
memset(currentCommand->command, 0, sizeof(char) * 16);
|
||||
memset(currentCommand->arguments, 0, sizeof(char) * MAX);
|
||||
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 = (rand() % 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 = (rand() % 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;
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// gamelogic.h: Function prototypes and data-structures for dealing with game logic.
|
||||
// Barry Kane, 2022.
|
||||
#ifndef GAMELOGIC_H
|
||||
#define GAMELOGIC_H
|
||||
#include "areadata.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
|
||||
// Forward-declare some data structures to prevent cyclic dependencies:
|
||||
typedef struct queue queue;
|
||||
typedef struct inputMessage inputMessage;
|
||||
|
||||
// ========================
|
||||
// -=[ 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
|
|
@ -1,173 +0,0 @@
|
|||
// inputoutput.c: Implementation of input/output library for SilverMUD.
|
||||
// Barry Kane, 2022.
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
#include "inputoutput.h"
|
||||
|
||||
// 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';
|
||||
}
|
|
@ -1,73 +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 <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include "constants.h"
|
||||
#include "playerdata.h"
|
||||
|
||||
// Forward-declare some data structures to prevent cyclic dependencies:
|
||||
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
|
||||
|
480
src/linkedlist.c
480
src/linkedlist.c
|
@ -1,480 +0,0 @@
|
|||
// linkedlist.h: Function definitions for the list type for SilverMUD.
|
||||
// Barry Kane, 2022.
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "playerdata.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// Deallocate a given list node, including it's data:
|
||||
static inline void deallocateListNode(listNode * node, listDataType type)
|
||||
{
|
||||
// Delete the node:
|
||||
switch (type)
|
||||
{
|
||||
case PLAYER:
|
||||
{
|
||||
deallocatePlayer(node->data.player);
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
destroyList(&(node->data.area->pathList));
|
||||
free(node->data.area);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
case PATH:
|
||||
{
|
||||
free(node->data.path);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
free(node->data.skill);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocates and instantiates a list of the specified type:
|
||||
list * createList(listDataType type)
|
||||
{
|
||||
// Allocate and clear the memory for the list:
|
||||
list * newList = calloc(sizeof(list), 1);
|
||||
|
||||
// Set the appropriate values in the new list:
|
||||
newList->type = type;
|
||||
newList->itemCount = 0;
|
||||
newList->head = NULL;
|
||||
newList->tail = NULL;
|
||||
|
||||
// Return the new list:
|
||||
return newList;
|
||||
}
|
||||
|
||||
// Deallocates a list and all of it's members:
|
||||
int destroyList(list ** list)
|
||||
{
|
||||
// Check if the list is empty:
|
||||
if ((*list)->itemCount == 0)
|
||||
{
|
||||
free(*list);
|
||||
list = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((*list)->itemCount > 0)
|
||||
{
|
||||
removeFromList((*list), (*list)->type, (*list)->itemCount - 1);
|
||||
}
|
||||
free(*list);
|
||||
*list = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the data at a given index in a list:
|
||||
listData * getFromList(list * list, size_t listIndex)
|
||||
{
|
||||
// Check that we were given a valid index:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
perror("Invalid index specified.\n");
|
||||
return NULL;
|
||||
}
|
||||
// Return the head if index is 0:
|
||||
else if (listIndex == 0)
|
||||
{
|
||||
return &(list->head->data);
|
||||
}
|
||||
// Loop through the entries in the list until we get to the right one:
|
||||
else
|
||||
{
|
||||
listNode * currentNode = list->head;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
return &(currentNode->data);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the node at a given index in a list:
|
||||
listNode * getNodeFromList(list * list, size_t listIndex)
|
||||
{
|
||||
// Check that we were given a valid index:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
perror("Invalid index specified.\n");
|
||||
return NULL;
|
||||
}
|
||||
// Return the head if index is 0:
|
||||
else if (listIndex == 0)
|
||||
{
|
||||
return list->head;
|
||||
}
|
||||
// Loop through the entries in the list until we get to the right one:
|
||||
else
|
||||
{
|
||||
if ((list->itemCount / 2) < listIndex)
|
||||
{
|
||||
listNode * currentNode = list->tail;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->previous;
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
listNode * currentNode = list->head;
|
||||
while (listIndex-- > 0)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the given data to the end of a list:
|
||||
listNode * addToList(list * list, void * data, listDataType type)
|
||||
{
|
||||
// Check the type:
|
||||
if (type != list->type)
|
||||
{
|
||||
fprintf(stderr, "Not the correct type for this list.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If this is the first item in the list:
|
||||
if (list->itemCount == 0)
|
||||
{
|
||||
// Allocate the new node for the list:
|
||||
list->head = calloc(1, sizeof(listNode));
|
||||
|
||||
// Set the appropriate pointers for the list:
|
||||
list->head->next = NULL;
|
||||
list->head->previous = NULL;
|
||||
list->tail = list->head;
|
||||
|
||||
// Add the data to the new node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
list->head->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
list->head->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
list->head->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
list->head->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate the new node at the end of the list:
|
||||
list->tail->next = calloc(1, sizeof(listNode));
|
||||
|
||||
// Add the data to the new node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
list->tail->next->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
list->tail->next->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
list->tail->next->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
list->tail->next->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the appropriate pointers in the new node:
|
||||
list->tail->next->previous = list->tail;
|
||||
|
||||
// Set the list's tail to the new tail:
|
||||
list->tail = list->tail->next;
|
||||
}
|
||||
// Increase the count of items in the list:
|
||||
list->itemCount++;
|
||||
|
||||
// Return the new item in the list:
|
||||
return list->tail;
|
||||
}
|
||||
|
||||
// Insert the given data at a given index in the list:
|
||||
listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex)
|
||||
{
|
||||
// Check that the types are correct:
|
||||
if (list->type != type)
|
||||
{
|
||||
fprintf(stderr, "Types do not match.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Handle the special case of adding to the end of the list:
|
||||
if (listIndex == (list->itemCount - 1))
|
||||
{
|
||||
return addToList(list, data, type);
|
||||
}
|
||||
|
||||
// Handle the special case of adding to the beginning of the list:
|
||||
if (listIndex == 0)
|
||||
{
|
||||
// Create the new node:
|
||||
listNode * newNode = calloc(1, sizeof(listNode));
|
||||
|
||||
// Add the data to the node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
newNode->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
newNode->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
newNode->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
newNode->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Place it in the list:
|
||||
newNode->next = list->head;
|
||||
newNode->previous = NULL;
|
||||
list->head->previous = newNode;
|
||||
list->head = newNode;
|
||||
list->itemCount++;
|
||||
|
||||
// Return the node:
|
||||
return newNode;
|
||||
}
|
||||
|
||||
// Check that the index is valid:
|
||||
if (listIndex > (list->itemCount - 1))
|
||||
{
|
||||
fprintf(stderr, "Index is invalid for the list.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the current node at the index:
|
||||
listNode * currentNode = list->head;
|
||||
for(size_t index = 0; index < listIndex; index++)
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
|
||||
// Get the node before the current node:
|
||||
listNode * previousNode = currentNode->previous;
|
||||
|
||||
// Create the new node:
|
||||
previousNode->next = calloc(1, sizeof(listNode));
|
||||
currentNode->previous = previousNode->next;
|
||||
previousNode->next->next = currentNode;
|
||||
previousNode->next->previous = previousNode;
|
||||
|
||||
// Add the data to the node:
|
||||
switch (type)
|
||||
{
|
||||
case PATH:
|
||||
{
|
||||
previousNode->next->data.path = (playerPath *)data;
|
||||
break;
|
||||
}
|
||||
case AREA:
|
||||
{
|
||||
previousNode->next->data.area = (playerArea *)data;
|
||||
break;
|
||||
}
|
||||
case PLAYER:
|
||||
{
|
||||
previousNode->next->data.player = (playerInfo *)data;
|
||||
break;
|
||||
}
|
||||
case SKILL:
|
||||
{
|
||||
previousNode->next->data.skill = (playerSkill *)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
list->itemCount++;
|
||||
return previousNode->next;
|
||||
}
|
||||
|
||||
// Delete the given data from a list:
|
||||
bool deleteFromList(list * list, void * data, listDataType type)
|
||||
{
|
||||
size_t index = 0;
|
||||
if (getIndexFromList(list, data, type, &index) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
removeFromList(list, type, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the data from a given point in a list:
|
||||
int removeFromList(list * list, listDataType type, size_t listIndex)
|
||||
{
|
||||
// Check that we're removing the correct type:
|
||||
if (list->type != type)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the list index is valid:
|
||||
if (listIndex > list->itemCount - 1)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
// The first node in the list:
|
||||
if (listIndex == 0)
|
||||
{
|
||||
// Get the current head and move the list's head on:
|
||||
listNode * oldHead = list->head;
|
||||
list->head = list->head->next;
|
||||
|
||||
// If we haven't removed the last item, set the previous pointer
|
||||
// in the new head to null.
|
||||
if (list->head != NULL)
|
||||
{
|
||||
list->head->previous = NULL;
|
||||
}
|
||||
|
||||
// Delete the node:
|
||||
deallocateListNode(oldHead, type);
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
// The last node in the list:
|
||||
else if (listIndex == (list->itemCount - 1))
|
||||
{
|
||||
// Move the tail up by one:
|
||||
list->tail = list->tail->previous;
|
||||
|
||||
// Deallocate the former tail:
|
||||
deallocateListNode(list->tail->next, type);
|
||||
|
||||
// Set the appropriate pointer:
|
||||
list->tail->next = NULL;
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
// A node in the middle of the list:
|
||||
else
|
||||
{
|
||||
// Get the needed node as a pointer:
|
||||
listNode * nodeToDelete = getNodeFromList(list, listIndex);
|
||||
|
||||
// Set the appropriate pointers for the surrounding nodes:
|
||||
nodeToDelete->previous->next = nodeToDelete->next;
|
||||
nodeToDelete->next->previous = nodeToDelete->previous;
|
||||
|
||||
// Deallocate the node:
|
||||
deallocateListNode(nodeToDelete, type);
|
||||
|
||||
// Return the new amount of items in the list:
|
||||
list->itemCount--;
|
||||
return list->itemCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the index of a given piece of data in a list:
|
||||
bool getIndexFromList(list * list, void * data, listDataType type, size_t * index)
|
||||
{
|
||||
// Check the list types are the same:
|
||||
if (list->type == type)
|
||||
{
|
||||
fprintf(stderr, "List types do not match.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search through the list, one-by-one, comparing the list items:
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
// linkedlist.h: Defines the linked list datatype for SilverMUD.
|
||||
// Barry Kane, 2022.
|
||||
#ifndef LINKEDLIST_H
|
||||
#define LINKEDLIST_H
|
||||
#include "areadata.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
|
225
src/playerdata.c
225
src/playerdata.c
|
@ -1,225 +0,0 @@
|
|||
// playerdata.c: Contains functions definitions for working with player data.
|
||||
// Barry Kane, 2021
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#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;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
// playerdata.h: Header file containing data structures for player data and function
|
||||
// prototypes for interacting with said data.
|
||||
#ifndef PLAYERDATA_H
|
||||
#define PLAYERDATA_H
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "areadata.h"
|
||||
#include "constants.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
// Let the compiler know there will be structs defined elsewhere:
|
||||
typedef struct playerInfo playerInfo;
|
||||
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
|
||||
{
|
||||
playerInfo * talkingWith;
|
||||
playerArea * currentArea;
|
||||
char playerName[32];
|
||||
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
|
220
src/queue.c
220
src/queue.c
|
@ -1,220 +0,0 @@
|
|||
// queue.c: Implements the queue data type and associated functions for SilverMUD.
|
||||
// Barry Kane, 2022
|
||||
#include <pthread.h>
|
||||
#include "queue.h"
|
||||
|
||||
// Allocates and instantiates a queue:
|
||||
queue * createQueue(void)
|
||||
{
|
||||
// Allocate the memory for the queue:
|
||||
queue * newQueue = malloc(sizeof(queue));
|
||||
|
||||
// Instantiate the variables in the data-structure:
|
||||
newQueue->itemCount = 0;
|
||||
newQueue->front = NULL;
|
||||
newQueue->back = NULL;
|
||||
|
||||
// Create the threading constructs:
|
||||
newQueue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
newQueue->condition = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
|
||||
// Return the pointer to the new queue:
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
// Destroys a queue and all of it's members:
|
||||
void destroyQueue(queue ** queue)
|
||||
{
|
||||
// Pop everything off of the queue:
|
||||
while ((*queue)->itemCount > 0)
|
||||
{
|
||||
popQueue(*queue);
|
||||
}
|
||||
|
||||
// Deallocate the queue:
|
||||
free(*queue);
|
||||
|
||||
// Point the queue pointer to NULL;
|
||||
*queue = NULL;
|
||||
}
|
||||
|
||||
// Returns the data at the front of the given queue:
|
||||
queueNode * peekQueue(queue * queue)
|
||||
{
|
||||
return queue->front;
|
||||
}
|
||||
|
||||
// Removes the current data from the front of the queue:
|
||||
void popQueue(queue * queue)
|
||||
{
|
||||
// Check if the queue is locked, and wait:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Check there is actually anything to remove:
|
||||
if (queue->itemCount == 0)
|
||||
{
|
||||
queue->lock = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the special case of being the last item in the queue:
|
||||
else if (queue->itemCount == 1)
|
||||
{
|
||||
// Deallocate the correct data:
|
||||
switch (queue->front->type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
free(queue->front->data.command->command);
|
||||
free(queue->front->data.command->arguments);
|
||||
free(queue->front->data.command);
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.inputMessage->content);
|
||||
free(queue->front->data.inputMessage);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.outputMessage->content);
|
||||
free(queue->front->data.outputMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deallocate the node:
|
||||
free(queue->front);
|
||||
|
||||
// Set the correct variables for the queue:
|
||||
queue->front = NULL;
|
||||
queue->back = NULL;
|
||||
queue->itemCount = 0;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the current front of the queue:
|
||||
else
|
||||
{
|
||||
// Deallocate the correct data:
|
||||
switch (queue->front->type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
break;
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
free(queue->front->data.command->command);
|
||||
free(queue->front->data.command->arguments);
|
||||
free(queue->front->data.command);
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.inputMessage->content);
|
||||
free(queue->front->data.inputMessage);
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
free(queue->front->data.outputMessage->content);
|
||||
free(queue->front->data.outputMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save a pointer to the current node so we don't leak it:
|
||||
queueNode * nodeToDelete = queue->front;
|
||||
|
||||
// Advance the queue:
|
||||
queue->front = queue->front->next;
|
||||
queue->itemCount--;
|
||||
|
||||
// Deallocate the old node:
|
||||
free(nodeToDelete);
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds data to the back of a queue:
|
||||
void pushQueue(queue * queue, void * data, queueDataType type)
|
||||
{
|
||||
// Check if the queue is locked:
|
||||
while (queue->lock);
|
||||
|
||||
// Create a new node:
|
||||
queueNode * newNode = malloc(sizeof(queueNode));
|
||||
newNode->type = type;
|
||||
// Copy the data into the correct slot for the type:
|
||||
switch (type)
|
||||
{
|
||||
case EVENT:
|
||||
{
|
||||
// TODO: Implement events.
|
||||
break;
|
||||
}
|
||||
case COMMAND:
|
||||
{
|
||||
newNode->data.command = data;
|
||||
break;
|
||||
}
|
||||
case INPUT_MESSAGE:
|
||||
{
|
||||
newNode->data.inputMessage = data;
|
||||
break;
|
||||
}
|
||||
case OUTPUT_MESSAGE:
|
||||
{
|
||||
newNode->data.outputMessage = data;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if the queue is locked:
|
||||
while (queue->lock);
|
||||
|
||||
// Lock the queue:
|
||||
queue->lock = true;
|
||||
|
||||
// Set the correct pointers:
|
||||
newNode->next = NULL;
|
||||
|
||||
if (queue->itemCount == 0)
|
||||
{
|
||||
queue->front = newNode;
|
||||
queue->back = newNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->back->next = newNode;
|
||||
queue->back = newNode;
|
||||
}
|
||||
|
||||
// Increase the queue item count:
|
||||
queue->itemCount++;
|
||||
|
||||
// Unlock the queue:
|
||||
queue->lock = false;
|
||||
|
||||
// Flag that the queue was modified:
|
||||
pthread_cond_broadcast(&queue->condition);
|
||||
}
|
71
src/queue.h
71
src/queue.h
|
@ -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"
|
||||
|
||||
// Forward-declare some data structures to prevent cyclic dependencies:
|
||||
typedef struct queue queue;
|
||||
|
||||
// ========================
|
||||
// -=[ Data Structures ]=-:
|
||||
// ========================
|
||||
|
||||
// An enum which is used to state what type of data is being stored in a queueNode:
|
||||
typedef enum queueDataType
|
||||
{
|
||||
EVENT,
|
||||
COMMAND,
|
||||
INPUT_MESSAGE,
|
||||
OUTPUT_MESSAGE
|
||||
} queueDataType;
|
||||
|
||||
// A union for storing a pointer to all the types of data a queueNode may hold:
|
||||
typedef union queueData
|
||||
{
|
||||
outputMessage * outputMessage;
|
||||
inputMessage * inputMessage;
|
||||
commandEvent * command;
|
||||
} queueData;
|
||||
|
||||
// A queue node, a singly-linked list node for our queue:
|
||||
typedef struct queueNode queueNode;
|
||||
typedef struct queueNode
|
||||
{
|
||||
queueDataType type;
|
||||
queueData data;
|
||||
queueNode * next;
|
||||
} queueNode;
|
||||
|
||||
// A queue, with pointers to the head and tail of the linked list, and data for multi-threading, locking, and an item count.
|
||||
typedef struct queue
|
||||
{
|
||||
volatile bool lock;
|
||||
size_t itemCount;
|
||||
queueNode * front;
|
||||
queueNode * back;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t condition;
|
||||
} queue;
|
||||
|
||||
// ========================
|
||||
// -=[ Functions ]=-:
|
||||
// ========================
|
||||
|
||||
// Allocates and instantiates a queue:
|
||||
queue * createQueue(void);
|
||||
|
||||
// Destroys a queue and all of it's members:
|
||||
void destroyQueue(queue ** queue);
|
||||
|
||||
// Returns the node at the front of the given queue:
|
||||
queueNode * peekQueue(queue * queue);
|
||||
|
||||
// Removes the current node from the front of the queue:
|
||||
void popQueue(queue * queue);
|
||||
|
||||
// Adds data to the back of a queue:
|
||||
void pushQueue(queue * queue, void * data, queueDataType type);
|
||||
|
||||
#endif
|
|
@ -1,217 +0,0 @@
|
|||
// schemeintegration.h: Function definitions for SilverMUD's Scheme integration.
|
||||
// Barra Ó Catháin, 2023.
|
||||
#include <string.h>
|
||||
#include <libguile.h>
|
||||
#include "schemeintegration.h"
|
||||
|
||||
// Create a player skill and add it to a given skill list from Scheme:
|
||||
SCM scheme_create_skill(SCM string, SCM skilllist)
|
||||
{
|
||||
size_t skillNameLength = 0;
|
||||
char * skillName = scm_to_latin1_stringn(string, &skillNameLength);
|
||||
createSkill(scm_to_pointer(skilllist), skillName, skillNameLength, false);
|
||||
free(skillName);
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
// Create a new area and add it to the list, given a name and area description, and an area list from Scheme.
|
||||
// Returns the index of the new area:
|
||||
SCM scheme_create_area(SCM area_name, SCM area_description, SCM area_list)
|
||||
{
|
||||
// Check if the area list exists:
|
||||
list * areaList = scm_to_pointer(area_list);
|
||||
if (areaList == NULL || areaList->type != AREA)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
// Turn the SCM values into C strings that we can use:
|
||||
char * areaName = scm_to_locale_stringn(area_name, NULL);
|
||||
char * areaDescription= scm_to_locale_stringn(area_description, NULL);
|
||||
|
||||
// Create and add the area to the area list:
|
||||
addToList(areaList, createArea(areaName, areaDescription), AREA);
|
||||
|
||||
// The new index of the area will be at the end of the list. Consider returning a pointer to the area:
|
||||
size_t areaIndex = areaList->itemCount - 1;
|
||||
|
||||
// Free the strings:
|
||||
free(areaName);
|
||||
free(areaDescription);
|
||||
|
||||
// Return the area index to Scheme for further lispy hacking:
|
||||
return scm_from_size_t(areaIndex);
|
||||
}
|
||||
|
||||
// Create a one way path between two areas from scheme:
|
||||
SCM scheme_create_path(SCM path_name, SCM from_area_index, SCM to_area_index, SCM area_list)
|
||||
{
|
||||
// Check if the area list exists:
|
||||
list * areaList = scm_to_pointer(area_list);
|
||||
if (areaList == NULL || areaList->type != AREA)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
// Check if the areas exist:
|
||||
playerArea * fromArea = getFromList(areaList, scm_to_size_t(from_area_index))->area;
|
||||
playerArea * toArea = getFromList(areaList, scm_to_size_t(to_area_index))->area;
|
||||
|
||||
if (fromArea == NULL || toArea == NULL)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
// Turn the SCM value into a C string that we can use:
|
||||
char * pathName = scm_to_locale_stringn(path_name, NULL);
|
||||
|
||||
// Create the path:
|
||||
createOneWayPath(fromArea, toArea, pathName);
|
||||
|
||||
// Free the string:
|
||||
free(pathName);
|
||||
|
||||
// Return true to Scheme:
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
// Change the name of an existing area in a list, given the number of the area in the list, from Scheme:
|
||||
SCM scheme_change_area_name(SCM new_name, SCM area_number, SCM area_list)
|
||||
{
|
||||
// Check if the area exists:
|
||||
list * areaList = scm_to_pointer(area_list);
|
||||
size_t areaNumber = scm_to_size_t(area_number);
|
||||
|
||||
if (areaList->type != AREA)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
playerArea * area = getFromList(areaList, areaNumber)->area;
|
||||
|
||||
if (area == NULL)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
// Create a string from the Scheme string and copy it into the area:
|
||||
size_t newNameLength = 0;
|
||||
char * newName = scm_to_locale_stringn(new_name, &newNameLength);
|
||||
memset(area->areaName, 0, 32);
|
||||
if (newNameLength > 32)
|
||||
{
|
||||
memcpy(area->areaName, newName, 31);
|
||||
area->areaName[31] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(area->areaName, newName, newNameLength);
|
||||
area->areaName[31] = '\0';
|
||||
}
|
||||
|
||||
free(newName);
|
||||
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
// Change the description of an existing area in a list, given the number of the area in the list, from Scheme:
|
||||
SCM scheme_change_area_description(SCM new_description, SCM area_number, SCM area_list)
|
||||
{
|
||||
// Check if the area exists:
|
||||
list * areaList = scm_to_pointer(area_list);
|
||||
size_t areaNumber = scm_to_size_t(area_number);
|
||||
|
||||
if (areaList->type != AREA)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
playerArea * area = getFromList(areaList, areaNumber)->area;
|
||||
|
||||
if (area == NULL)
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
// Create a string from the Scheme string and copy it into the area:
|
||||
size_t newDescriptionLength = 0;
|
||||
char * newDescription = scm_to_locale_stringn(new_description, &newDescriptionLength);
|
||||
memset(area->areaDescription, 0, MAX - 35);
|
||||
if (newDescriptionLength > MAX - 35)
|
||||
{
|
||||
memcpy(area->areaDescription, newDescription, MAX - 35);
|
||||
area->areaDescription[MAX - 36] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(area->areaDescription, newDescription, newDescriptionLength);
|
||||
area->areaDescription[MAX - 36] = '\0';
|
||||
}
|
||||
|
||||
free(newDescription);
|
||||
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
// Message every currently connected player from Scheme:
|
||||
SCM scheme_message_everyone(SCM sender_name, SCM message_content, SCM output_queue)
|
||||
{
|
||||
// Allocate the memory for the needed data structures:
|
||||
outputMessage * newOutputMessage = calloc(1, sizeof(userMessage));
|
||||
userMessage * newMessage = calloc(1, sizeof(userMessage));
|
||||
|
||||
// Set some basic information for the output message, allowing it to be sent to everyone:
|
||||
newOutputMessage->content = newMessage;
|
||||
newOutputMessage->recipientsCount = 0;
|
||||
newOutputMessage->recipients = NULL;
|
||||
|
||||
// Convert the Scheme strings to C strings, and ensure they're NULL terminated:
|
||||
scm_to_locale_stringbuf(sender_name, newMessage->senderName, 31);
|
||||
newMessage->senderName[31] = '\0';
|
||||
scm_to_locale_stringbuf(message_content, newMessage->messageContent, MAX - 1);
|
||||
newMessage->messageContent[MAX - 1] = '\0';
|
||||
|
||||
// Clean up the message contents to ensure they're safe to send and display correctly:
|
||||
userNameSanatize(newMessage->senderName, 32);
|
||||
userInputSanatize(newMessage->messageContent, MAX);
|
||||
|
||||
// Push it to the queue, where it will be handled and de-allocated:
|
||||
pushQueue(scm_to_pointer(output_queue), newOutputMessage, OUTPUT_MESSAGE);
|
||||
|
||||
// Return a Scheme #t value:
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
// The function ran by the Scheme thread which handles all game-master and interpreter interaction:
|
||||
void * schemeHandler(void * parameters)
|
||||
{
|
||||
// Take in the needed values from the main thread and make it back into the struct:
|
||||
SchemeThreadParameters * schemeThreadParameters = parameters;
|
||||
|
||||
// Initialize GNU Guile:
|
||||
scm_init_guile();
|
||||
|
||||
// Register the various functions:
|
||||
scm_c_define_gsubr("create-area", 3, 0, 0, &scheme_create_area);
|
||||
scm_c_define_gsubr("create-path", 4, 0, 0, &scheme_create_path);
|
||||
scm_c_define_gsubr("create-skill", 2, 0, 0, &scheme_create_skill);
|
||||
scm_c_define_gsubr("message-everyone", 3, 0, 0, &scheme_message_everyone);
|
||||
scm_c_define_gsubr("change-area-name", 3, 0, 0, &scheme_change_area_name);
|
||||
scm_c_define_gsubr("change-area-description", 3, 0, 0, &scheme_change_area_description);
|
||||
|
||||
// Define the various game state pointers as Scheme objects:
|
||||
scm_c_define("area-list", scm_from_pointer(schemeThreadParameters->areaList, NULL));
|
||||
scm_c_define("skill-list", scm_from_pointer(schemeThreadParameters->skillList, NULL));
|
||||
scm_c_define("output-queue", scm_from_pointer(schemeThreadParameters->outputQueue, NULL));
|
||||
|
||||
// Enable readline support:
|
||||
scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))");
|
||||
scm_c_eval_string("(begin (use-modules (system repl server)) (if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\")) (spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))");
|
||||
|
||||
// Drop into the Scheme interpreter:
|
||||
scm_shell(0, NULL);
|
||||
|
||||
// Return NULL. This should be unreachable.
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// schemeintegration.h: Data-structures and function prototypes for SilverMUD's Scheme integration.
|
||||
// Barra Ó Catháin, 2023.
|
||||
#ifndef SCHEMEINTEGRATION_H
|
||||
#define SCHEMEINTEGRATION_H
|
||||
#include "queue.h"
|
||||
#include "linkedlist.h"
|
||||
|
||||
typedef struct list list;
|
||||
typedef struct queue queue;
|
||||
|
||||
typedef struct SchemeThreadParameters
|
||||
{
|
||||
list * areaList, * skillList;
|
||||
queue * inputQueue, * outputQueue;
|
||||
} SchemeThreadParameters;
|
||||
|
||||
void * schemeHandler(void * parameters);
|
||||
|
||||
#endif
|
|
@ -1,378 +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 <time.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <ncurses.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../queue.h"
|
||||
#include "../areadata.h"
|
||||
#include "../gamelogic.h"
|
||||
#include "../constants.h"
|
||||
#include "../playerdata.h"
|
||||
#include "../linkedlist.h"
|
||||
#include "../texteffects.h"
|
||||
#include "../inputoutput.h"
|
||||
#include "../schemeintegration.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;
|
||||
fd_set connectedClients;
|
||||
pthread_t gameLogicThread, outputThread, schemeThread;
|
||||
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, 2047);
|
||||
motd[2047] = '\0';
|
||||
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:
|
||||
srand((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);
|
||||
}
|
||||
|
||||
memset(&serverAddress, 0, 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);
|
||||
|
||||
// Prepare the Scheme handler thread:
|
||||
SchemeThreadParameters * schemeParameters = malloc(sizeof(SchemeThreadParameters));
|
||||
schemeParameters->skillList = globalSkillList;
|
||||
schemeParameters->outputQueue = outputQueue;
|
||||
schemeParameters->areaList = areas;
|
||||
slowPrint("\tScheme Thread is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
||||
slowPrint("=====\n", delay);
|
||||
pthread_create(&schemeThread, NULL, &schemeHandler, schemeParameters);
|
||||
|
||||
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)
|
||||
{
|
||||
volatile int handshakeReturnValue = 0;
|
||||
clientSockets[index] = connectionFileDesc;
|
||||
gnutls_transport_set_int(tlssessions[index], clientSockets[index]);
|
||||
do
|
||||
{
|
||||
handshakeReturnValue = gnutls_handshake(tlssessions[index]);
|
||||
} while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0);
|
||||
|
||||
// If it's good, send them the welcome message:
|
||||
if(handshakeReturnValue == 0)
|
||||
{
|
||||
// Send a greeting message:
|
||||
memcpy(sendBuffer.senderName, "\0 Login > \0", 11);
|
||||
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;
|
||||
}
|
||||
|
||||
// If it's not good, close it, we don't want it:
|
||||
shutdown(clientSockets[index], 2);
|
||||
close(clientSockets[index]);
|
||||
clientSockets[index] = 0;
|
||||
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);
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
// texteffects.c: Implementation of text effect library for SilverMUD.
|
||||
// Barry Kane, 2021.
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// texteffects.h: Header file for the texteffects library for SilverMUD.
|
||||
// Barry Kane, 2021.
|
||||
#ifndef TEXTEFFECTS_H_
|
||||
#define TEXTEFFECTS_H_
|
||||
#include <stdio.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
// 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
|
|
@ -1,46 +0,0 @@
|
|||
#include "../src/linkedlist.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static inline void printAreaList(list * areaList)
|
||||
{
|
||||
listData * currentData;
|
||||
for(int index = 0; index < areaList->itemCount; index++)
|
||||
{
|
||||
currentData = getFromList(areaList, index);
|
||||
printf("%d\t| %s - %s\n", index, currentData->area->areaName, currentData->area->areaDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
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("");
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
// Test for the queue type in SilverMUD:
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../src/queue.h"
|
||||
|
||||
#define formatBoolean(b) ((b) ? "true" : "false")
|
||||
#define formatEquality(b) ((b) ? "equal" : "not equal")
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Create a queue:
|
||||
printf("-=[ Creating queue ]=-:\n");
|
||||
queue * testQueue = createQueue();
|
||||
|
||||
// Check that the queue has the correct values:
|
||||
printf("-=[ Checking initial values ]=-:\n");
|
||||
|
||||
printf("- Item count should be 0:\n");
|
||||
assert(testQueue->itemCount == 0);
|
||||
printf("Item count is %d.\n\n", testQueue->itemCount);
|
||||
|
||||
printf("- Lock should be false:\n");
|
||||
assert(testQueue->lock == false);
|
||||
printf("Lock is %s.\n\n", formatBoolean(testQueue->lock));
|
||||
|
||||
printf("- Front should be (nil):\n");
|
||||
assert(testQueue->front == NULL);
|
||||
printf("Front is %p.\n\n", testQueue->front);
|
||||
|
||||
printf("- Back should be (nil):\n");
|
||||
assert(testQueue->back == NULL);
|
||||
printf("Back is %p.\n\n", testQueue->front);
|
||||
|
||||
// Create some items for the queue:
|
||||
inputMessage * testInputMessage = malloc(sizeof(inputMessage));
|
||||
testInputMessage->sender = NULL;
|
||||
testInputMessage->next = NULL;
|
||||
testInputMessage->content = malloc(sizeof(userMessage));
|
||||
strcpy(testInputMessage->content->senderName,"Queue Test Input Sender");
|
||||
strcpy(testInputMessage->content->messageContent, "Queue Test Input Content - Hello!");
|
||||
|
||||
outputMessage * testOutputMessage = malloc(sizeof(outputMessage));
|
||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
||||
{
|
||||
testOutputMessage->targets[index] = NULL;
|
||||
}
|
||||
testOutputMessage->next = NULL;
|
||||
testOutputMessage->content = malloc(sizeof(userMessage));
|
||||
strcpy(testOutputMessage->content->senderName, "Queue Test Output Sender");
|
||||
strcpy(testOutputMessage->content->messageContent, "Queue Test Output Content - World!");
|
||||
|
||||
commandEvent * testCommandEvent = malloc(sizeof(commandEvent));
|
||||
testCommandEvent->next = NULL;
|
||||
testCommandEvent->caller = NULL;
|
||||
testCommandEvent->command = malloc(5 * sizeof(char));
|
||||
testCommandEvent->arguments = malloc(15 * sizeof(char));
|
||||
strcpy(testCommandEvent->command, "Test");
|
||||
strcpy(testCommandEvent->arguments, "Test Arguments");
|
||||
|
||||
// Add them to the queue:
|
||||
printf("-=[ Adding items to the queue ]=-:\n");
|
||||
printf("- First item, Item count should be 1. Front and Back should be equal.\n");
|
||||
pushQueue(testQueue, testInputMessage, INPUT_MESSAGE);
|
||||
assert(testQueue->itemCount == 1);
|
||||
assert(testQueue->front == testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Second item, Item count should be 2. Front and Back should be not equal.\n");
|
||||
pushQueue(testQueue, testOutputMessage, OUTPUT_MESSAGE);
|
||||
assert(testQueue->itemCount == 2);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Third item, Item count should be 3. Front and Back should be not equal.\n");
|
||||
pushQueue(testQueue, testCommandEvent, COMMAND);
|
||||
assert(testQueue->itemCount == 3);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("-=[ Checking items and popping from queue ]=-:\n");
|
||||
printf("- First item peeked should point to testInputMessage.\n");
|
||||
assert(peekQueue(testQueue)->data.inputMessage == testInputMessage);
|
||||
printf("Peeked data is located at: %p, testInputMessage is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.inputMessage, testInputMessage);
|
||||
|
||||
printf("- Popping first item, Item count should be 2, Front and Back should not be equal.\n");
|
||||
popQueue(testQueue);
|
||||
assert(testQueue->itemCount == 2);
|
||||
assert(testQueue->front != testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Second item peeked should point to testOutputMessage.\n");
|
||||
assert(peekQueue(testQueue)->data.outputMessage == testOutputMessage);
|
||||
printf("Peeked data is located at: %p, testOutputMessage is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.outputMessage, testOutputMessage);
|
||||
|
||||
printf("- Popping second item, Item count should be 1, Front and Back should be equal.\n");
|
||||
popQueue(testQueue);
|
||||
assert(testQueue->itemCount == 1);
|
||||
assert(testQueue->front == testQueue->back);
|
||||
printf("Item count is: %d, Front and Back are %s.\n\n", testQueue->itemCount,
|
||||
formatEquality(testQueue->front == testQueue->back));
|
||||
|
||||
printf("- Third item peeked should point to testCommandEvent.\n");
|
||||
assert(peekQueue(testQueue)->data.command == testCommandEvent);
|
||||
printf("Peeked data is located at: %p, testCommandEvent is located at: %p.\n\n",
|
||||
peekQueue(testQueue)->data.command, testCommandEvent);
|
||||
|
||||
printf("- Popping third item:\n");
|
||||
popQueue(testQueue);
|
||||
|
||||
printf("- Item count should be 0:\n");
|
||||
assert(testQueue->itemCount == 0);
|
||||
printf("Item count is %d.\n\n", testQueue->itemCount);
|
||||
|
||||
printf("- Lock should be false:\n");
|
||||
assert(testQueue->lock == false);
|
||||
printf("Lock is %s.\n\n", formatBoolean(testQueue->lock));
|
||||
|
||||
printf("- Front should be (nil):\n");
|
||||
assert(testQueue->front == NULL);
|
||||
printf("Front is %p.\n\n", testQueue->front);
|
||||
|
||||
printf("- Back should be (nil):\n");
|
||||
assert(testQueue->back == NULL);
|
||||
printf("Back is %p.\n\n", testQueue->front);
|
||||
}
|
Loading…
Reference in New Issue