Merge branch 'dev'
This commit is contained in:
commit
b83dfb7782
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/>.
|
|
29
Makefile
29
Makefile
|
@ -1,29 +0,0 @@
|
||||||
CC = gcc
|
|
||||||
clientsrc = $(wildcard src/*.c) \
|
|
||||||
src/client/SilverMUDClient.c
|
|
||||||
clientobj = $(clientsrc:.c=.o)
|
|
||||||
serversrc = $(wildcard src/*.c) \
|
|
||||||
src/server/SilverMUDServer.c
|
|
||||||
serverobj = $(serversrc:.c=.o)
|
|
||||||
CLIENTLDFLAGS= -lpthread -lncurses -lgnutls
|
|
||||||
SERVERLDFLAGS= -lpthread -lncurses -lgnutls
|
|
||||||
SilverMUDClient: $(clientobj)
|
|
||||||
gcc -o $@ $^ $(CLIENTLDFLAGS)
|
|
||||||
|
|
||||||
SilverMUDServer: $(serverobj)
|
|
||||||
gcc -o $@ $^ $(SERVERLDFLAGS)
|
|
||||||
|
|
||||||
SilverMUDClientDebug: $(clientobj)
|
|
||||||
gcc -pg $^ $(CLIENTLDFLAGS) -o $@
|
|
||||||
|
|
||||||
SilverMUDServerDebug: $(serverobj)
|
|
||||||
gcc -pg $^ $(SERVERLDFLAGS) -o $@
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f $(clientobj) $(serverobj) SilverMUDClient SilverMUDServer SilverMUDClientDebug SilverMUDServerDebug
|
|
||||||
|
|
||||||
all: clean SilverMUDClient SilverMUDServer
|
|
||||||
all: CFLAGS += -Wall -Wextra -Ofast
|
|
||||||
debug: CFLAGS += -Wall -Wextra -pg -ggdb -Og -D debug
|
|
||||||
debug: clean SilverMUDClientDebug SilverMUDServerDebug
|
|
91
README.org
91
README.org
|
@ -1,91 +0,0 @@
|
||||||
#+LATEX_HEADER: \RequirePackage[left=0.3in,top=0.3in,right=0.3in,bottom=0.3in, a4paper]{geometry}
|
|
||||||
* SilverMUD: The Hackable Terminal-Top Roleplaying Game.
|
|
||||||
SilverMUD is a tool for creating engaging and communal stories, all over the
|
|
||||||
world through the internet. It's designed to give a gamemaster the same power
|
|
||||||
to improvise that they have at the table, through simple programming and
|
|
||||||
easy-to-understand structures.
|
|
||||||
|
|
||||||
* Player's Guide
|
|
||||||
** Running The Client
|
|
||||||
*** How To Connect To A Server:
|
|
||||||
To connect to a server, use the command-line option =-i=, and the IP address of
|
|
||||||
the server. If the server admin is hosting the server on a port other than the
|
|
||||||
default port of 5000, you can use the =-p= option with the number of the port. If
|
|
||||||
the connection is successful, you will be placed in the server's login
|
|
||||||
area. Type =/join <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. |
|
|
||||||
|
|
||||||
* Gamemaster's Guide
|
|
||||||
** Running the Server:
|
|
||||||
|
|
||||||
* Developer's Guide
|
|
||||||
** Build Prerequisites:
|
|
||||||
SilverMUD has the following dependencies:
|
|
||||||
- GnuTLS
|
|
||||||
- ncurses
|
|
||||||
|
|
||||||
** C Style Guide:
|
|
||||||
These rules attempt to make the program as visually clear as possible, while
|
|
||||||
some rules may be made based on my own personal tastes.
|
|
||||||
|
|
||||||
- () :: These are parentheses.
|
|
||||||
- [] :: These are brackets.
|
|
||||||
- {} :: These are braces.
|
|
||||||
*** Formatting:
|
|
||||||
**** Control Statements:
|
|
||||||
- A space should be between the keyword and the condition. This is to make
|
|
||||||
control statements visually distinct from function calls.
|
|
||||||
|
|
||||||
- Opening braces should be on the line after the control statement, and closing
|
|
||||||
braces on the line after the last statement, on it's own. This is to make the
|
|
||||||
scope of the control statement easily identifiable.
|
|
||||||
|
|
||||||
- else and else if should always be on a new line, not the same line as an if
|
|
||||||
statement's closing brace. This is to more easily distinguish the seperate
|
|
||||||
blocks.
|
|
||||||
|
|
||||||
- Control statements should never omit braces and do single statements. This is
|
|
||||||
mostly personal preference, but I do think it makes things more clear.
|
|
||||||
|
|
||||||
*** Naming:
|
|
||||||
**** Rule 0: NEVER USE i AND j!
|
|
||||||
Never use the variable names i and j. These are easy to confuse, and often make
|
|
||||||
nested loops awful to read. Name these more descriptively.
|
|
||||||
For example:
|
|
||||||
- If you are using a variable to index an array, name the variable index.
|
|
||||||
- If you are indexing multiple arrays, name it "array name + Index".
|
|
||||||
- If you are using it to count something, call it count, or "name of the
|
|
||||||
thing you are counting + count".
|
|
||||||
|
|
||||||
**** Rule 1: No one letter variable names, unless in a mathematical function.
|
|
||||||
You should never use one letter variable names. They're needlessly obtuse and
|
|
||||||
you will not remember their meaning upon re-reading of the source code. The
|
|
||||||
exception to this is when you are writing a function which replicates a
|
|
||||||
mathematical formula or function with commonly accepted notation. However, you
|
|
||||||
should consider if it would be better to break mathematical convention for
|
|
||||||
clarity inside the program, such as when the variable names are the first letter
|
|
||||||
of a word or the mathematical notation uses many similar looking variables.
|
|
||||||
|
|
||||||
**** Rule 2: Prefer to use full words in variable and function names:
|
|
||||||
You should always prefer to use full words in variable and function names. It
|
|
||||||
makes the source code much easier to read, like a sentence. Ideally, if you want
|
|
||||||
to shorten the name, use synonyms or rephrasing before you resort to removing
|
|
||||||
letters.
|
|
||||||
|
|
||||||
*** Comments:
|
|
|
@ -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"
|
|
||||||
#include "linkedlist.h"
|
|
||||||
|
|
||||||
// ====================
|
|
||||||
// -=[ Area/Paths: ]=-:
|
|
||||||
// ====================
|
|
||||||
|
|
||||||
// Let the compiler know that we're going to define these types:
|
|
||||||
typedef struct playerPath playerPath;
|
|
||||||
typedef struct playerArea playerArea;
|
|
||||||
|
|
||||||
// A path, which contains a name, and a pointer to the area which the player will travel to:
|
|
||||||
struct playerPath
|
|
||||||
{
|
|
||||||
char pathName[32];
|
|
||||||
playerArea * areaToJoin;
|
|
||||||
};
|
|
||||||
|
|
||||||
// An area, containing a list of paths exiting from the area, and a name and description of the area:
|
|
||||||
struct playerArea
|
|
||||||
{
|
|
||||||
list * pathList;
|
|
||||||
char areaName[32];
|
|
||||||
char areaDescription[MAX - 35];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an area given a name and description, returning a pointer to the new area:
|
|
||||||
playerArea * createArea(char * nameString, char * descriptionString);
|
|
||||||
|
|
||||||
// Create a path between two areas given two areas and two strings, adding it to the both area's list of paths:
|
|
||||||
int createPath(playerArea * fromArea, playerArea * toArea, char * fromDescription, char * toDescription);
|
|
||||||
|
|
||||||
// Create a one-way path between two areas given two areas and a string, adding it to the first area's list of paths:
|
|
||||||
int createOneWayPath(playerArea * fromArea, playerArea * toArea, char * description);
|
|
||||||
|
|
||||||
// TO BE IMPLEMENTED:
|
|
||||||
/* int saveAreaList(areaNode * listToSave); */
|
|
||||||
|
|
||||||
/* int savePathList(pathNode * listToSave); */
|
|
||||||
|
|
||||||
/* int loadAreaList(areaNode * listToLoad); */
|
|
||||||
|
|
||||||
/* int loadPathList(pathNode * listToLoad); */
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,360 +0,0 @@
|
||||||
// Silverkin Industries Comm-Link Client, Public Demonstration Sample Alpha 0.5.
|
|
||||||
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
|
||||||
// Barry Kane, 2021
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.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;
|
|
||||||
} threadparameters;
|
|
||||||
|
|
||||||
// Use sockaddr as a type:
|
|
||||||
typedef struct sockaddr sockaddr;
|
|
||||||
|
|
||||||
// A globally available exit boolean:
|
|
||||||
bool shouldExit = false;
|
|
||||||
|
|
||||||
// A function for managing the sending thread:
|
|
||||||
void * messageSender(void * parameters)
|
|
||||||
{
|
|
||||||
threadparameters * threadParameters = parameters;
|
|
||||||
gnutls_session_t tlsSession = threadParameters->tlsSession;
|
|
||||||
FILE * loggingStream = threadParameters->loggingStream;
|
|
||||||
bool loggingFlag = threadParameters->loggingFlag;
|
|
||||||
WINDOW * window = threadParameters->window;
|
|
||||||
userMessage sendBuffer;
|
|
||||||
|
|
||||||
// Repeatedly get input from the user, place it in a userMessage, and send it to the server:
|
|
||||||
while (!shouldExit)
|
|
||||||
{
|
|
||||||
// Print the prompt:
|
|
||||||
wprintw(window, "\n\n\nCOMM-LINK> ");
|
|
||||||
if (wgetnstr(window, sendBuffer.messageContent, MAX) == ERR)
|
|
||||||
{
|
|
||||||
// Quit if there's any funny business with getting input:
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore empty messages:
|
|
||||||
if (sendBuffer.messageContent[0] == '\n')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the message to the log if logging is enabled:
|
|
||||||
if (loggingFlag == true)
|
|
||||||
{
|
|
||||||
fputs(sendBuffer.messageContent, loggingStream);
|
|
||||||
fputs("\n", loggingStream);
|
|
||||||
fflush(loggingStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the message off to the server:
|
|
||||||
messageSend(tlsSession, &sendBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rejoin the main thread:
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A function for managing the receiving thread:
|
|
||||||
void * messageReceiver(void * parameters)
|
|
||||||
{
|
|
||||||
threadparameters * threadParameters = parameters;
|
|
||||||
gnutls_session_t tlsSession = threadParameters->tlsSession;
|
|
||||||
FILE * loggingStream = threadParameters->loggingStream;
|
|
||||||
int characterDelay = threadParameters->characterDelay;
|
|
||||||
bool loggingFlag = threadParameters->loggingFlag;
|
|
||||||
WINDOW * window = threadParameters->window;
|
|
||||||
|
|
||||||
int returnValue = 0;
|
|
||||||
userMessage receiveBuffer;
|
|
||||||
bool serverMessage = false;
|
|
||||||
int screenWidth = getmaxx(threadParameters->window);
|
|
||||||
|
|
||||||
// Repeatedly take messages from the server and print them to the chat log window:
|
|
||||||
while (!shouldExit)
|
|
||||||
{
|
|
||||||
// Get the next message:
|
|
||||||
returnValue = messageReceive(tlsSession, &receiveBuffer);
|
|
||||||
|
|
||||||
// Check we haven't been disconnected:
|
|
||||||
if (returnValue == -10 || returnValue == 0)
|
|
||||||
{
|
|
||||||
shouldExit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's a server message:
|
|
||||||
else if (receiveBuffer.senderName[0] == '\0')
|
|
||||||
{
|
|
||||||
// Check if it's a command to disconnect:
|
|
||||||
if (receiveBuffer.messageContent[0] == '\0')
|
|
||||||
{
|
|
||||||
shouldExit = true;
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fit the string to the screen:
|
|
||||||
wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1, screenWidth);
|
|
||||||
|
|
||||||
// If it's the first server message in a block, begin a block of server messages:
|
|
||||||
if (serverMessage == false)
|
|
||||||
{
|
|
||||||
slowPrintNcurses("\n --====<>====--", characterDelay, window, true);
|
|
||||||
serverMessage = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the message:
|
|
||||||
slowPrintNcurses("\n", characterDelay, window, true);
|
|
||||||
slowPrintNcurses(receiveBuffer.messageContent, characterDelay,
|
|
||||||
window, false);
|
|
||||||
slowPrintNcurses("\n", characterDelay, window, true);
|
|
||||||
}
|
|
||||||
// It's a user message:
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fit the string to the screen:
|
|
||||||
wrapString(receiveBuffer.messageContent, strlen(receiveBuffer.messageContent) - 1,
|
|
||||||
screenWidth - strlen(receiveBuffer.senderName) - 2);
|
|
||||||
|
|
||||||
// If the user has requested logging, insert the message into the file:
|
|
||||||
if (loggingFlag == true)
|
|
||||||
{
|
|
||||||
fputs(receiveBuffer.senderName, loggingStream);
|
|
||||||
fputs(": ", loggingStream);
|
|
||||||
fputs(receiveBuffer.messageContent, loggingStream);
|
|
||||||
fflush(loggingStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're in a block of server messages, end it:
|
|
||||||
if (serverMessage == true)
|
|
||||||
{
|
|
||||||
slowPrintNcurses("\n --====<>====-- \n", characterDelay, window, true);
|
|
||||||
serverMessage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the message:
|
|
||||||
slowPrintNcurses(receiveBuffer.senderName, characterDelay, window, true);
|
|
||||||
slowPrintNcurses(": ", characterDelay, window, true);
|
|
||||||
slowPrintNcurses(receiveBuffer.messageContent, characterDelay, window, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Exit the thread if shouldExit is true:
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
|
||||||
{
|
|
||||||
int socketFileDesc;
|
|
||||||
struct sockaddr_in serverAddress;
|
|
||||||
pthread_t sendingThread;
|
|
||||||
pthread_t receivingThread;
|
|
||||||
int port = 5000;
|
|
||||||
int currentopt = 0;
|
|
||||||
int characterDelay = 4000;
|
|
||||||
char chatLogPath[PATH_MAX + 1];
|
|
||||||
char gameLogPath[PATH_MAX + 1];
|
|
||||||
char ipAddress[32] = "127.0.0.1";
|
|
||||||
FILE * chatLog = NULL, * gameLog = NULL;
|
|
||||||
bool chatLogging = false, gameLogging = false;
|
|
||||||
|
|
||||||
// Print welcome message:
|
|
||||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK CLIENT ====--\nVersion Alpha 0.5\n", 5000);
|
|
||||||
|
|
||||||
// Parse command-line options:
|
|
||||||
while ((currentopt = getopt(argc, argv, "i:c:g:p:d:")) != -1)
|
|
||||||
{
|
|
||||||
switch (currentopt)
|
|
||||||
{
|
|
||||||
case 'i':
|
|
||||||
{
|
|
||||||
memcpy(ipAddress, optarg, 32);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c':
|
|
||||||
{
|
|
||||||
memcpy(chatLogPath, optarg, PATH_MAX + 1);
|
|
||||||
chatLog = fopen(chatLogPath, "a+");
|
|
||||||
if (chatLog == NULL)
|
|
||||||
{
|
|
||||||
chatLogging = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chatLogging = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'g':
|
|
||||||
{
|
|
||||||
memcpy(gameLogPath, optarg, PATH_MAX + 1);
|
|
||||||
gameLog = fopen(gameLogPath, "a+");
|
|
||||||
if (gameLog == NULL)
|
|
||||||
{
|
|
||||||
gameLogging = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gameLogging = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'p':
|
|
||||||
{
|
|
||||||
port = atoi(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'd':
|
|
||||||
{
|
|
||||||
characterDelay = atoi(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '?':
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give me a socket, and make sure it's working:
|
|
||||||
socketFileDesc = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (socketFileDesc == -1)
|
|
||||||
{
|
|
||||||
printf("Socket creation failed.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slowPrint("Socket successfully created.\n", characterDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set our IP address and port. Default to localhost for testing:
|
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
serverAddress.sin_addr.s_addr = inet_addr(ipAddress);
|
|
||||||
serverAddress.sin_port = htons(port);
|
|
||||||
|
|
||||||
// Connect the server and client sockets, Kronk:
|
|
||||||
if (connect(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress)) != 0)
|
|
||||||
{
|
|
||||||
slowPrint("Connection with the Silverkin Industries Comm-Link Server Failed:\nPlease contact your service representative.\n", characterDelay);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a GnuTLS session and initialize it:
|
|
||||||
gnutls_session_t tlsSession = NULL;
|
|
||||||
if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0)
|
|
||||||
{
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the private credentials for our GnuTLS session:
|
|
||||||
gnutls_anon_client_credentials_t clientkey = NULL;
|
|
||||||
gnutls_anon_allocate_client_credentials(&clientkey);
|
|
||||||
gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientkey);
|
|
||||||
|
|
||||||
// Bind the open socket to the TLS session:
|
|
||||||
gnutls_transport_set_int(tlsSession, socketFileDesc);
|
|
||||||
gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL);
|
|
||||||
|
|
||||||
// Use the default for the GnuTLS handshake timeout:
|
|
||||||
gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
|
||||||
|
|
||||||
// Repeatedly attempt to handshake unless we encounter a fatal error:
|
|
||||||
int returnValue = -1;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
returnValue = gnutls_handshake(tlsSession);
|
|
||||||
}
|
|
||||||
while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0);
|
|
||||||
|
|
||||||
// Setup Ncurses:
|
|
||||||
initscr();
|
|
||||||
|
|
||||||
// Create two pointers to structs to pass arguments to the threads:
|
|
||||||
threadparameters * logArea;
|
|
||||||
threadparameters * messageArea;
|
|
||||||
|
|
||||||
logArea = malloc(sizeof(*logArea));
|
|
||||||
messageArea = malloc(sizeof(*messageArea));
|
|
||||||
|
|
||||||
// Make the windows for the structs, and pass the socket descriptor:
|
|
||||||
logArea->window = newwin(LINES - 5, COLS - 2, 1, 1);
|
|
||||||
logArea->tlsSession = tlsSession;
|
|
||||||
logArea->loggingFlag = chatLogging;
|
|
||||||
logArea->characterDelay = characterDelay;
|
|
||||||
if (chatLog != NULL)
|
|
||||||
{
|
|
||||||
logArea->loggingStream = chatLog;
|
|
||||||
}
|
|
||||||
messageArea->window = newwin(3, COLS - 2, LINES - 4, 1);
|
|
||||||
messageArea->tlsSession = tlsSession;
|
|
||||||
messageArea->loggingFlag = gameLogging;
|
|
||||||
|
|
||||||
// Set the appropriate log pointers:
|
|
||||||
if (gameLog != NULL)
|
|
||||||
{
|
|
||||||
messageArea->loggingStream = gameLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the two windows to scroll:
|
|
||||||
scrollok(logArea->window, true);
|
|
||||||
scrollok(messageArea->window, true);
|
|
||||||
|
|
||||||
// Run a thread to send messages, and use another to recieve:
|
|
||||||
pthread_create(&sendingThread, NULL, messageSender, messageArea);
|
|
||||||
pthread_create(&receivingThread, NULL, messageReceiver, logArea);
|
|
||||||
|
|
||||||
// Wait for /EXIT:
|
|
||||||
pthread_join(receivingThread, NULL);
|
|
||||||
|
|
||||||
// Close the threads:
|
|
||||||
pthread_cancel(sendingThread);
|
|
||||||
|
|
||||||
// Close the session and socket:
|
|
||||||
gnutls_bye(tlsSession, GNUTLS_SHUT_WR);
|
|
||||||
close(socketFileDesc);
|
|
||||||
|
|
||||||
// Free the structs:
|
|
||||||
free(logArea);
|
|
||||||
free(messageArea);
|
|
||||||
|
|
||||||
// Close the log files:
|
|
||||||
if (gameLog != NULL)
|
|
||||||
{
|
|
||||||
fclose(gameLog);
|
|
||||||
}
|
|
||||||
if (chatLog != NULL)
|
|
||||||
{
|
|
||||||
fclose(chatLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsetup Ncurses:
|
|
||||||
endwin();
|
|
||||||
|
|
||||||
// Say goodbye:
|
|
||||||
slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", characterDelay);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
790
src/gamelogic.c
790
src/gamelogic.c
|
@ -1,790 +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 "queue.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "gamelogic.h"
|
|
||||||
#include "playerdata.h"
|
|
||||||
#include "linkedlist.h"
|
|
||||||
#include "inputoutput.h"
|
|
||||||
|
|
||||||
// =======================
|
|
||||||
// -=[ Main Game Loop ]=-:
|
|
||||||
// =======================
|
|
||||||
|
|
||||||
// Thread function which runs the main game loop, given the needed parameters:
|
|
||||||
void * gameLogicHandler(void * parameters)
|
|
||||||
{
|
|
||||||
gameLogicParameters * threadParameters = parameters;
|
|
||||||
inputMessage * currentInput = NULL;
|
|
||||||
queue * commandQueue = createQueue();
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// Evaluate remaining commands:
|
|
||||||
while (commandQueue->itemCount != 0)
|
|
||||||
{
|
|
||||||
evaluateNextCommand(threadParameters, commandQueue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait if there is nothing to do:
|
|
||||||
if (threadParameters->inputQueue->itemCount == 0)
|
|
||||||
{
|
|
||||||
pthread_cond_wait(&threadParameters->inputQueue->condition, &threadParameters->inputQueue->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for new messages and pop them off the queue:
|
|
||||||
if (threadParameters->inputQueue->itemCount != 0)
|
|
||||||
{
|
|
||||||
while (threadParameters->inputQueue->lock == true);
|
|
||||||
threadParameters->inputQueue->lock = true;
|
|
||||||
currentInput = peekQueue(threadParameters->inputQueue)->data.inputMessage;
|
|
||||||
userInputSanatize(currentInput->content->messageContent, MAX);
|
|
||||||
// A slash as the first character means the message is a user command:
|
|
||||||
if (currentInput->content->messageContent[0] == '/')
|
|
||||||
{
|
|
||||||
queueMessagedCommand(commandQueue, currentInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!(currentInput->sender->currentArea == getFromList(threadParameters->areaList, 0)->area) &&
|
|
||||||
currentInput->content->messageContent[0] != '\n')
|
|
||||||
{
|
|
||||||
// Copy the correct name into the sender name field:
|
|
||||||
strncpy(currentInput->content->senderName, currentInput->sender->playerName, 32);
|
|
||||||
currentInput->content->senderName[31] = '\0';
|
|
||||||
|
|
||||||
// Allocate an array of playerInfo to store the current players in the area for the output message:
|
|
||||||
playerInfo ** recipients = malloc(sizeof(playerInfo*) * PLAYERCOUNT);
|
|
||||||
|
|
||||||
// Initialize them all to NULL:
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
recipients[index] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the players in the current area and add them to our array:
|
|
||||||
int recipientIndex = 0;
|
|
||||||
for (int playerIndex = 0; playerIndex < *threadParameters->playerCount; playerIndex++)
|
|
||||||
{
|
|
||||||
if (threadParameters->connectedPlayers[playerIndex].currentArea == currentInput->sender->currentArea)
|
|
||||||
{
|
|
||||||
recipients[recipientIndex] = &threadParameters->connectedPlayers[playerIndex];
|
|
||||||
recipientIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the outputMessage for the queue:
|
|
||||||
outputMessage * newOutputMessage = createTargetedOutputMessage(currentInput->content, recipients, recipientIndex);
|
|
||||||
|
|
||||||
// Push the message onto the queue:
|
|
||||||
pushQueue(threadParameters->outputQueue, newOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
// Free the array;
|
|
||||||
free(recipients);
|
|
||||||
}
|
|
||||||
currentInput = NULL;
|
|
||||||
threadParameters->inputQueue->lock = false;
|
|
||||||
popQueue(threadParameters->inputQueue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the next commandEvent in a queue:
|
|
||||||
void queueMessagedCommand(queue * queue, inputMessage * messageToQueue)
|
|
||||||
{
|
|
||||||
// Prepare the new commandEvent:
|
|
||||||
commandEvent * newCommand = calloc(1, sizeof(commandEvent));
|
|
||||||
newCommand->command = calloc(16, sizeof(char));
|
|
||||||
newCommand->arguments = calloc(MAX, sizeof(char));
|
|
||||||
newCommand->caller = messageToQueue->sender;
|
|
||||||
|
|
||||||
// Seperate the command from it's arguments:
|
|
||||||
strtok(messageToQueue->content->messageContent, " ");
|
|
||||||
|
|
||||||
// Copy the command and arguments to the new commandEvent:
|
|
||||||
memcpy(newCommand->command, &messageToQueue->content->messageContent[1], 16);
|
|
||||||
memcpy(newCommand->arguments, &messageToQueue->content->messageContent[strlen(newCommand->command) + 2],
|
|
||||||
MAX - (strlen(newCommand->command) + 2));
|
|
||||||
|
|
||||||
// Ensure the arguments are safe to parse, without adding newlines:
|
|
||||||
userNameSanatize(newCommand->command, 16);
|
|
||||||
newCommand->command[15] = '\0';
|
|
||||||
|
|
||||||
userNameSanatize(newCommand->arguments, MAX);
|
|
||||||
newCommand->arguments[MAX - 1] = '\0';
|
|
||||||
|
|
||||||
// Lowercase the command for easier comparison:
|
|
||||||
for (char * character = newCommand->command; *character; ++character)
|
|
||||||
{
|
|
||||||
*character = tolower(*character);
|
|
||||||
}
|
|
||||||
|
|
||||||
pushQueue(queue, newCommand, COMMAND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enqueue a command to a queue:
|
|
||||||
void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength,
|
|
||||||
playerInfo * callingPlayer)
|
|
||||||
{
|
|
||||||
// Prepare the new commandEvent:
|
|
||||||
commandEvent * newCommand = calloc(1, sizeof(commandEvent));
|
|
||||||
newCommand->command = calloc(16, sizeof(char));
|
|
||||||
newCommand->arguments = calloc(MAX, sizeof(char));
|
|
||||||
newCommand->caller = callingPlayer;
|
|
||||||
|
|
||||||
// Copy the command and arguments:
|
|
||||||
strncpy(newCommand->command, command, commandLength);
|
|
||||||
if (argumentsLength > 0)
|
|
||||||
{
|
|
||||||
strncpy(newCommand->arguments, arguments, argumentsLength);
|
|
||||||
}
|
|
||||||
// Ensure the arguments are safe to parse, without adding newlines:
|
|
||||||
userNameSanatize(newCommand->command, 16);
|
|
||||||
|
|
||||||
pushQueue(queue, newCommand, COMMAND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the next commandEvent in a queue:
|
|
||||||
int evaluateNextCommand(gameLogicParameters * parameters, queue * queue)
|
|
||||||
{
|
|
||||||
commandEvent * currentCommand = peekQueue(queue)->data.command;
|
|
||||||
while (queue->lock);
|
|
||||||
queue->lock = true;
|
|
||||||
if (currentCommand == NULL)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the command and execute the relevant functionality:
|
|
||||||
switch (hashCommand(currentCommand->command, strlen(currentCommand->command)))
|
|
||||||
{
|
|
||||||
// Look command: Returns the description of the current area and paths:
|
|
||||||
case 5626697:
|
|
||||||
{
|
|
||||||
char formattedString[64];
|
|
||||||
userMessage * lookMessage = calloc(1, sizeof(userMessage));
|
|
||||||
lookMessage->senderName[0] = '\0';
|
|
||||||
strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaName, 33);
|
|
||||||
strncat(lookMessage->messageContent, "\n", 2);
|
|
||||||
strncat(lookMessage->messageContent, currentCommand->caller->currentArea->areaDescription, MAX - 35);
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
//queueTargetedOutputMessage(parameters->outputQueue, lookMessage, ¤tCommand->caller, 1);
|
|
||||||
bzero(lookMessage, sizeof(userMessage));
|
|
||||||
|
|
||||||
// Loop through the paths and send the appropriate amount of messages:
|
|
||||||
int charCount = 13;
|
|
||||||
strncat(lookMessage->messageContent, "You can go:", 13);
|
|
||||||
|
|
||||||
if (currentCommand->caller->currentArea->pathList->itemCount > 0)
|
|
||||||
{
|
|
||||||
for(size_t index = 0; index < currentCommand->caller->currentArea->pathList->itemCount; index++)
|
|
||||||
{
|
|
||||||
if ((charCount + 64) >= MAX)
|
|
||||||
{
|
|
||||||
lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
bzero(lookMessage, sizeof(userMessage));
|
|
||||||
charCount = 0;
|
|
||||||
}
|
|
||||||
snprintf(formattedString, 64, "\n\t%ld. %s", index + 1,
|
|
||||||
getFromList(currentCommand->caller->currentArea->pathList, index)->path->pathName);
|
|
||||||
strncat(lookMessage->messageContent, formattedString, 64);
|
|
||||||
charCount += 64;
|
|
||||||
}
|
|
||||||
// Allocate another outputMessage for the queue:
|
|
||||||
lookOutputMessage = createTargetedOutputMessage(lookMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, lookOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
}
|
|
||||||
free(lookMessage);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat command: Displays the current character's sheet.
|
|
||||||
case 5987604:
|
|
||||||
{
|
|
||||||
char * formattedString = calloc(121, sizeof(char));
|
|
||||||
userMessage * statMessage = calloc(1, sizeof(userMessage));
|
|
||||||
statMessage->senderName[0] = '\0';
|
|
||||||
// Basic status: Name, level, location.
|
|
||||||
snprintf(formattedString, 120, "%s, Level %d | %s\n", currentCommand->caller->playerName,
|
|
||||||
currentCommand->caller->stats->level, currentCommand->caller->currentArea->areaName);
|
|
||||||
strncat(statMessage->messageContent, formattedString, 120);
|
|
||||||
|
|
||||||
// Current stats: Health and WISED.
|
|
||||||
snprintf(formattedString, 120,
|
|
||||||
"Health: %d/%d\nStats:\n\tWits: %2d | Intellect: %2d | Strength: %2d | Endurance: %2d | Dexerity: %2d \n",
|
|
||||||
currentCommand->caller->stats->currentHealth, currentCommand->caller->stats->maxHealth,
|
|
||||||
currentCommand->caller->stats->wits, currentCommand->caller->stats->intellect,
|
|
||||||
currentCommand->caller->stats->strength, currentCommand->caller->stats->endurance,
|
|
||||||
currentCommand->caller->stats->dexerity);
|
|
||||||
strncat(statMessage->messageContent, formattedString, 120);
|
|
||||||
|
|
||||||
// Levelling stats: Current XP, and spec points.
|
|
||||||
if (currentCommand->caller->stats->specPoints > 0 || currentCommand->caller->stats->skillPoints > 0)
|
|
||||||
{
|
|
||||||
snprintf(formattedString, 120, "Current Experience: %ld | Spec Points Available: %d | Skill Points Available: %d",
|
|
||||||
currentCommand->caller->stats->experience, currentCommand->caller->stats->specPoints, currentCommand->caller->stats->skillPoints);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(formattedString, 120, "Current Experience: %ld", currentCommand->caller->stats->experience);
|
|
||||||
}
|
|
||||||
strncat(statMessage->messageContent, formattedString, 120);
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
bzero(statMessage->messageContent, sizeof(char) * MAX);
|
|
||||||
if (currentCommand->caller->skills->head != NULL)
|
|
||||||
{
|
|
||||||
size_t skillIndex = 0;
|
|
||||||
int charCount = 0;
|
|
||||||
bool addNewline = false;
|
|
||||||
playerSkill * skill;
|
|
||||||
while (skillIndex < currentCommand->caller->skills->itemCount)
|
|
||||||
{
|
|
||||||
skill = getFromList(currentCommand->caller->skills, skillIndex)->skill;
|
|
||||||
skillIndex++;
|
|
||||||
snprintf(formattedString, 120, "| %2d | %31s ", skill->skillPoints, skill->skillName);
|
|
||||||
charCount += 43;
|
|
||||||
strncat(statMessage->messageContent, formattedString, 120);
|
|
||||||
if ((charCount + 43) >= MAX)
|
|
||||||
{
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
bzero(statMessage, sizeof(userMessage));
|
|
||||||
charCount = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (addNewline)
|
|
||||||
{
|
|
||||||
strncat(statMessage->messageContent, "|\n", 3);
|
|
||||||
charCount++;
|
|
||||||
addNewline = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
addNewline = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
statOutputMessage = createTargetedOutputMessage(statMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, statOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
}
|
|
||||||
free(statMessage);
|
|
||||||
free(formattedString);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spec command: Assign spec points to stats:
|
|
||||||
case 5982259:
|
|
||||||
{
|
|
||||||
userMessage * specMessage = calloc(1, sizeof(userMessage));
|
|
||||||
specMessage->senderName[0] = '\0';
|
|
||||||
char * formattedString = calloc(121, sizeof(char));
|
|
||||||
if (currentCommand->caller->stats->specPoints > 0)
|
|
||||||
{
|
|
||||||
int selectedAmount = 0;
|
|
||||||
strtok(currentCommand->arguments, " ");
|
|
||||||
selectedAmount = atoi(¤tCommand->arguments[strlen(currentCommand->arguments) + 1]);
|
|
||||||
coreStat selectedStat = getCoreStatFromString(currentCommand->arguments, 16);
|
|
||||||
if (selectedAmount > 0 && (currentCommand->caller->stats->specPoints - selectedAmount) >= 0)
|
|
||||||
{
|
|
||||||
switch (selectedStat)
|
|
||||||
{
|
|
||||||
case WITS:
|
|
||||||
{
|
|
||||||
currentCommand->caller->stats->wits += selectedAmount;
|
|
||||||
strncat(specMessage->messageContent, "Increased wits.", 16);
|
|
||||||
currentCommand->caller->stats->specPoints -= selectedAmount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INTELLECT:
|
|
||||||
{
|
|
||||||
currentCommand->caller->stats->intellect += selectedAmount;
|
|
||||||
strncat(specMessage->messageContent, "Increased intellect.", 21);
|
|
||||||
currentCommand->caller->stats->specPoints -= selectedAmount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STRENGTH:
|
|
||||||
{
|
|
||||||
currentCommand->caller->stats->strength += selectedAmount;
|
|
||||||
strncat(specMessage->messageContent, "Increased strength.", 20);
|
|
||||||
currentCommand->caller->stats->specPoints -= selectedAmount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ENDURANCE:
|
|
||||||
{
|
|
||||||
currentCommand->caller->stats->endurance += selectedAmount;
|
|
||||||
strncat(specMessage->messageContent, "Increased endurance.", 21);
|
|
||||||
currentCommand->caller->stats->specPoints -= selectedAmount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DEXERITY:
|
|
||||||
{
|
|
||||||
currentCommand->caller->stats->dexerity += selectedAmount;
|
|
||||||
strncat(specMessage->messageContent, "Increased dexerity.", 21);
|
|
||||||
currentCommand->caller->stats->specPoints -= selectedAmount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INVALID:
|
|
||||||
{
|
|
||||||
strncat(specMessage->messageContent, "Invalid stat.", 21);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strncat(specMessage->messageContent, "You have entered an invalid amount of spec points.", 51);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strncat(specMessage->messageContent, "You have no spec points available.", 35);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * specOutputMessage = createTargetedOutputMessage(specMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, specOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
// Show the new stat sheet:
|
|
||||||
queue->lock = false;
|
|
||||||
queueCommand(queue, "stat", "", 5, 0, currentCommand->caller);
|
|
||||||
queue->lock = true;
|
|
||||||
|
|
||||||
// Free the finished message:
|
|
||||||
free(specMessage);
|
|
||||||
free(formattedString);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try command: Attempt to use a stat or skill on an object:
|
|
||||||
case 163143:
|
|
||||||
{
|
|
||||||
// Allocate the userMessage to send:
|
|
||||||
userMessage * tryMessage = malloc(sizeof(userMessage));
|
|
||||||
tryMessage->senderName[0] = '\0';
|
|
||||||
|
|
||||||
// Temporary message until we can implement objects, events, and challenges.
|
|
||||||
strcpy(tryMessage->messageContent, "The try command is currently not implemented. Implement it if you want to use it.\n");
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * tryOutputMessage = createTargetedOutputMessage(tryMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, tryOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
// Free the userMessage:
|
|
||||||
free(tryMessage);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move command: Moves the caller to a different area given a path name or number:
|
|
||||||
case 5677603:
|
|
||||||
{
|
|
||||||
char requestedPath[32];
|
|
||||||
if (strlen(currentCommand->arguments) > 0 && currentCommand->caller->currentArea != getFromList(parameters->areaList, 0)->area)
|
|
||||||
{
|
|
||||||
memcpy(requestedPath, currentCommand->arguments, 32);
|
|
||||||
userNameSanatize(requestedPath, 32);
|
|
||||||
requestedPath[31] = '\0';
|
|
||||||
if (movePlayerToArea(currentCommand->caller, requestedPath) == 0)
|
|
||||||
{
|
|
||||||
// Call the look command after moving. It's fine to unlock, because the loop won't
|
|
||||||
// continue until the command is queued:
|
|
||||||
queue->lock = false;
|
|
||||||
queueCommand(queue, "look", "", 5, 0, currentCommand->caller);
|
|
||||||
queue->lock = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skill command: Allows you to put skill points into skills:
|
|
||||||
case 221096235:
|
|
||||||
{
|
|
||||||
userMessage * skillMessage = calloc(1, sizeof(userMessage));
|
|
||||||
skillMessage->senderName[0] = '\0';
|
|
||||||
if ((currentCommand->caller->stats->skillPoints - 1) >= 0)
|
|
||||||
{
|
|
||||||
int returnValue = takeSkill(parameters->globalSkillList, currentCommand->arguments,
|
|
||||||
strlen(currentCommand->arguments), currentCommand->caller);
|
|
||||||
switch(returnValue)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
{
|
|
||||||
strcpy(skillMessage->messageContent, "Not a valid skill.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
strcpy(skillMessage->messageContent, "Took ");
|
|
||||||
strcat(skillMessage->messageContent, currentCommand->arguments);
|
|
||||||
strcat(skillMessage->messageContent, ".");
|
|
||||||
currentCommand->caller->stats->skillPoints--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strcpy(skillMessage->messageContent, "You don't have enough skill points to take this skill.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * skillOutputMessage = createTargetedOutputMessage(skillMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, skillOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
free(skillMessage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listskills commands: List all available skills on the server:
|
|
||||||
case 2395990522:
|
|
||||||
{
|
|
||||||
userMessage * listMessage = calloc(1, sizeof(userMessage));
|
|
||||||
char * formattedString = calloc(121, sizeof(char));
|
|
||||||
int charCount = 0;
|
|
||||||
size_t skillIndex = 0;
|
|
||||||
bool addNewline = false;
|
|
||||||
playerSkill * currentSkill;
|
|
||||||
while (skillIndex < parameters->globalSkillList->itemCount)
|
|
||||||
{
|
|
||||||
currentSkill = getFromList(parameters->globalSkillList, skillIndex)->skill;
|
|
||||||
snprintf(formattedString, 120, "| %-31s ", currentSkill->skillName);
|
|
||||||
charCount += 43;
|
|
||||||
strncat(listMessage->messageContent, formattedString, 120);
|
|
||||||
if ((charCount + 46) >= MAX)
|
|
||||||
{
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
bzero(listMessage, sizeof(userMessage));
|
|
||||||
charCount = 0;
|
|
||||||
addNewline = false;
|
|
||||||
}
|
|
||||||
else if (addNewline)
|
|
||||||
{
|
|
||||||
strncat(listMessage->messageContent, "|\n", 3);
|
|
||||||
charCount++;
|
|
||||||
addNewline = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
addNewline = true;
|
|
||||||
}
|
|
||||||
skillIndex++;
|
|
||||||
}
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * listOutputMessage = createTargetedOutputMessage(listMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, listOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
free(listMessage);
|
|
||||||
free(formattedString);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Talk command: Allows the player to begin a chat session with another player:
|
|
||||||
case 601264:
|
|
||||||
{
|
|
||||||
userMessage * talkMessage = malloc(sizeof(userMessage));
|
|
||||||
talkMessage->senderName[0] = '\0';
|
|
||||||
|
|
||||||
// Temporary message until we can implement objects, events, and challenges.
|
|
||||||
strcpy(talkMessage->messageContent, "The talk command is currently not implemented. Implement it if you want to use it.\n");
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * talkOutputMessage = createTargetedOutputMessage(talkMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, talkOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
// Free the userMessage:
|
|
||||||
free(talkMessage);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit command: Sends an "empty" exit message to disconnect a client:
|
|
||||||
case 5284234:
|
|
||||||
{
|
|
||||||
// Allocate a userMessage containing null characters as the first char in both fields:
|
|
||||||
userMessage * exitMessage = malloc(sizeof(userMessage));
|
|
||||||
exitMessage->senderName[0] = '\0';
|
|
||||||
exitMessage->messageContent[0] = '\0';
|
|
||||||
|
|
||||||
// Allocate an outputMessage for the queue:
|
|
||||||
outputMessage * exitOutputMessage = createTargetedOutputMessage(exitMessage, ¤tCommand->caller, 1);
|
|
||||||
|
|
||||||
// Queue the outputMessage:
|
|
||||||
pushQueue(parameters->outputQueue, exitOutputMessage, OUTPUT_MESSAGE);
|
|
||||||
|
|
||||||
// Free the userMessage
|
|
||||||
free(exitMessage);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join command: Allows the player to join the game given a name:
|
|
||||||
// TODO: Implement login/character creation. Will be a while:
|
|
||||||
case 5525172:
|
|
||||||
{
|
|
||||||
if (currentCommand->caller->currentArea == getFromList(parameters->areaList, 0)->area)
|
|
||||||
{
|
|
||||||
bool validName = true;
|
|
||||||
for(int index = 0; index < *parameters->playerCount; index++)
|
|
||||||
{
|
|
||||||
if (currentCommand->arguments[0] == '\0')
|
|
||||||
{
|
|
||||||
validName = false;
|
|
||||||
}
|
|
||||||
if (strncmp(currentCommand->arguments, parameters->connectedPlayers[index].playerName, 16) == 0)
|
|
||||||
{
|
|
||||||
validName = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (validName)
|
|
||||||
{
|
|
||||||
strncpy(currentCommand->caller->playerName, currentCommand->arguments, 16);
|
|
||||||
currentCommand->caller->currentArea = getFromList(parameters->areaList, 1)->area;
|
|
||||||
// Call the look command after joining. It's fine to unlock, because the loop won't
|
|
||||||
// continue until the command is queued:
|
|
||||||
queue->lock = false;
|
|
||||||
queueCommand(queue, "look", "", 5, 0, currentCommand->caller);
|
|
||||||
queue->lock = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the current command and unlock the queue:
|
|
||||||
currentCommand = NULL;
|
|
||||||
queue->lock = false;
|
|
||||||
popQueue(queue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run a stat check for the given player, returning an outcome:
|
|
||||||
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck)
|
|
||||||
{
|
|
||||||
// Calculate the chance:
|
|
||||||
if (chance > 100 || chance < 0)
|
|
||||||
{
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
chance = 100 - chance;
|
|
||||||
|
|
||||||
// Calculate the modifier:
|
|
||||||
int modifier = 0;
|
|
||||||
switch(statToCheck)
|
|
||||||
{
|
|
||||||
case WITS:
|
|
||||||
{
|
|
||||||
modifier = player->stats->wits * 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INTELLECT:
|
|
||||||
{
|
|
||||||
modifier = player->stats->intellect * 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STRENGTH:
|
|
||||||
{
|
|
||||||
modifier = player->stats->strength * 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ENDURANCE:
|
|
||||||
{
|
|
||||||
modifier = player->stats->endurance * 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DEXERITY:
|
|
||||||
{
|
|
||||||
modifier = player->stats->dexerity * 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int attempt = (random() % 100) + modifier;
|
|
||||||
if (attempt >= chance)
|
|
||||||
{
|
|
||||||
if (attempt >= 98)
|
|
||||||
{
|
|
||||||
return CRITICAL_SUCCESS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (attempt <= 2)
|
|
||||||
{
|
|
||||||
return CRITICAL_FAILURE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run a skill check for the given player, returning an outcome:
|
|
||||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList)
|
|
||||||
{
|
|
||||||
// Calculate the chance:
|
|
||||||
if (chance > 100 || chance < 0)
|
|
||||||
{
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
chance = 100 - chance;
|
|
||||||
|
|
||||||
// Check if the player has the given skill:
|
|
||||||
bool playerHasSkill = false;
|
|
||||||
size_t playerIndex = 0;
|
|
||||||
while (playerIndex < player->skills->itemCount)
|
|
||||||
{
|
|
||||||
if (strncmp(skillName, getFromList(player->skills, playerIndex)->skill->skillName, skillNameLength) != 0)
|
|
||||||
{
|
|
||||||
playerHasSkill = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
playerIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the player doesn't have the skill, check if it's in the game and is trained:
|
|
||||||
bool trainedSkill = false;
|
|
||||||
size_t globalIndex = 0;
|
|
||||||
while (globalIndex < globalSkillList->itemCount)
|
|
||||||
{
|
|
||||||
if (strncmp(skillName, getFromList(globalSkillList, globalIndex)->skill->skillName, skillNameLength) != 0)
|
|
||||||
{
|
|
||||||
trainedSkill = getFromList(globalSkillList, globalIndex)->skill->trainedSkill;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
globalIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the modifier:
|
|
||||||
int modifier = 0;
|
|
||||||
if (trainedSkill)
|
|
||||||
{
|
|
||||||
modifier = -100;
|
|
||||||
}
|
|
||||||
else if (playerHasSkill)
|
|
||||||
{
|
|
||||||
modifier = getFromList(player->skills, playerIndex)->skill->skillModifier * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt the check:
|
|
||||||
int attempt = (random() % 100) + modifier;
|
|
||||||
if (attempt >= chance)
|
|
||||||
{
|
|
||||||
if (attempt >= 98)
|
|
||||||
{
|
|
||||||
return CRITICAL_SUCCESS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (attempt <= 2)
|
|
||||||
{
|
|
||||||
return CRITICAL_FAILURE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move a player along a path in their current area:
|
|
||||||
int movePlayerToArea(playerInfo * player, char * requestedPath)
|
|
||||||
{
|
|
||||||
// Check if a number was given first:
|
|
||||||
size_t selected = atoi(requestedPath);
|
|
||||||
if (selected != 0 && !(selected > player->currentArea->pathList->itemCount))
|
|
||||||
{
|
|
||||||
if (getFromList(player->currentArea->pathList, selected - 1)->path != NULL &&
|
|
||||||
getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin != NULL)
|
|
||||||
{
|
|
||||||
player->currentArea = getFromList(player->currentArea->pathList, selected - 1)->path->areaToJoin;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise search for the description:
|
|
||||||
for (size_t index = 0; index < player->currentArea->pathList->itemCount; index++)
|
|
||||||
{
|
|
||||||
if (strncmp(getFromList(player->currentArea->pathList, index)->path->pathName,
|
|
||||||
requestedPath, 32) == 0)
|
|
||||||
{
|
|
||||||
player->currentArea = getFromList(player->currentArea->pathList, index)->path->areaToJoin;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A hash function for distinguishing commands for the game logic handler:
|
|
||||||
unsigned int hashCommand(char * command, unsigned int commandLength)
|
|
||||||
{
|
|
||||||
unsigned int hash = 0;
|
|
||||||
char * currentCharacter = command;
|
|
||||||
|
|
||||||
for (unsigned int index = 0; index < commandLength && *currentCharacter != '\0'; currentCharacter++)
|
|
||||||
{
|
|
||||||
hash = 37 * hash + *currentCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
// gamelogic.h: Function prototypes and data-structures for dealing with game logic.
|
|
||||||
// Barry Kane, 2022.
|
|
||||||
#ifndef GAMELOGIC_H
|
|
||||||
#define GAMELOGIC_H
|
|
||||||
#include "queue.h"
|
|
||||||
#include "areadata.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "playerdata.h"
|
|
||||||
#include "inputoutput.h"
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Data Structures ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// An event for storing the information needed to evaluate a command:
|
|
||||||
typedef struct commandEvent commandEvent;
|
|
||||||
typedef struct commandEvent
|
|
||||||
{
|
|
||||||
playerInfo * caller;
|
|
||||||
commandEvent * next;
|
|
||||||
char * command;
|
|
||||||
char * arguments;
|
|
||||||
} commandEvent;
|
|
||||||
|
|
||||||
// A data-structure containing the needed parameters for the main game loop:
|
|
||||||
typedef struct gameLogicParameters
|
|
||||||
{
|
|
||||||
// Players:
|
|
||||||
int * playerCount;
|
|
||||||
playerInfo * connectedPlayers;
|
|
||||||
|
|
||||||
// Queues:
|
|
||||||
queue * inputQueue;
|
|
||||||
queue * outputQueue;
|
|
||||||
|
|
||||||
// Lists:
|
|
||||||
list * areaList;
|
|
||||||
list * globalSkillList;
|
|
||||||
} gameLogicParameters;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Functions ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Thread function which runs the main game loop, given the needed parameters:
|
|
||||||
void * gameLogicHandler(void * parameters);
|
|
||||||
|
|
||||||
// Enqueue a command that has been sent as a message from a user to a queue:
|
|
||||||
void queueMessagedCommand(queue * queue, inputMessage * messageToQueue);
|
|
||||||
|
|
||||||
// Evaluate the next commandEvent in a queue:
|
|
||||||
int evaluateNextCommand(gameLogicParameters * parameters, queue * queue);
|
|
||||||
|
|
||||||
// Enqueue a command to a queue:
|
|
||||||
void queueCommand(queue * queue, char * command, char * arguments, int commandLength, int argumentsLength,
|
|
||||||
playerInfo * callingPlayer);
|
|
||||||
|
|
||||||
// A hash function for distinguishing commands for the game logic handler:
|
|
||||||
unsigned int hashCommand(char * command, unsigned int commandLength);
|
|
||||||
|
|
||||||
// ============================
|
|
||||||
// -=[ Gameplay Primitives ]=-:
|
|
||||||
// ============================
|
|
||||||
|
|
||||||
// The possible outcomes of a check or challenge:
|
|
||||||
typedef enum outcome
|
|
||||||
{
|
|
||||||
CRITICAL_FAILURE,
|
|
||||||
FAILURE,
|
|
||||||
SUCCESS,
|
|
||||||
CRITICAL_SUCCESS,
|
|
||||||
ERROR
|
|
||||||
} outcome;
|
|
||||||
|
|
||||||
// Move a player along a path in their current area:
|
|
||||||
int movePlayerToArea(playerInfo * player, char * requestedPath);
|
|
||||||
|
|
||||||
// Run a stat check for the given player, returning an outcome:
|
|
||||||
outcome statCheck(playerInfo * player, int chance, coreStat statToCheck);
|
|
||||||
|
|
||||||
// Run a skill check for the given player, returning an outcome:
|
|
||||||
outcome skillCheck(playerInfo * player, int chance, char * skillName, size_t skillNameLength, list * globalSkillList);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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,75 +0,0 @@
|
||||||
// inputoutput.h: Header file contatning function prototypes and datastructures
|
|
||||||
// for dealing with input and output.
|
|
||||||
// Barry Kane, 2022.
|
|
||||||
#ifndef INPUTOUTPUT_H
|
|
||||||
#define INPUTOUTPUT_H
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <gnutls/gnutls.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "playerdata.h"
|
|
||||||
|
|
||||||
// Let the compiler know there will be structs defined elsewhere:
|
|
||||||
typedef struct queue queue;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Data Structures ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Contains a character/player name and the content of a message:
|
|
||||||
typedef struct userMessage
|
|
||||||
{
|
|
||||||
char senderName[32];
|
|
||||||
char messageContent[MAX];
|
|
||||||
} userMessage;
|
|
||||||
|
|
||||||
// Contains a message sent to the server and a pointer to the playerInfo of the connection which sent it:
|
|
||||||
typedef struct inputMessage
|
|
||||||
{
|
|
||||||
playerInfo * sender;
|
|
||||||
userMessage * content;
|
|
||||||
} inputMessage;
|
|
||||||
|
|
||||||
// Contains a message to be sent, the amount of recipients, and pointers to their playerInfo:
|
|
||||||
typedef struct outputMessage
|
|
||||||
{
|
|
||||||
int recipientsCount;
|
|
||||||
userMessage * content;
|
|
||||||
playerInfo ** recipients;
|
|
||||||
} outputMessage;
|
|
||||||
|
|
||||||
// Contains pointers to the necessary information to be shared outputThreadHandler function:
|
|
||||||
typedef struct outputThreadParameters
|
|
||||||
{
|
|
||||||
queue * outputQueue;
|
|
||||||
gnutls_session_t * tlssessions;
|
|
||||||
playerInfo * connectedPlayers;
|
|
||||||
} outputThreadParameters;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Functions ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Sends a message to a given TLS session, wraps the calls to gnutls_write:
|
|
||||||
int messageSend(gnutls_session_t receivingSession, userMessage * messageToSend);
|
|
||||||
|
|
||||||
// Receives a message from a given TLS session, wraps the calls to gnutls_read:
|
|
||||||
int messageReceive(gnutls_session_t receiveFromSession, userMessage * receiveToMessage);
|
|
||||||
|
|
||||||
// Create a targetedOutput message to be delivered to the players pointed to in recipients:
|
|
||||||
outputMessage * createTargetedOutputMessage(userMessage * messageToQueue, playerInfo ** recipients, int recipientCount);
|
|
||||||
|
|
||||||
// A function for the output thread, which sends queued messages:
|
|
||||||
void * outputThreadHandler(void * parameters);
|
|
||||||
|
|
||||||
// Sanatize user input to ensure it's okay to process:
|
|
||||||
void userInputSanatize(char * inputString, int length);
|
|
||||||
|
|
||||||
// Sanatize user names so they display correctly:
|
|
||||||
void userNameSanatize(char * inputString, int length);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
479
src/linkedlist.c
479
src/linkedlist.c
|
@ -1,479 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,85 +0,0 @@
|
||||||
// linkedlist.h: Defines the linked list datatype for SilverMUD.
|
|
||||||
// Barry Kane, 2022.
|
|
||||||
#ifndef LINKEDLIST_H
|
|
||||||
#define LINKEDLIST_H
|
|
||||||
#include "areadata.h"
|
|
||||||
#include "playerdata.h"
|
|
||||||
|
|
||||||
// Let the compiler know there will be structs defined elsewhere:
|
|
||||||
typedef struct playerPath playerPath;
|
|
||||||
typedef struct playerArea playerArea;
|
|
||||||
typedef struct playerInfo playerInfo;
|
|
||||||
typedef struct playerSkill playerSkill;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Data Structures ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// An enum of the possible data types that can be stored in a list:
|
|
||||||
typedef enum listDataType
|
|
||||||
{
|
|
||||||
PATH,
|
|
||||||
AREA,
|
|
||||||
PLAYER,
|
|
||||||
SKILL
|
|
||||||
} listDataType;
|
|
||||||
|
|
||||||
// A union containing a pointers to all data types that can be stored in a list:
|
|
||||||
typedef union listData
|
|
||||||
{
|
|
||||||
playerPath * path;
|
|
||||||
playerArea * area;
|
|
||||||
playerInfo * player;
|
|
||||||
playerSkill * skill;
|
|
||||||
} listData;
|
|
||||||
|
|
||||||
// A doubly linked node for the linked list type:
|
|
||||||
typedef struct listNode listNode;
|
|
||||||
typedef struct listNode
|
|
||||||
{
|
|
||||||
listData data;
|
|
||||||
listNode * next;
|
|
||||||
listNode * previous;
|
|
||||||
} listNode;
|
|
||||||
|
|
||||||
// A header structure for the list containing the length, head, tail, and type of the list.
|
|
||||||
typedef struct list
|
|
||||||
{
|
|
||||||
listDataType type;
|
|
||||||
size_t itemCount;
|
|
||||||
listNode * head;
|
|
||||||
listNode * tail;
|
|
||||||
} list;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Functions ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Allocates and instantiates a list of the specified type:
|
|
||||||
list * createList(listDataType type);
|
|
||||||
|
|
||||||
// Deallocates a list and all of it's members:
|
|
||||||
int destroyList(list ** list);
|
|
||||||
|
|
||||||
// Returns the data at a given index in a list:
|
|
||||||
listData * getFromList(list * list, size_t listIndex);
|
|
||||||
|
|
||||||
// Returns the node at a given index in a list:
|
|
||||||
listNode * getNodeFromList(list * list, size_t listIndex);
|
|
||||||
|
|
||||||
// Adds the given data to the end of a list:
|
|
||||||
listNode * addToList(list * list, void * data, listDataType type);
|
|
||||||
|
|
||||||
// Insert the given data at a given index in the list:
|
|
||||||
listNode * insertIntoList(list * list, void * data, listDataType type, size_t listIndex);
|
|
||||||
|
|
||||||
// Delete the given data from a list:
|
|
||||||
bool deleteFromList(list * list, void * data, listDataType type);
|
|
||||||
|
|
||||||
// Delete the data from a given point in a list:
|
|
||||||
int removeFromList(list * list, listDataType type, size_t listIndex);
|
|
||||||
|
|
||||||
// Get the index of a given piece of data in a list:
|
|
||||||
bool getIndexFromList(list * list, void * data, listDataType type, size_t * index);
|
|
||||||
|
|
||||||
#endif
|
|
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,82 +0,0 @@
|
||||||
// playerdata.h: Header file containing data structures for player data and function
|
|
||||||
// prototypes for interacting with said data.
|
|
||||||
#ifndef PLAYERDATA_H
|
|
||||||
#define PLAYERDATA_H
|
|
||||||
#include <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 playerArea playerArea;
|
|
||||||
typedef struct playerPath playerPath;
|
|
||||||
typedef struct listNode listNode;
|
|
||||||
typedef struct list list;
|
|
||||||
|
|
||||||
// The basic information that needs to be stored for a player or creature's stats:
|
|
||||||
typedef struct statBlock
|
|
||||||
{
|
|
||||||
// Levelling:
|
|
||||||
int level;
|
|
||||||
long experience;
|
|
||||||
|
|
||||||
// Health:
|
|
||||||
int currentHealth;
|
|
||||||
int maxHealth;
|
|
||||||
|
|
||||||
// Core Stats:
|
|
||||||
int wits;
|
|
||||||
int intellect;
|
|
||||||
int strength;
|
|
||||||
int endurance;
|
|
||||||
int dexerity;
|
|
||||||
|
|
||||||
// Character Building:
|
|
||||||
int specPoints;
|
|
||||||
int skillPoints;
|
|
||||||
} statBlock;
|
|
||||||
|
|
||||||
// Information about a skill, including skill levels and modifiers for the player:
|
|
||||||
typedef struct playerSkill
|
|
||||||
{
|
|
||||||
char skillName[32];
|
|
||||||
int skillPoints;
|
|
||||||
int skillModifier;
|
|
||||||
bool trainedSkill;
|
|
||||||
} playerSkill;
|
|
||||||
|
|
||||||
// Information about a single player's character:
|
|
||||||
typedef struct playerInfo
|
|
||||||
{
|
|
||||||
char playerName[32];
|
|
||||||
playerArea * currentArea;
|
|
||||||
statBlock * stats;
|
|
||||||
list * skills;
|
|
||||||
} playerInfo;
|
|
||||||
|
|
||||||
// An enum of the main stats of the game:
|
|
||||||
typedef enum coreStat
|
|
||||||
{
|
|
||||||
WITS,
|
|
||||||
INTELLECT,
|
|
||||||
STRENGTH,
|
|
||||||
ENDURANCE,
|
|
||||||
DEXERITY,
|
|
||||||
INVALID
|
|
||||||
} coreStat;
|
|
||||||
|
|
||||||
// Create a new skill and add it to the global skill list:
|
|
||||||
listNode * createSkill(list * globalSkillList, char * skillName, int skillNameLength, bool trainedSkill);
|
|
||||||
|
|
||||||
// Take a skill and add it to the player's skill list:
|
|
||||||
int takeSkill(list * globalSkillList, char * skillName, int skillNameLength, playerInfo * targetPlayer);
|
|
||||||
int takeSkillbyID(list * globalSkillList, int skillID, playerInfo * targetPlayer);
|
|
||||||
|
|
||||||
// Take a string containing a core stat name and return the core stat:
|
|
||||||
coreStat getCoreStatFromString(char * string, int stringLength);
|
|
||||||
|
|
||||||
// Deallocate a player's information including the skill lists and stats:
|
|
||||||
int deallocatePlayer(playerInfo * playerToDeallocate);
|
|
||||||
|
|
||||||
#endif
|
|
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"
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Data Structures ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Let the compiler know there will be structs defined elsewhere:
|
|
||||||
typedef struct queue queue;
|
|
||||||
|
|
||||||
// An enum which is used to state what type of data is being stored in a queueNode:
|
|
||||||
typedef enum queueDataType
|
|
||||||
{
|
|
||||||
EVENT,
|
|
||||||
COMMAND,
|
|
||||||
INPUT_MESSAGE,
|
|
||||||
OUTPUT_MESSAGE
|
|
||||||
} queueDataType;
|
|
||||||
|
|
||||||
// A union for storing a pointer to all the types of data a queueNode may hold:
|
|
||||||
typedef union queueData
|
|
||||||
{
|
|
||||||
outputMessage * outputMessage;
|
|
||||||
inputMessage * inputMessage;
|
|
||||||
commandEvent * command;
|
|
||||||
} queueData;
|
|
||||||
|
|
||||||
// A queue node, a singly-linked list node for our queue:
|
|
||||||
typedef struct queueNode queueNode;
|
|
||||||
typedef struct queueNode
|
|
||||||
{
|
|
||||||
queueDataType type;
|
|
||||||
queueData data;
|
|
||||||
queueNode * next;
|
|
||||||
} queueNode;
|
|
||||||
|
|
||||||
// A queue, with pointers to the head and tail of the linked list, and data for multi-threading, locking, and an item count.
|
|
||||||
typedef struct queue
|
|
||||||
{
|
|
||||||
volatile bool lock;
|
|
||||||
size_t itemCount;
|
|
||||||
queueNode * front;
|
|
||||||
queueNode * back;
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
pthread_cond_t condition;
|
|
||||||
} queue;
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// -=[ Functions ]=-:
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
// Allocates and instantiates a queue:
|
|
||||||
queue * createQueue(void);
|
|
||||||
|
|
||||||
// Destroys a queue and all of it's members:
|
|
||||||
void destroyQueue(queue ** queue);
|
|
||||||
|
|
||||||
// Returns the node at the front of the given queue:
|
|
||||||
queueNode * peekQueue(queue * queue);
|
|
||||||
|
|
||||||
// Removes the current node from the front of the queue:
|
|
||||||
void popQueue(queue * queue);
|
|
||||||
|
|
||||||
// Adds data to the back of a queue:
|
|
||||||
void pushQueue(queue * queue, void * data, queueDataType type);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,359 +0,0 @@
|
||||||
// Silverkin Industries Comm-Link Server, Engineering Sample Alpha 0.5
|
|
||||||
// PROJECT CODENAME: WHAT DO I PAY YOU FOR? | Level-3 Clearance.
|
|
||||||
// Barry Kane, 2021
|
|
||||||
#include <time.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <unistd.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"
|
|
||||||
|
|
||||||
typedef struct sockaddr sockaddr;
|
|
||||||
void sigintHandler(int signal)
|
|
||||||
{
|
|
||||||
printf("Caught signal %d.\n", signal);
|
|
||||||
_exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
|
||||||
{
|
|
||||||
time_t currentTime;
|
|
||||||
unsigned delay = 800;
|
|
||||||
int socketFileDesc, connectionFileDesc, length, clientsAmount,
|
|
||||||
socketCheck, activityCheck, returnVal;
|
|
||||||
fd_set connectedClients;
|
|
||||||
pthread_t gameLogicThread, outputThread;
|
|
||||||
int clientSockets[PLAYERCOUNT];
|
|
||||||
userMessage sendBuffer, receiveBuffer;
|
|
||||||
playerInfo connectedPlayers[PLAYERCOUNT];
|
|
||||||
char testString[32] = "Hehe.";
|
|
||||||
struct sockaddr_in serverAddress, clientAddress;
|
|
||||||
char motd[2048] = "Please login with the /join command.";
|
|
||||||
queue * inputQueue = createQueue(), * outputQueue = createQueue();
|
|
||||||
|
|
||||||
// Parse command-line options:
|
|
||||||
int currentopt = 0;
|
|
||||||
while ((currentopt = getopt(argc, argv, "d:m:")) != -1)
|
|
||||||
{
|
|
||||||
switch(currentopt)
|
|
||||||
{
|
|
||||||
case 'd':
|
|
||||||
{
|
|
||||||
delay = atoi(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm':
|
|
||||||
{
|
|
||||||
strncpy(motd, optarg, strlen(optarg) + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the handler for SIGINT:
|
|
||||||
signal(2, sigintHandler);
|
|
||||||
|
|
||||||
// -==[ TEST GAME-STATE INITIALIZATION ]==-
|
|
||||||
// Initialize test areas:
|
|
||||||
list * areas = createList(AREA);
|
|
||||||
addToList(areas, createArea("Login Area", motd), AREA);
|
|
||||||
|
|
||||||
// Create the areas:
|
|
||||||
addToList(areas, createArea("Octal One - Docking Bay Alpha",
|
|
||||||
"You are standing in the main docking bay of the largest station in the Octal System. "
|
|
||||||
"The sheer size of the bay is awe-inpiring. The number of ships is endless. "
|
|
||||||
"The bay is curved along with the body of the station. A catwalk runs along the back wall of the bay. "
|
|
||||||
"Two large arches lie at each end, leading to the other bays, and in the center, a set of doors leading to the interior of the station."), AREA);
|
|
||||||
|
|
||||||
addToList(areas, createArea("Octal One - Station Access Control",
|
|
||||||
"You enter into the hallway leading to the main interior of the station."
|
|
||||||
"The attendant informs you that due to a computer error, exits cannot be proccessed at the moment,"
|
|
||||||
" so you will be unable to leave, until it is resolved. "
|
|
||||||
"He apologizes profusely for the inconvenience, and clears you for entry if you wish to continue."), AREA);
|
|
||||||
|
|
||||||
addToList(areas, createArea("Octal One - Floor Zero",
|
|
||||||
"You've never quite seen so many people in one place. A large ring of shopfronts surrounds an area filled with benches and tables. "
|
|
||||||
"There's so many buisnesses in sight that you feel you could find everything you need, and this is only one of 25 main floors, "
|
|
||||||
"not to mention the 6 outer pylons which surround the main hull of the station. Staircases lead to an upper platform allowing access to the pylons. "), AREA);
|
|
||||||
|
|
||||||
// Initialize test paths:
|
|
||||||
createPath(getFromList(areas, 1)->area, getFromList(areas, 2)->area,
|
|
||||||
"Enter the station interior.", "Return to Docking Bay Alpha.");
|
|
||||||
createOneWayPath(getFromList(areas, 2)->area, getFromList(areas, 3)->area,
|
|
||||||
"Continue to station interior. ");
|
|
||||||
|
|
||||||
list * globalSkillList = createList(SKILL);
|
|
||||||
|
|
||||||
// Create a few basic skills:
|
|
||||||
createSkill(globalSkillList, "Medicine", 8, true);
|
|
||||||
createSkill(globalSkillList, "Lockpicking", 12, true);
|
|
||||||
createSkill(globalSkillList, "Programming", 12, true);
|
|
||||||
createSkill(globalSkillList, "Sensor Reading", 14, false);
|
|
||||||
createSkill(globalSkillList, "Starship Piloting", 17, true);
|
|
||||||
createSkill(globalSkillList, "Mechanical Repair", 17, true);
|
|
||||||
|
|
||||||
// Initialize playerdata:
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
sprintf(testString, "UNNAMED %d", index);
|
|
||||||
// OH NO IT'S NOT MEMORY SAFE BETTER REWRITE IT IN RUST
|
|
||||||
// But wait, we know the string won't be too big, so it's fine.
|
|
||||||
strcpy(connectedPlayers[index].playerName, testString);
|
|
||||||
connectedPlayers[index].currentArea = getFromList(areas, 0)->area;
|
|
||||||
connectedPlayers[index].stats = calloc(1, sizeof(statBlock));
|
|
||||||
connectedPlayers[index].stats->specPoints = 30;
|
|
||||||
connectedPlayers[index].stats->skillPoints = 30;
|
|
||||||
connectedPlayers[index].skills = createList(SKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -==[ TEST GAME-STATE INITIALIZATION END ]==-
|
|
||||||
|
|
||||||
// Give an intro: Display the Silverkin Industries logo and splash text.
|
|
||||||
slowPrint(logostring, delay);
|
|
||||||
slowPrint("\n--==== \033[33;40mSILVERKIN INDUSTRIES\033[0m COMM-LINK SERVER ====--\nVersion Alpha 0.5\n", delay);
|
|
||||||
|
|
||||||
// Seed random number generator from the current time:
|
|
||||||
srandom((unsigned)time(¤tTime));
|
|
||||||
|
|
||||||
// Initialize the sockets to 0, so we don't crash.
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
clientSockets[index] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a socket and make sure we actually get one.
|
|
||||||
socketFileDesc = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (socketFileDesc == -1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\tSocket Creation is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slowPrint("\tSocket Creation is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero(&serverAddress, sizeof(serverAddress));
|
|
||||||
|
|
||||||
// Assign IP and port:
|
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
serverAddress.sin_port = htons(PORT);
|
|
||||||
|
|
||||||
// Binding newly created socket to given IP, and checking it works:
|
|
||||||
if ((bind(socketFileDesc, (sockaddr*)&serverAddress, sizeof(serverAddress))) != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\tSocket Binding is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slowPrint("\tSocket Binding is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's start listening:
|
|
||||||
if ((listen(socketFileDesc, PLAYERCOUNT)) != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\tServer Listener is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slowPrint("\tServer Listener is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
}
|
|
||||||
length = sizeof(clientAddress);
|
|
||||||
|
|
||||||
// Declare the needed variables for TLS sessions:
|
|
||||||
gnutls_session_t tlssessions[PLAYERCOUNT];
|
|
||||||
gnutls_anon_server_credentials_t serverkey = NULL;
|
|
||||||
gnutls_anon_allocate_server_credentials(&serverkey);
|
|
||||||
gnutls_anon_set_server_known_dh_params(serverkey, GNUTLS_SEC_PARAM_MEDIUM);
|
|
||||||
|
|
||||||
// Initialize all the TLS sessions to NULL: We use this to check if it's an "empty connection."
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
tlssessions[index] = NULL;
|
|
||||||
if (gnutls_init(&tlssessions[index], GNUTLS_SERVER) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\tTLS Sessions Initialization is:\t\033[33;40mRED.\033[0m Aborting launch.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
|
||||||
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
|
||||||
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
|
||||||
}
|
|
||||||
slowPrint("\tTLS Preparation is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
|
|
||||||
// Prepare the game logic thread:
|
|
||||||
gameLogicParameters * gameLogicThreadParameters = malloc(sizeof(gameLogicParameters));
|
|
||||||
gameLogicThreadParameters->connectedPlayers = connectedPlayers;
|
|
||||||
gameLogicThreadParameters->playerCount = &clientsAmount;
|
|
||||||
gameLogicThreadParameters->globalSkillList = globalSkillList;
|
|
||||||
gameLogicThreadParameters->outputQueue = outputQueue;
|
|
||||||
gameLogicThreadParameters->inputQueue = inputQueue;
|
|
||||||
gameLogicThreadParameters->areaList = areas;
|
|
||||||
pthread_create(&gameLogicThread, NULL, &gameLogicHandler, gameLogicThreadParameters);
|
|
||||||
|
|
||||||
slowPrint("\tEvent Thread is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
|
|
||||||
// Prepare the output queue thread:
|
|
||||||
outputThreadParameters * outputParameters = malloc(sizeof(outputThreadParameters));
|
|
||||||
outputParameters->outputQueue = outputQueue;
|
|
||||||
outputParameters->tlssessions = tlssessions;
|
|
||||||
outputParameters->connectedPlayers = connectedPlayers;
|
|
||||||
pthread_create(&outputThread, NULL, &outputThreadHandler, outputParameters);
|
|
||||||
|
|
||||||
slowPrint("\tOutput Thread is:\t\033[32;40mGREEN.\033[0m\n", delay);
|
|
||||||
slowPrint("=====\n", delay);
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
// Clear the set of file descriptors and add the master socket:
|
|
||||||
FD_ZERO(&connectedClients);
|
|
||||||
FD_SET(socketFileDesc, &connectedClients);
|
|
||||||
clientsAmount = socketFileDesc;
|
|
||||||
|
|
||||||
// Find all sockets that are still working and place them in the set:
|
|
||||||
for(int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
// Just get the one we're working with to another name:
|
|
||||||
socketCheck = clientSockets[index];
|
|
||||||
|
|
||||||
// If it's working, bang it into the list:
|
|
||||||
if(socketCheck > 0)
|
|
||||||
{
|
|
||||||
FD_SET(socketCheck, &connectedClients);
|
|
||||||
}
|
|
||||||
// The amount of clients is needed for select():
|
|
||||||
if(socketCheck > clientsAmount)
|
|
||||||
{
|
|
||||||
clientsAmount = socketCheck;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if a connection is ready to be interacted with:
|
|
||||||
activityCheck = select((clientsAmount + 1), &connectedClients, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
// Check if select() worked:
|
|
||||||
if ((activityCheck < 0) && (errno != EINTR))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error in select(), retrying.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's the master socket selected, there is a new connection:
|
|
||||||
if (FD_ISSET(socketFileDesc, &connectedClients))
|
|
||||||
{
|
|
||||||
if ((connectionFileDesc = accept(socketFileDesc, (struct sockaddr *)&clientAddress, (socklen_t*)&length)) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to accept connection. Aborting.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
// See if we can put in the client:
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
// When there is an empty slot, pop it in:
|
|
||||||
if (clientSockets[index] == 0)
|
|
||||||
{
|
|
||||||
clientSockets[index] = connectionFileDesc;
|
|
||||||
printf("Adding to list of sockets as %d.\n", index);
|
|
||||||
gnutls_transport_set_int(tlssessions[index], clientSockets[index]);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
returnVal = gnutls_handshake(tlssessions[index]);
|
|
||||||
}
|
|
||||||
while (returnVal < 0 && gnutls_error_is_fatal(returnVal) == 0);
|
|
||||||
|
|
||||||
// Send a greeting message:
|
|
||||||
strcpy(sendBuffer.senderName, "");
|
|
||||||
strcpy(sendBuffer.messageContent, "Welcome to the server!");
|
|
||||||
messageSend(tlssessions[index], &sendBuffer);
|
|
||||||
strcpy(receiveBuffer.messageContent, "/look");
|
|
||||||
|
|
||||||
// Allocate the memory for a new input message:
|
|
||||||
inputMessage * newMessage = malloc(sizeof(inputMessage));
|
|
||||||
newMessage->content = malloc(sizeof(userMessage));
|
|
||||||
|
|
||||||
// Copy in the correct data:
|
|
||||||
memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage));
|
|
||||||
newMessage->sender = &connectedPlayers[index];
|
|
||||||
|
|
||||||
// Push the new message onto the queue:
|
|
||||||
pushQueue(inputQueue, newMessage, INPUT_MESSAGE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise, it's a client we need to interact with:
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int index = 0; index < PLAYERCOUNT; index++)
|
|
||||||
{
|
|
||||||
socketCheck = clientSockets[index];
|
|
||||||
|
|
||||||
if(FD_ISSET(socketCheck, &connectedClients))
|
|
||||||
{
|
|
||||||
int returnVal = messageReceive(tlssessions[index], &receiveBuffer);
|
|
||||||
// If player has disconnected:
|
|
||||||
if(returnVal == -10 || returnVal == 0)
|
|
||||||
{
|
|
||||||
// Close the session:
|
|
||||||
gnutls_bye(tlssessions[index], GNUTLS_SHUT_WR);
|
|
||||||
gnutls_deinit(tlssessions[index]);
|
|
||||||
shutdown(clientSockets[index], 2);
|
|
||||||
close(clientSockets[index]);
|
|
||||||
clientSockets[index] = 0;
|
|
||||||
tlssessions[index] = NULL;
|
|
||||||
|
|
||||||
// Clear out the old player state so that a new one may join:
|
|
||||||
sprintf(testString, "UNNAMED %d", index);
|
|
||||||
strcpy(connectedPlayers[index].playerName, testString);
|
|
||||||
connectedPlayers[index].currentArea = getFromList(areas, 0)->area;
|
|
||||||
|
|
||||||
// Prepare a fresh SSL session for the next new player:
|
|
||||||
gnutls_init(&tlssessions[index], GNUTLS_SERVER);
|
|
||||||
gnutls_priority_set_direct(tlssessions[index], "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
|
||||||
gnutls_credentials_set(tlssessions[index], GNUTLS_CRD_ANON, &serverkey);
|
|
||||||
gnutls_handshake_set_timeout(tlssessions[index], GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
|
||||||
}
|
|
||||||
// Otherwise, they've sent a message:
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Allocate the memory for a new input message:
|
|
||||||
inputMessage * newMessage = malloc(sizeof(inputMessage));
|
|
||||||
newMessage->content = malloc(sizeof(userMessage));
|
|
||||||
|
|
||||||
// Copy in the correct data:
|
|
||||||
memcpy(newMessage->content, &receiveBuffer, sizeof(userMessage));
|
|
||||||
newMessage->sender = &connectedPlayers[index];
|
|
||||||
|
|
||||||
// Push the new message onto the queue:
|
|
||||||
pushQueue(inputQueue, newMessage, INPUT_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_cancel(gameLogicThread);
|
|
||||||
pthread_cancel(outputThread);
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
|
@ -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