Faunis/ 0000775 0001750 0001750 00000000000 12062730612 011022 5 ustar user user Faunis/fdl-1.3.txt 0000644 0001750 0001750 00000054662 12062715036 012645 0 ustar user user
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. PREAMBLE
The purpose of this License is to make a manual, textbook, or other
functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft
license designed for free software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The "Document", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as "you". You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
A "Modified Version" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of
the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall
subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (Thus, if the Document is in
part a textbook of mathematics, a Secondary Section may not explain
any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
The "Invariant Sections" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML
or XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the
machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, "Title Page" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
The "publisher" means any person or entity that distributes copies of
the Document to the public.
A section "Entitled XYZ" means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language. (Here XYZ stands for a
specific section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".) To "Preserve the Title"
of such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no
other conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to
give them a chance to provide you with an updated version of the
Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
A. Use in the Title Page (and on the covers, if any) a title distinct
from that of the Document, and from those of previous versions
(which should, if there were any, be listed in the History section
of the Document). You may use the same title as a previous version
if the original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five),
unless they release you from this requirement.
C. State on the Title page the name of the publisher of the
Modified Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
F. Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled "History", Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled "History" in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the "History" section.
You may omit a network location for a work that was published at
least four years before the Document itself, or if the original
publisher of the version it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications",
Preserve the Title of the section, and preserve in the section all
the substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
M. Delete any section Entitled "Endorsements". Such a section
may not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled "Endorsements"
or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains
nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History"
in the various original documents, forming one section Entitled
"History"; likewise combine any sections Entitled "Acknowledgements",
and any sections Entitled "Dedications". You must delete all sections
Entitled "Endorsements".
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other
documents released under this License, and replace the individual
copies of this License in the various documents with a single copy
that is included in the collection, provided that you follow the rules
of this License for verbatim copying of each of the documents in all
other respects.
You may extract a single document from such a collection, and
distribute it individually under this License, provided you insert a
copy of this License into the extracted document, and follow this
License in all other respects regarding verbatim copying of that
document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense, or distribute it is void, and
will automatically terminate your rights under this License.
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, receipt of a copy of some or all of the same material does
not give you any rights to use it.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation 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. See
http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation. If the Document
specifies that a proxy can decide which future versions of this
License can be used, that proxy's public statement of acceptance of a
version permanently authorizes you to choose that version for the
Document.
11. RELICENSING
"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
World Wide Web server that publishes copyrightable works and also
provides prominent facilities for anybody to edit those works. A
public wiki that anybody can edit is an example of such a server. A
"Massive Multiauthor Collaboration" (or "MMC") contained in the site
means any set of copyrightable works thus published on the MMC site.
"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
license published by Creative Commons Corporation, a not-for-profit
corporation with a principal place of business in San Francisco,
California, as well as future copyleft versions of that license
published by that same organization.
"Incorporate" means to publish or republish a Document, in whole or in
part, as part of another Document.
An MMC is "eligible for relicensing" if it is licensed under this
License, and if all works that were first published under this License
somewhere other than this MMC, and subsequently incorporated in whole or
in part into the MMC, (1) had no cover texts or invariant sections, and
(2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site
under CC-BY-SA on the same site at any time before August 1, 2009,
provided the MMC is eligible for relicensing.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
Copyright (c) YEAR YOUR NAME.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the "with...Texts." line with this:
with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.
If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the GNU General Public License,
to permit their use in free software.
Faunis/.project 0000644 0001750 0001750 00000000555 12061412430 012466 0 ustar user user
Faunis
org.eclipse.jdt.core.javabuilder
org.eclipse.jdt.core.javanature
Faunis/serverData/ 0000775 0001750 0001750 00000000000 12062730610 013120 5 ustar user user Faunis/serverData/accounts/ 0000775 0001750 0001750 00000000000 12062730610 014737 5 ustar user user Faunis/serverData/accounts/user2/ 0000775 0001750 0001750 00000000000 12062730610 015777 5 ustar user user Faunis/serverData/accounts/user2/account.txt 0000644 0001750 0001750 00000000005 12055474662 020203 0 ustar user user user2 Faunis/serverData/accounts/user2/players/ 0000775 0001750 0001750 00000000000 12062730610 017456 5 ustar user user Faunis/serverData/accounts/user2/players/oso2/ 0000775 0001750 0001750 00000000000 12062730610 020340 5 ustar user user Faunis/serverData/accounts/user2/players/oso2/oso2 0000644 0001750 0001750 00000000700 12061733654 021152 0 ustar user user sr
server.Player I xI yL accountNamet Ljava/lang/String;L currentEmoteq ~ L currentMapNameq ~ L directiont Lcommunication/enums/Direction;L nameq ~ L patht Lcommunication/movement/Path;L typet $Lcommunication/enums/CharacterClass;xp t user2pt greenFields~r communication.enums.Direction xr java.lang.Enum xpt rightt oso2p~r "communication.enums.CharacterClass xq ~ t arctos Faunis/serverData/accounts/user1/ 0000775 0001750 0001750 00000000000 12062730610 015776 5 ustar user user Faunis/serverData/accounts/user1/account.txt 0000644 0001750 0001750 00000000007 12055474640 020200 0 ustar user user user1
Faunis/serverData/accounts/user1/players/ 0000775 0001750 0001750 00000000000 12062730610 017455 5 ustar user user Faunis/serverData/accounts/user1/players/oso1/ 0000775 0001750 0001750 00000000000 12062730610 020336 5 ustar user user Faunis/serverData/accounts/user1/players/oso1/oso1 0000644 0001750 0001750 00000000677 12062726176 021166 0 ustar user user sr
server.Player I xI yL accountNamet Ljava/lang/String;L currentEmoteq ~ L currentMapNameq ~ L directiont Lcommunication/enums/Direction;L nameq ~ L patht Lcommunication/movement/Path;L typet $Lcommunication/enums/CharacterClass;xp t user1pt greenFields~r communication.enums.Direction xr java.lang.Enum xpt downt oso1p~r "communication.enums.CharacterClass xq ~ t arctos Faunis/.settings/ 0000775 0001750 0001750 00000000000 12062730610 012736 5 ustar user user Faunis/.settings/org.eclipse.jdt.core.prefs 0000644 0001750 0001750 00000001166 11546033632 017730 0 ustar user user #Sun Apr 03 11:02:17 CEST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
Faunis/Makefile 0000644 0001750 0001750 00000001762 12062717630 012473 0 ustar user user # Copyright 2012 Simon Ley alias "skarute"
#
# This file is part of Faunis.
#
# Faunis 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.
#
# Faunis 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 Faunis. If not, see
# .
JAVAC = javac -sourcepath ./src/ -classpath ./bin/ -d ./bin/
JAVA = java -enableassertions -classpath ./bin/
compile:
$(JAVAC) ./src/client/Client.java
$(JAVAC) ./src/server/MainServer.java
runClient:
$(JAVA) client/Client
runServer:
$(JAVA) server/MainServer
clean:
rm -rf ./bin/*
Faunis/devdoc.txt 0000644 0001750 0001750 00000014471 12062725306 013040 0 ustar user user Copyright 2012 Simon Ley alias "skarute"
This documentation is published under the GNU Free Documentation
License v1.3 or later. You can find a copy of this license in
"fdl-1.3.txt" or at .
########################################
Faunis Developers' Documentation
########################################
This documentation is still incomplete. I'm working on it. :o(
1. Concurrency
Lots of synchronised statements are used to avoid problems with concurrency. To avoid deadlocks, all class fields are reserved from top to bottom as their definitions appear in the class. After them, all variables not part of the class are reserved.
2. Code structure
In general, the classes are structured in three main packages: "client" contains everything only needed by the client, "server" everything only needed by the server, and communication contains everything which is needed by both or which is exchanged during client-server communication. All exchanged communication messages are called orders and carry a prefix of two letters to better distinguish between them. The first letter indicates where the order is from (there are some few exceptions where this isn't true, though), while the second letter indicates who the recipient is. C stands for Client, B for Butler, M for MapManager.
Playable characters are represented by the Player class on the server side and by the PlayerGraphics class on the client side. Certain player data relevant for both sides is in GraphicalPlayerStatus which primarily stores information about how the character is displayed. Note that the PlayerGraphics instance can completely be produced from the GraphicalPlayerStatus object.
2.1 Client side
There is the Client class which represents the main client functionality and has the main method. The user interface is represented by the class GraphWin (basic gaming interface) and GameWindow (specialised for client functionality).
2.1.1 Animation
The more complicated part on the client side is the animation stuff: A character frame can be drawn as a whole (compact AniCompoType) or consist of separate limb images (limbed AniCompoType). The compact style is easier to create (since even if you decide to isolate the frame into single limbs, you will have to draw it as a whole anyway just to see if the limbs are proportional), but the limbed style has the advantage that later the character can keep its facial expression during emotes, amongst others.
All frames that form one animation cycle are stored in a Bone instance, which therefore is determined by character class, animation / emote state, body part and direction.
An animation can have different termination behaviour: It can simply return to the default standing animation (revert AniEndType), it can be repeated endlessly (repeat AniEndType), or it can stay in the last frame (end AniEndType).
Except for standing and walking, every animation can be triggered as an emote for now. Note that a walk command will remove any emote in progress.
The GraphicsContentManager is responsible for loading and storing these Bones and will provide them if requested. He also keeps information about AniCompoType and AniEndType. The reason why he is also used on the server side is because the server has to verify if an emote is valid, and must know its AniEndType to handle it correctly. The methods for loading the image files are actually not called on the server side.
2.1.2 Animators and Movers
Whenever a player is moving, a Mover is instantiated on both client and server side (stored in Client resp. MapManager). His task is to regularly adjust the player's position, thus he has an own Timer that frequently executes a TimerTask, in our case an instance of RoughMovingTask on the server side or SoftMovingTask on the client side. Reason for this differentiation is that on the server side it's enough to simply change the coordinates by one step during every task execution, but on the client side that would look too rough and we want a smooth transition between fields. That's why the SoftMovingTask also adjusts a special attribute, the so-called deltaLevel (I couldn't come up with a better name, I'm sorry). It indicates the relative position shift with relation to the recorded coordinates. The deltaLevel is only one value though for a two-dimensional shift we'd need two, however we still have the player's direction, so it is sufficient.
Like with movement, there's a similar approach with animation: The Animator class similarly has its own timer and timerTask to increment a player's frame counter and to handle animation termination. All Animators are kept by the Client class. Since there is no need to have frame counters on the server side, there are no Animators there.
2.2 Server side
The server side main class is MainServer, where at last all data references are hold. There is a Reception class which steadily looks out for new connecting clients and redirects them to a newly created Butler.
The Butler class represents a client on the server side. Besides the reception, he is the only one directly communicating with him, checking and implementing his orders as well as returning the orders meant for him. When the client disconnects, the butler is destroyed.
For each map of the game, there exists a MapManager who manages the list of players / butlers that are currently on this map. Every player / butler who holds an active player must be registered at a mapman. Whenever something happens on the map that others should perceive, the mapman sends orders to all butlers to notify them about the change.
3. Threads
Client:
- main thread: frequently redraws the screen
- one thread to handle user input
- one to respond to server orders
MainServer:
- main thread: Only used for initialisation and to make the window visible
Butler:
- one to handle orders from the client side
- one to handle orders from the server side (mapman etc.)
MapManager:
- one to handle the butlers' orders
Reception:
- one to receive incoming clients
Furthermore every Animator and Mover has its own thread.
4. What isn't implemented so far
- maps in general
- everything that turns a MMOG into a MMORPG: Fighting and attributes
- inventory and items
- security and encryption (minimum requirements for a stable version!), data backup
- better administration
- and much, much more...
Faunis/src/ 0000775 0001750 0001750 00000000000 12062730610 011607 5 ustar user user Faunis/src/communication/ 0000775 0001750 0001750 00000000000 12062730610 014454 5 ustar user user Faunis/src/communication/MapInfo.java 0000644 0001750 0001750 00000002223 12060472556 016657 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
import java.io.Serializable;
import java.util.HashMap;
public class MapInfo implements Serializable {
private static final long serialVersionUID = 1L;
public String mapName;
public HashMap players;
public MapInfo(String mapName, HashMap players) {
this.mapName = mapName;
this.players = players;
}
}
Faunis/src/communication/GraphicalPlayerStatus.java 0000644 0001750 0001750 00000003004 12061420306 021562 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
import java.io.Serializable;
import communication.enums.CharacterClass;
import communication.enums.Direction;
import communication.movement.Path;
/** Contains the whole information about how to correctly draw a player in its current
* state. Is transmitted in serialised form. */
public class GraphicalPlayerStatus implements Serializable {
private static final long serialVersionUID = 1L;
public String name;
public CharacterClass type;
public int x;
public int y;
public Direction direction;
public Path path = null;
public String currentEmote; // "revert" emotes may also be stored here
public boolean hasPath() {
return (path != null);
}
public boolean hasEmote() {
return (currentEmote != null);
}
}
Faunis/src/communication/OwnPlayerExtendedInfo.java 0000644 0001750 0001750 00000001473 12060472556 021551 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
public class OwnPlayerExtendedInfo {
}
Faunis/src/communication/butlerToClientOrders/ 0000775 0001750 0001750 00000000000 12062730610 020572 5 ustar user user Faunis/src/communication/butlerToClientOrders/BCRemoveCharOrder.java 0000644 0001750 0001750 00000002417 12061575046 024704 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import server.mapmanToButlerOrders.MBRemoveCharOrder;
/** The butler tells the client that the character defined by this order
* has left the map and should no longer be displayed. */
public class BCRemoveCharOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String playerName;
public BCRemoveCharOrder(MBRemoveCharOrder order) {
this.playerName = order.getPlayerName();
}
public String getPlayerName() {
return playerName;
}
}
Faunis/src/communication/butlerToClientOrders/BCSetMapOrder.java 0000644 0001750 0001750 00000002725 12061575306 024043 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import server.mapmanToButlerOrders.MBMapInfoOrder;
import communication.MapInfo;
/** The butler tells the client that the map defined by this order
* should be shown, including info about all characters on the new map. */
public class BCSetMapOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private MapInfo mapInfo;
private String activePlayerName;
public BCSetMapOrder(MBMapInfoOrder order, String activePlayerName) {
this.mapInfo = order.getMapInfo();
this.activePlayerName = activePlayerName;
}
public MapInfo getMapInfo() {
return mapInfo;
}
public String getActivePlayerName() {
return activePlayerName;
}
}
Faunis/src/communication/butlerToClientOrders/BCSetClientStatusOrder.java 0000644 0001750 0001750 00000002260 12061575212 025736 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import communication.enums.ClientStatus;
/** The butler tells the client that his client status should change. */
public class BCSetClientStatusOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private ClientStatus newStatus;
public BCSetClientStatusOrder(ClientStatus newStatus) {
this.newStatus = newStatus;
}
public ClientStatus getNewStatus() {
return this.newStatus;
}
}
Faunis/src/communication/butlerToClientOrders/BCChatMessageOrder.java 0000644 0001750 0001750 00000002551 12061575146 025035 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
/** The butler tells the client that a system message should be shown. */
public class BCChatMessageOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String message;
private String fromName;
private boolean isBroadcast;
public BCChatMessageOrder(String message, String fromName, boolean isBroadcast) {
this.message = message;
this.fromName = fromName;
this.isBroadcast = isBroadcast;
}
public String getMessage() {
return message;
}
public String getFromName() {
return fromName;
}
public boolean isBroadcast() {
return isBroadcast;
}
}
Faunis/src/communication/butlerToClientOrders/BCAddCharOrder.java 0000644 0001750 0001750 00000002723 12061574662 024142 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import communication.GraphicalPlayerStatus;
import server.mapmanToButlerOrders.MBAddCharOrder;
/** The butler tells the client that the character defined by this order
* has joined the map and should be displayed. */
public class BCAddCharOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String playerName;
private GraphicalPlayerStatus graphStatus;
public BCAddCharOrder(MBAddCharOrder order) {
this.playerName = order.getPlayerName();
this.graphStatus = order.getGraphStatus();
}
public String getPlayerName() {
return playerName;
}
public GraphicalPlayerStatus getGraphStatus() {
return graphStatus;
}
}
Faunis/src/communication/butlerToClientOrders/BCOrder.java 0000644 0001750 0001750 00000001666 12061573664 022741 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import java.io.Serializable;
public abstract class BCOrder implements Serializable {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/butlerToClientOrders/BCErrorMessageOrder.java 0000644 0001750 0001750 00000002142 12061575122 025235 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
/** The butler tells the client that an error message should be shown. */
public class BCErrorMessageOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String message;
public BCErrorMessageOrder(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Faunis/src/communication/butlerToClientOrders/BCChangeCharOrder.java 0000644 0001750 0001750 00000003033 12061575026 024625 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
import server.mapmanToButlerOrders.MBChangeCharOrder;
import communication.GraphicalPlayerStatus;
/** The butler tells the client that the character defined by this order
* has changed the graphical status, so that the client can adjust the
* way the character is displayed. */
public class BCChangeCharOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String playerName;
private GraphicalPlayerStatus graphStatus;
public BCChangeCharOrder(MBChangeCharOrder order) {
this.playerName = order.getPlayerName();
this.graphStatus = order.getGraphStatus();
}
public String getPlayerName() {
return playerName;
}
public GraphicalPlayerStatus getGraphStatus() {
return graphStatus;
}
}
Faunis/src/communication/butlerToClientOrders/BCSystemMessageOrder.java 0000644 0001750 0001750 00000002144 12061576066 025442 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.butlerToClientOrders;
/** The butler tells the client that a system message should be shown. */
public class BCSystemMessageOrder extends BCOrder {
private static final long serialVersionUID = 1L;
private String message;
public BCSystemMessageOrder(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
Faunis/src/communication/MapContentManager.java 0000644 0001750 0001750 00000002252 12060472556 020673 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
import java.util.HashMap;
public class MapContentManager {
private static MapContentManager instance;
private HashMap mapnameToMap;
public static void initialize() {
// TODO
}
public static MapContentManager getInstance() {
return instance;
}
public Map getByName(String mapName) {
synchronized(mapnameToMap) {
return mapnameToMap.get(mapName);
}
}
}
Faunis/src/communication/GraphicsContentManager.java 0000644 0001750 0001750 00000027645 12061716332 021725 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import client.Bone;
import client.Client;
import client.AnimationData;
import communication.enums.AniCompoType;
import communication.enums.AniEndType;
import communication.enums.BodyPart;
import communication.enums.CharacterClass;
import communication.enums.Direction;
public class GraphicsContentManager {
private String graphicsPath;
private String fileEnding;
private HashMap>>> bones;
private HashMap> drawingOrders;
private List compactDrawingOrder;
private HashMap> availableAnimationsAndData;
private HashMap> availableFaces;
private HashMap compositionTypes;
/** Don't forget to call loadResourcesForClient() / loadResourcesForServer()
* afterwards! */
public GraphicsContentManager(String graphicsPath, String fileEnding) {
this.graphicsPath = graphicsPath;
this.fileEnding = fileEnding;
}
/** Must be called by client after calling the content manager's constructor,
* will initialise all the fields that the client needs. */
public void loadResourcesForClient() {
compactDrawingOrder = new ArrayList();
compactDrawingOrder.add(BodyPart.compact);
bones = new HashMap>>>();
drawingOrders = new HashMap>();
loadDrawingOrders();
compositionTypes = new HashMap();
loadCompositionTypes();
availableAnimationsAndData = new HashMap>();
loadAvailableAnimations(true);
availableFaces = new HashMap>();
loadAvailableFaces();
loadImages();
}
/** Must be called by server after calling the content manager's constructor,
* will initialise all the fields that the server needs. */
public void loadResourcesForServer() {
availableAnimationsAndData = new HashMap>();
loadAvailableAnimations(false);
availableFaces = new HashMap>();
loadAvailableFaces();
}
public HashMap> getDrawingOrders() {
return this.drawingOrders;
}
public List getDrawingOrder(Direction direction) {
return this.drawingOrders.get(direction);
}
public List getCompactDrawingOrder() {
return compactDrawingOrder;
}
public Set getAvailableAnimations(CharacterClass type) {
return availableAnimationsAndData.get(type).keySet();
}
public AnimationData getAnimationData(CharacterClass type, String animation) {
return availableAnimationsAndData.get(type).get(animation);
}
public List getAvailableFaces(CharacterClass type) {
return availableFaces.get(type);
}
public AniCompoType getCompositionType(CharacterClass type) {
return compositionTypes.get(type);
}
/** Determines which animations are available for which character class.
* If given flag countFrames is set, stores the number of frames available for
* an animation (Takes body/down as reference, though all limbs/directions must have
* the same number).
* Note that if frames should be counted, this method will require compositionTypes
* to be initialised. */
private void loadAvailableAnimations(boolean countFrames) {
for (CharacterClass type : CharacterClass.values()) {
File typeDirectory = new File(graphicsPath+type.toString());
File[] subdirs = typeDirectory.listFiles(Client.directoryFilter);
HashMap animationAndData = new HashMap();
if (subdirs != null) {
for (File subdir : subdirs) {
String animation = subdir.getName();
if (!animation.equals("faces")) {
int numFrames = 0;
if (countFrames) {
String prefix;
if (compositionTypes.get(type) == AniCompoType.limbed)
prefix = graphicsPath+type+"/"
+animation+"/body/down";
else
prefix = graphicsPath+type+"/"
+animation+"/down";
numFrames = countAvailableFrames(prefix);
System.out.println("Animation "+animation+" of "+type+" has "+numFrames+" frames.");
}
AniEndType endType = determineEndType(type, animation);
AnimationData animationData = new AnimationData(numFrames, endType);
animationAndData.put(animation, animationData);
}
}
}
availableAnimationsAndData.put(type, animationAndData);
}
}
private void loadAvailableFaces() {
for (CharacterClass type : CharacterClass.values()) {
File faceDirectory = new File(graphicsPath+type.toString()+"/faces");
File[] subdirs = faceDirectory.listFiles(Client.directoryFilter);
if (subdirs == null) continue;
List faceList = new ArrayList();
for (File subdir : subdirs) {
String subdirName = subdir.getName();
faceList.add(subdirName);
}
availableFaces.put(type, faceList);
}
}
private void loadCompositionTypes() {
for (CharacterClass type : CharacterClass.values()) {
File bodyDirectory = new File(graphicsPath+type.toString()+"/stand/body");
if (bodyDirectory.isDirectory() && bodyDirectory.exists()) {
System.out.println(type+" has limbed graphics.");
compositionTypes.put(type, AniCompoType.limbed);
} else {
System.out.println(type+" has compact graphics.");
compositionTypes.put(type, AniCompoType.compact);
}
}
}
/** determines in which order body parts have to be drawn */
private void loadDrawingOrders() {
List leftList = Arrays.asList(
new BodyPart[] {BodyPart.rightArm,
BodyPart.rightLeg, BodyPart.body, BodyPart.head,
BodyPart.leftLeg, BodyPart.leftArm}
);
drawingOrders.put(Direction.left, leftList);
List rightList = Arrays.asList(
new BodyPart[] {BodyPart.leftArm,
BodyPart.leftLeg, BodyPart.body, BodyPart.head,
BodyPart.rightLeg, BodyPart.rightArm}
);
drawingOrders.put(Direction.right, rightList);
List downList = Arrays.asList(
new BodyPart[] {BodyPart.body,
BodyPart.leftLeg, BodyPart.rightLeg, BodyPart.leftArm,
BodyPart.rightArm, BodyPart.head}
);
drawingOrders.put(Direction.down, downList);
List upList = Arrays.asList(
new BodyPart[] {BodyPart.body,
BodyPart.leftLeg, BodyPart.rightLeg, BodyPart.leftArm,
BodyPart.rightArm, BodyPart.head}
);
drawingOrders.put(Direction.up, upList);
}
private void loadImages() {
for (CharacterClass type : CharacterClass.values()) {
AniCompoType compoType = compositionTypes.get(type);
for (String animation : getAvailableAnimations(type)) {
AnimationData animationData = getAnimationData(type, animation);
int countFrames = animationData.numberOfFrames;
String prefixString = graphicsPath+type+"/"+animation+"/";
for (BodyPart part : BodyPart.values()) {
if (compoType == AniCompoType.compact
&& part != BodyPart.compact)
continue;
if (compoType != AniCompoType.compact
&& part == BodyPart.compact)
continue;
for (Direction dir:Direction.values()) {
String prefixString2;
if (part != BodyPart.compact)
prefixString2 = prefixString+part+"/"+dir;
else
prefixString2 = prefixString+dir;
if (countFrames == 0) {
// load non-animation
String address = prefixString2+fileEnding;
if (!new File(address).exists()) {
System.out.println("couldn't load "+address);
continue;
}
System.out.println("load "+address);
Bone bone = new Bone(address);
addBone(type, animation, part, dir, bone);
} else if (countFrames > 0) {
// load animation with multiple frames
String toCheck = prefixString2+"0"+fileEnding;
if (!new File(toCheck).exists()) {
System.out.println("couldn't load "+toCheck);
continue;
}
System.out.println("load "+prefixString2+"*"+fileEnding);
Bone bone = new Bone(prefixString2, fileEnding,
countFrames-1);
addBone(type, animation, part, dir, bone);
} else {
System.out.println("WARNING: Animation "+animation+" of "+type+" hasn't any pictures!");
}
}
}
}
}
}
/** Detects if there are multiple numbered picture files (returns their number)
* or only a single un-numbered file (returns 0). If neither is found,
* returns -1. */
private int countAvailableFrames(String prefixString) {
File noAnim = new File(prefixString+fileEnding);
if (noAnim.exists()) return 0;
int counter = 0;
while (new File(prefixString+counter+fileEnding).exists()) {
counter++;
}
if (counter == 0) {
System.out.println("WARNING: Couldn't find neither single picture nor animation frames!");
return -1;
}
return counter;
}
private AniEndType determineEndType(CharacterClass type, String animation) {
File settingsFile = new File(graphicsPath+type+"/"+animation
+"/aniSettings.txt");
assert(settingsFile.exists());
BufferedReader reader;
String endTypeString;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(settingsFile)));
endTypeString = reader.readLine();
reader.close();
} catch(IOException e) {
System.out.println("Couldn't read settings for animation "+animation+" of "+type+"!");
return null;
}
System.out.println("Animation "+animation+" of "+type+" should be of AniEndType \""+endTypeString+"\".");
return AniEndType.valueOf(endTypeString);
}
private void addBone(CharacterClass type, String animation, BodyPart part, Direction dir, Bone bone) {
HashMap>> map = bones.get(type);
if (map == null) {
map = new HashMap>>();
bones.put(type, map);
}
HashMap> map2 = map.get(animation);
if (map2 == null) {
map2 = new HashMap>();
map.put(animation, map2);
}
HashMap map3 = map2.get(part);
if (map3 == null) {
map3 = new HashMap();
map2.put(part, map3);
}
assert(!map3.containsKey(dir));
map3.put(dir, bone);
}
public Bone getBone(CharacterClass type, String animation, BodyPart part, Direction dir) {
HashMap>> map = bones.get(type);
if (map == null) return null;
HashMap> map2 = map.get(animation);
if (map2 == null) return null;
HashMap map3 = map2.get(part);
if (map3 == null) return null;
return map3.get(dir);
}
}
Faunis/src/communication/clientToButlerOrders/ 0000775 0001750 0001750 00000000000 12062730610 020572 5 ustar user user Faunis/src/communication/clientToButlerOrders/CBSetFaceOrder.java 0000644 0001750 0001750 00000001767 12061576442 024173 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to make the active player show
* a certain facial expression. */
public class CBSetFaceOrder extends CBOrder {
private static final long serialVersionUID = -2353951011956540653L;
}
Faunis/src/communication/clientToButlerOrders/CBCreatePlayerOrder.java 0000644 0001750 0001750 00000002256 12061576204 025227 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that a new character should be created under
* the logged account. */
public class CBCreatePlayerOrder extends CBOrder {
private static final long serialVersionUID = 1L;
private String playerName;
public CBCreatePlayerOrder(String playerName) {
this.playerName = playerName;
}
public String getPlayerName() {
return playerName;
}
}
Faunis/src/communication/clientToButlerOrders/CBLoadPlayerOrder.java 0000644 0001750 0001750 00000002256 12061576264 024711 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to load an existing character
* from the logged account. */
public class CBLoadPlayerOrder extends CBOrder {
private static final long serialVersionUID = 1L;
private String playerName;
public CBLoadPlayerOrder(String playerName) {
this.playerName = playerName;
}
public String getPlayerName() {
return playerName;
}
}
Faunis/src/communication/clientToButlerOrders/CBChatOrder.java 0000644 0001750 0001750 00000002464 12061576150 023527 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to send a chat message. */
public class CBChatOrder extends CBOrder {
private static final long serialVersionUID = 1L;
private String message;
private String toName;
/** If toName = "" or null, then the message will be broadcast on the map. */
public CBChatOrder(String message, String toName){
this.message = message;
this.toName = toName;
}
public String getMessage() {
return message;
}
public String getToName() {
return toName;
}
}
Faunis/src/communication/clientToButlerOrders/CBDisconnectOrder.java 0000644 0001750 0001750 00000001717 12061576220 024737 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to disconnect. */
public class CBDisconnectOrder extends CBOrder {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/clientToButlerOrders/CBOrder.java 0000644 0001750 0001750 00000001764 12061573556 022740 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
import java.io.Serializable;
/** Represents a request from the client to the server side */
public abstract class CBOrder implements Serializable {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/clientToButlerOrders/CBLogoutOrder.java 0000644 0001750 0001750 00000001707 12061576314 024122 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to log out. */
public class CBLogoutOrder extends CBOrder {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/clientToButlerOrders/CBTriggerEmoteOrder.java 0000644 0001750 0001750 00000002177 12061576506 025253 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to make the active player show
* a certain gesture. */
public class CBTriggerEmoteOrder extends CBOrder {
private static final long serialVersionUID = 2134927626424448916L;
private String emote;
public CBTriggerEmoteOrder(String emote) {
this.emote = emote;
}
public String getEmote() {
return emote;
}
}
Faunis/src/communication/clientToButlerOrders/CBUnloadPlayerOrder.java 0000644 0001750 0001750 00000001736 12061576524 025255 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to unload the active player. */
public class CBUnloadPlayerOrder extends CBOrder {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/clientToButlerOrders/CBMoveOrder.java 0000644 0001750 0001750 00000002334 12061576336 023560 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to move the active player. */
public class CBMoveOrder extends CBOrder {
private static final long serialVersionUID = 1L;
private int xTarget;
private int yTarget;
public CBMoveOrder(int xTarget, int yTarget) {
this.xTarget = xTarget;
this.yTarget = yTarget;
}
public int getXTarget() {
return xTarget;
}
public int getYTarget() {
return yTarget;
}
}
Faunis/src/communication/clientToButlerOrders/CBServerSourceOrder.java 0000644 0001750 0001750 00000001724 12061576360 025300 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** A request about how to get the server's
* source code, as demanded by the GNU AGPL. */
public class CBServerSourceOrder extends CBOrder {
private static final long serialVersionUID = 1L;
}
Faunis/src/communication/clientToButlerOrders/CBLoginOrder.java 0000644 0001750 0001750 00000002355 12061576304 023720 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.clientToButlerOrders;
/** The client tells the butler that he wants to log in. */
public class CBLoginOrder extends CBOrder {
private static final long serialVersionUID = 1L;
private String name;
private String password;
public CBLoginOrder(String name, String password){
this.name = name;
this.password = password;
}
public String getName(){
return this.name;
}
public String getPassword(){
return this.password;
}
}
Faunis/src/communication/Map.java 0000644 0001750 0001750 00000002201 12060472556 016037 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication;
public class Map {
private String name;
private int width;
private int height;
public Map(String name, int width, int height) {
this.name = name;
this.width = width;
this.height = height;
}
public String getName() {
return name;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
Faunis/src/communication/movement/ 0000775 0001750 0001750 00000000000 12062730610 016306 5 ustar user user Faunis/src/communication/movement/MovingTask.java 0000644 0001750 0001750 00000007067 12061420306 021240 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.awt.Point;
import java.util.TimerTask;
import communication.enums.Direction;
public abstract class MovingTask extends TimerTask {
protected Object runningMutexKey;
protected boolean stopRunning;
protected Mover parent;
protected Moveable moveable;
public MovingTask(Mover parent, Moveable moveable) {
this.parent = parent;
this.moveable = moveable;
this.runningMutexKey = new Object();
this.stopRunning = false;
}
/** Locks moveable, runningMutexKey or moving-list, animation-list, moveable */
@Override
public void run() {
// move moveable to target and
// unregister at parent when target reached
/* PROBLEM: On the client side, I want to call
tryStopAnimation() in the unregisterMover() method.
But that forces me to first synchronise on the locked
resources there before I synchronise on moveable.
*/
synchronized(moveable) {
if (!this.isMovementFinished()) {
this.move();
return;
}
}
// else:
Object[] lists = parent.parent.getSynchroStuffForMoverStop();
assert(lists != null);
if (lists.length == 1) {
synchronized(lists[0]) {
synchronized(moveable) {
System.out.println("Synchronized on movingPlayerGraphics,"
+" moveable.");
if (this.isMovementFinished())
parent.stopAndUnregister();
}
}
} else if (lists.length == 2) {
synchronized(lists[0]) {
synchronized(lists[1]) {
System.out.println("Synchronized on movingPlayerGraphics,"
+" animatedPlayerGraphics, moveable.");
synchronized(moveable) {
if (this.isMovementFinished())
parent.stopAndUnregister();
}
}
}
} else {
throw new RuntimeException("MovingTask: Lists array is of strange size!");
}
}
protected abstract boolean isMovementFinished();
protected abstract void move();
public void stop() {
parent.timer.cancel();
synchronized(runningMutexKey) {
this.stopRunning = true;
}
synchronized(moveable) {
moveable.resetPath();
}
}
public static Direction deltaToDirection(int deltaX, int deltaY) {
if (Math.abs(deltaX) >= Math.abs(deltaY)) {
if (deltaX < 0)
return Direction.left;
else if (deltaX > 0)
return Direction.right;
else
return null;
} else {
if (deltaY < 0)
return Direction.up;
else if (deltaY > 0)
return Direction.down;
else
return null;
}
}
public static Point directionToDelta(Direction direction) {
if (direction == null)
return new Point(0,0);
switch(direction) {
case left:
return new Point(-1,0);
case right:
return new Point(1,0);
case up:
return new Point(0,-1);
case down:
return new Point(0,1);
}
throw new RuntimeException("directionToDelta: Unknown direction!");
}
}
Faunis/src/communication/movement/MoverManager.java 0000644 0001750 0001750 00000001643 12060472556 021550 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
public interface MoverManager {
public void unregisterMover(Moveable forMoveable);
public Object[] getSynchroStuffForMoverStop();
}
Faunis/src/communication/movement/Pathfinder.java 0000644 0001750 0001750 00000001501 12061577556 021251 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
public class Pathfinder {
// TODO
}
Faunis/src/communication/movement/SoftMovingTask.java 0000644 0001750 0001750 00000006404 12061600122 022062 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.awt.Point;
import client.Client;
import client.ClientSettings;
import client.PlayerGraphics;
import communication.enums.Direction;
/** For the client side: Moves the player along the path, but between adjusting
* the coordinates, special values (deltaLevel) are also set, such that the
* animation looks softer. Therefore it needs more (frequent) calls of move()
* than RoughMovingTask, and the coordinates are not changed with every call.
*/
public class SoftMovingTask extends MovingTask {
public SoftMovingTask(Mover parent, Moveable moveable) {
super(parent, moveable);
// TODO Auto-generated constructor stub
}
@Override
protected boolean isMovementFinished() {
return (moveable.getPath().isEmpty());
}
@Override
protected void move() {
synchronized(moveable) {
synchronized(runningMutexKey) {
if (stopRunning)
return;
PlayerGraphics playerGraphics = (PlayerGraphics) moveable;
assert(playerGraphics != null);
ClientSettings settings = ((Client)(parent.parent)).getClientSettings();
int deltaLevelAmplitude = settings.deltaLevelAmplitude();
Path path = playerGraphics.getPath();
assert(path != null);
int deltaLevel = playerGraphics.getDeltaLevel();
Point nextPoint = path.top();
//System.out.println("deltaLevel="+deltaLevel+", nextPoint="+nextPoint);
assert(!(deltaLevel == 0 && nextPoint == null));
// We know where to go to,
// and we also know the deltaLevel
deltaLevel++;
if (deltaLevel == 0) {
playerGraphics.setDeltaLevel(deltaLevel);
// stop movement if there's no further waypoint
if (nextPoint == null) {
// stop movement
// playerGraphics.resetPath();
return;
}
} else if (deltaLevel == 1) {
// adapt direction to next waypoint
Direction newDirection = MovingTask.deltaToDirection(
nextPoint.x-playerGraphics.getX(), nextPoint.y-playerGraphics.getY());
if (newDirection != null)
playerGraphics.setDirection(newDirection);
playerGraphics.setDeltaLevel(deltaLevel);
} else if (deltaLevel > deltaLevelAmplitude) {
// move over to the next field and remove waypoint from path
deltaLevel = -deltaLevelAmplitude;
playerGraphics.setDeltaLevel(deltaLevel);
assert(nextPoint != null);
playerGraphics.moveAbsolute(nextPoint.x, nextPoint.y, false);
path.pop();
} else {
playerGraphics.setDeltaLevel(deltaLevel);
}
}
}
}
}
Faunis/src/communication/movement/Path.java 0000644 0001750 0001750 00000004061 12061577512 020055 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.awt.Point;
import java.io.Serializable;
import java.util.LinkedList;
/** FIFO-queue of coordinates along which a moveable can be moved. */
public class Path implements Serializable {
private static final long serialVersionUID = 1L;
LinkedList steps;
public Path() {
steps = new LinkedList();
}
/** deep copy */
public Path copy() {
Path result = new Path();
for (Point step : this.steps) {
result.push((Point)step.clone());
}
return result;
}
public void push(Point step) {
steps.add(step);
}
/** Removes the next waypoint from this path and returns it,
* or returns null if this path is empty. */
public Point pop() {
if (steps.isEmpty())
return null;
else
return steps.pop();
}
/** Returns the next waypoint or null if path is empty */
public Point top() {
return steps.peek();
}
public boolean isEmpty() {
return steps.isEmpty();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (Point step : steps) {
stringBuilder.append("(");
stringBuilder.append(step.x);
stringBuilder.append(",");
stringBuilder.append(step.y);
stringBuilder.append(")");
}
return stringBuilder.toString();
}
}
Faunis/src/communication/movement/PathFactory.java 0000644 0001750 0001750 00000003205 12060472556 021405 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.awt.Point;
import communication.Map;
public class PathFactory {
public static Path createAirlinePath(int fromX, int fromY, int toX, int toY) {
Path result = new Path();
int currentX = fromX;
int currentY = fromY;
while (currentX != toX || currentY != toY) {
int deltaX = toX-currentX;
int deltaY = toY-currentY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
currentX += Math.signum(deltaX);
Point point = new Point(currentX, currentY);
result.push(point);
} else {
currentY += Math.signum(deltaY);
Point point = new Point(currentX, currentY);
result.push(point);
}
}
assert(!result.isEmpty());
System.out.println("Path=" + result.toString());
return result;
}
/*
public static Path createShortestPath(Map map, int fromX, int fromY, int toX, int toY) {
// TODO
}
*/
}
Faunis/src/communication/movement/RoughMovingTask.java 0000644 0001750 0001750 00000003303 12061600142 022230 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.awt.Point;
/** For the server side: Moves the Player in whole steps along the path
* without any intermediate animation adjustments like in SoftMovingTask.
* Therefore, one call of move() = one field onward. */
public class RoughMovingTask extends MovingTask {
public RoughMovingTask(Mover parent, Moveable moveable) {
super(parent, moveable);
}
@Override
protected boolean isMovementFinished() {
return (moveable.getPath().isEmpty());
}
@Override
protected void move() {
synchronized(moveable) {
synchronized(runningMutexKey) {
if (stopRunning)
return;
Path path = moveable.getPath();
assert(path != null);
Point nextPoint = path.pop();
if (nextPoint == null) {
// moveable.setPath(null);
return;
}
moveable.moveAbsolute(nextPoint.x, nextPoint.y, true);
// if (path.isEmpty()) moveable.setPath(null);
}
}
}
}
Faunis/src/communication/movement/Moveable.java 0000644 0001750 0001750 00000002032 12060472556 020710 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
public interface Moveable {
public boolean hasPath();
public void resetPath();
public int getX();
public int getY();
public void moveAbsolute(int x, int y, boolean adaptDirection);
public Path getPath();
public void setPath(Path path);
}
Faunis/src/communication/movement/Mover.java 0000644 0001750 0001750 00000003727 12060472556 020262 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.movement;
import java.util.Timer;
public class Mover {
protected MoverManager parent;
protected Moveable moveable;
protected Timer timer;
protected MovingTask movingTask;
protected long interval;
/** PLEASE NOTE: You'll also have to create a MovingTask for this Mover
* and assign it by calling Mover.setMovingTask()! Afterwards, don't forget
* that this Mover isn't started yet. */
public Mover(MoverManager parent, Moveable moveable,
long delay) {
this.parent = parent;
this.moveable = moveable;
this.interval = delay;
this.timer = new Timer();
}
public void setMovingTask(MovingTask movingTask) {
this.movingTask = movingTask;
}
/** Starts the MovingTask. Has to be explicitely called,
* as that isn't done automatically.
* HINT: Doesn't register this Mover at the parent, because that must
* already happen while creating the Mover. */
public void start() {
this.timer.scheduleAtFixedRate(movingTask, 0, interval);
}
public Moveable getMoveable() {
return moveable;
}
public void stop() {
this.movingTask.stop();
}
public void stopAndUnregister() {
this.stop();
parent.unregisterMover(this.moveable);
}
}
Faunis/src/communication/enums/ 0000775 0001750 0001750 00000000000 12062730610 015603 5 ustar user user Faunis/src/communication/enums/BodyPart.java 0000644 0001750 0001750 00000001740 12061420306 020167 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines the limb, for animation. "compact" is only
* used if the class graphics are not limbed. */
public enum BodyPart {
body, head, leftArm, rightArm, leftLeg, rightLeg, tail, compact
}
Faunis/src/communication/enums/Direction.java 0000644 0001750 0001750 00000001611 12061577370 020376 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines the direction in which a player is looking. */
public enum Direction {
left, right, up, down
}
Faunis/src/communication/enums/AniCompoType.java 0000644 0001750 0001750 00000001670 12061576674 021040 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines if each frame of an animation is drawn as a whole (compact)
* or composed of separate images for each limb (limbed). */
public enum AniCompoType {
limbed, compact
}
Faunis/src/communication/enums/CharacterClass.java 0000644 0001750 0001750 00000001704 12061577302 021336 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines a character's class:
* rodent, badger, bear, owl, deer, lynx, eagle */
public enum CharacterClass {
rodens, meles, arctos, glaux, cervus, lynx, aguila
}
Faunis/src/communication/enums/ClientStatus.java 0000644 0001750 0001750 00000001625 12061577344 021106 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines the client's connection / game status. */
public enum ClientStatus {
disconnected, loggedOut, noCharLoaded, exploring, fighting
}
Faunis/src/communication/enums/AniEndType.java 0000644 0001750 0001750 00000001725 12061577106 020461 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines what happens when an animation ends, if it should be repeated,
* stay in the last frame (end) or revert to the default standing state (revert). */
public enum AniEndType {
revert, repeat, end
}
Faunis/src/communication/enums/FaceExpression.java 0000644 0001750 0001750 00000001620 12061577440 021372 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package communication.enums;
/** Determines possible facial expressions for a player. */
public enum FaceExpression {
normal, happy, sad, angry, surprised, tired, sick
}
Faunis/src/client/ 0000775 0001750 0001750 00000000000 12062730610 013065 5 ustar user user Faunis/src/client/AnimatorManager.java 0000644 0001750 0001750 00000001605 12060472074 017002 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
public interface AnimatorManager {
public void unregisterAnimator(Animateable animateable);
public Object getSynchroStuffForAnimatorStop();
}
Faunis/src/client/MessageType.java~ 0000644 0001750 0001750 00000001464 12062726756 016377 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
public enum MessageType {
error, system, whisper, broadcast
}
Faunis/src/client/GameWindow.java 0000644 0001750 0001750 00000022225 12062201130 015760 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import communication.enums.ClientStatus;
import communication.enums.Direction;
import communication.movement.MovingTask;
public class GameWindow extends GraphWin {
protected Client parent;
private JPanel commandPanel;
private JScrollPane loggingScrollPane;
private JTextPane loggingTextPane;
private StyledDocument loggingDocument;
private JButton commandSendButton;
protected JTextField commandEdit;
private SimpleAttributeSet errorTextStyle;
private SimpleAttributeSet systemTextStyle;
private SimpleAttributeSet whisperTextStyle;
private SimpleAttributeSet oldCommandTextStyle;
private SimpleAttributeSet broadcastTextStyle;
public GameWindow(Client parent, int width, int height, String title) {
super(width, height, title);
this.parent = parent;
errorTextStyle = new SimpleAttributeSet();
StyleConstants.setForeground(errorTextStyle, Color.red);
systemTextStyle = new SimpleAttributeSet();
StyleConstants.setForeground(systemTextStyle, new Color(0, 120, 120));
whisperTextStyle = new SimpleAttributeSet();
StyleConstants.setForeground(whisperTextStyle, Color.blue);
oldCommandTextStyle = new SimpleAttributeSet();
StyleConstants.setForeground(oldCommandTextStyle, Color.gray);
broadcastTextStyle = new SimpleAttributeSet();
StyleConstants.setForeground(broadcastTextStyle, new Color(140, 80, 0));
loggingTextPane = new JTextPane();
loggingTextPane.setEditable(false);
loggingTextPane.setBackground(new Color(230, 230, 230));
loggingDocument = loggingTextPane.getStyledDocument();
assert(loggingDocument != null);
loggingScrollPane = new JScrollPane(loggingTextPane);
loggingScrollPane.setPreferredSize(new Dimension(100,100));
JPanel loggingPanel = new JPanel(new BorderLayout());
loggingPanel.add(BorderLayout.CENTER, loggingScrollPane);
commandEdit = new JTextField();
commandEdit.setPreferredSize(new Dimension(300,25));
commandSendButton = new JButton("Send");
commandSendButton.addActionListener(new CommandSendButtonListener());
commandPanel = new JPanel();
commandPanel.add(commandEdit);
commandPanel.add(commandSendButton);
JPanel southPanel = new JPanel(new BorderLayout());
southPanel.add(BorderLayout.CENTER, loggingPanel);
southPanel.add(BorderLayout.SOUTH, commandPanel);
win.getRootPane().setDefaultButton(commandSendButton);
win.getContentPane().add(BorderLayout.SOUTH, southPanel);
drawingPanel.setPreferredSize(new Dimension(width, height));
win.pack();
System.out.println("packed");
}
@Override
/** locks parent.clientStatus, (parent.currentPlayers; playerGraphics)
* redraws Backbuffer (please call repaint() to copy it to the front) */
public void draw() {
graph.setColor(Color.green);
graph.fillRect(0, 0, img.getWidth(), img.getHeight());
Point mausPos = this.mousePos();
if (mausPos == null) mausPos = new Point(0,0);
graph.setColor(Color.black);
graph.drawRect(mausPos.x, mausPos.y, 100, 100);
// depending on Client.clientStatus, other things have to be drawn
synchronized(parent.getClientStatus()) {
ClientStatus status = parent.getClientStatus();
if (status == ClientStatus.exploring || status == ClientStatus.fighting) {
// draw grid:
drawFieldGrid();
// write map and player name:
writeMapAndPlayerInfo();
// draw all player graphics:
drawAllPlayers();
}
// draw clientStatus:
drawClientStatus();
}
}
private void drawFieldGrid() {
graph.setColor(Color.lightGray);
int width = img.getWidth();
int height = img.getHeight();
int fieldWidth = parent.getClientSettings().fieldWidth();
int fieldHeight = parent.getClientSettings().fieldHeight();
int maxRow = height / fieldHeight;
int maxColumn = width / fieldWidth;
// draw horizontal lines:
for (int y = 0; y < maxRow; y++) {
graph.drawRect(0, y*fieldHeight, width, 0);
}
// draw vertical lines:
for (int x = 0; x < maxColumn; x++) {
graph.drawRect(x*fieldWidth, 0, 0, height);
}
}
private void writeMapAndPlayerInfo() {
String mapName = parent.getCurrentMapName();
String playerName = parent.getCurrentPlayerName();
graph.setColor(Color.black);
if (mapName != null) graph.drawString(mapName, 10, 20);
if (playerName != null) {
PlayerGraphics player = parent.getPlayerGraphics(playerName);
int x = player.getX();
int y = player.getY();
graph.drawString(playerName, 10, 30);
graph.drawString("("+x+","+y+")", 10, 40);
}
}
private void drawClientStatus() {
ClientStatus status = parent.getClientStatus();
graph.setColor(Color.black);
graph.drawString(status.toString(), 10, 10);
}
/** locks currentPlayers; einzelne playerGraphics
* Draw all characters */
private void drawAllPlayers() {
ArrayList allPlayerGraphics = parent.getAllGraphicsToDraw();
for (PlayerGraphics playerGraphics : allPlayerGraphics) {
synchronized(playerGraphics) {
Point offset = calculateDrawingOffset(playerGraphics);
String playerName = playerGraphics.getName();
int playerNameWidth = graph.getFontMetrics().stringWidth(playerName);
graph.setColor(Color.black);
graph.drawString(playerName, offset.x - playerNameWidth/2, offset.y + 15);
int frameIndex = playerGraphics.getFrame();
playerGraphics.draw(graph, offset.x, offset.y, frameIndex);
}
}
}
private Point calculateDrawingOffset(PlayerGraphics playerGraphics) {
ClientSettings clientSettings = parent.getClientSettings();
int numDeltaLevels = clientSettings.numberOfDeltaLevelStates();
int fieldWidth = clientSettings.fieldWidth();
int fieldHeight = clientSettings.fieldHeight();
Direction direction = playerGraphics.getDirection();
Point delta = MovingTask.directionToDelta(direction);
int deltaHoriz = (fieldWidth/numDeltaLevels) * delta.x;
int deltaVert = (fieldHeight/numDeltaLevels) * delta.y;
int deltaLevel = playerGraphics.getDeltaLevel();
int fieldX = playerGraphics.getX();
int fieldY = playerGraphics.getY();
// calculate resulting coordinates:
int offsetX = fieldWidth/2 + fieldWidth * fieldX + deltaLevel*deltaHoriz;
int offsetY = fieldHeight/2 + fieldHeight * fieldY + deltaLevel*deltaVert;
return new Point(offsetX, offsetY);
}
protected class CommandSendButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String command = commandEdit.getText();
logOldCommandMessage(command);
if (command.length() > 0 &&
command.startsWith("/"))
{
String[] commandSplit = command.split(" ");
assert(commandSplit.length > 0);
String commandPrefix = commandSplit[0];
String[] commandRest = new String[commandSplit.length-1];
for (int i = 1; i < commandSplit.length; i++) {
commandRest[i-1] = commandSplit[i];
}
// evaluate command:
boolean success = parent.parseCommand(commandPrefix, commandRest);
if (success) commandEdit.setText(null);
}
}
}
private void logMessage(String message, SimpleAttributeSet set) {
try {
loggingDocument.insertString(loggingDocument.getLength(),
message+"\n", set);
this.loggingTextPane.setCaretPosition(loggingDocument.getLength());
} catch (BadLocationException e) {
e.printStackTrace();
System.out.println("Error appending a message to the loggingPane!");
}
}
public void logErrorMessage(String message) {
logMessage(message, errorTextStyle);
}
public void logWhisperMessage(String message) {
logMessage(message, whisperTextStyle);
}
public void logOldCommandMessage(String message) {
logMessage(message, oldCommandTextStyle);
}
public void logBroadcastMessage(String message) {
logMessage(message, broadcastTextStyle);
}
public void logSystemMessage(String message) {
logMessage(message, systemTextStyle);
}
}
Faunis/src/client/ClientSettings.java 0000644 0001750 0001750 00000005277 12061602254 016701 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.io.File;
public class ClientSettings {
private String classPath; // paths are declared in constructor below;
private String clientDataPath; // NOTE: All paths must end in "/"
private String playerGraphicsPath;
private String imageFileEnding = ".png"; // must begin with a fullstop!
private int fieldWidth = 20;
private int fieldHeight = 14;
private int deltaLevelAmplitude = 2;
private String host = null; // the hostname, or null for loopback
private int port = 1024; // the port through which to connect to the server
private int frameRate = 20; // frames per second
public ClientSettings() {
try {
classPath = getClass().getProtectionDomain().
getCodeSource().getLocation().toURI().getPath();
} catch (Exception e) {
e.printStackTrace();
return;
}
//classPath = classPath.substring(1); // Windows error only?
File classPathFile = new File(classPath);
String parentPath = classPathFile.getParent()+"/";
clientDataPath = parentPath+"clientData/";
playerGraphicsPath = clientDataPath+"playerGraphics/";
}
public String playerGraphicsPath() {
return playerGraphicsPath;
}
public String imageFileEnding() {
return imageFileEnding;
}
public int fieldWidth() {
return fieldWidth;
}
public int fieldHeight() {
return fieldHeight;
}
public int deltaLevelAmplitude() {
return deltaLevelAmplitude;
}
public int numberOfDeltaLevelStates() {
return 2*deltaLevelAmplitude+1;
}
public String host() {
return host;
}
public int port() {
return port;
}
public int frameRate() {
return frameRate;
}
public int delayBetweenFrames() {
return 1000 / frameRate;
}
public String checkPaths() {
File playerGraphicsDir = new File(playerGraphicsPath);
if (! (playerGraphicsDir.exists() && playerGraphicsDir.isDirectory())) {
String error = "Player graphics directory "+playerGraphicsPath+" doesn't exist!";
System.out.println("WARNING: "+error);
return error;
}
return null;
}
}
Faunis/src/client/PlayerGraphics.java 0000644 0001750 0001750 00000013572 12061420306 016650 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.awt.Graphics;
import java.awt.Point;
import java.util.List;
import communication.GraphicalPlayerStatus;
import communication.GraphicsContentManager;
import communication.enums.AniCompoType;
import communication.enums.BodyPart;
import communication.enums.CharacterClass;
import communication.enums.Direction;
import communication.movement.Moveable;
import communication.movement.MovingTask;
import communication.movement.Path;
/** Represents the graphics of a player. It can be completely created just from
* the player's GraphicalPlayerStatus and the client's GraphicsContentManager.
* Any additional fields here don't have to be stored at / distributed over
* the server and can be filled of each client during construction of playerGraphics. */
public class PlayerGraphics implements Moveable, Animateable {
private GraphicalPlayerStatus info;
private Client parent;
private int deltaLevel = 0; // between -1 and +1
private int frame = 0;
public PlayerGraphics(GraphicalPlayerStatus info, Client parent) {
this.info = info;
this.parent = parent;
}
private Bone getBone(BodyPart part) {
String animation;
if (hasPath())
animation = "walk";
else if (info.currentEmote != null)
animation = info.currentEmote;
else
animation = "stand";
return parent.getGraphicsContentManager().
getBone(info.type, animation, part, info.direction);
}
public void draw(Graphics drawOnto, int x, int y, int frameIndex) {
// Decide whether we have a limbed or compact type
GraphicsContentManager graphicsContentManager = parent.getGraphicsContentManager();
AniCompoType compoType =
graphicsContentManager.getCompositionType(info.type);
List orderList;
if (compoType == AniCompoType.compact) {
orderList = graphicsContentManager.getCompactDrawingOrder();
} else {
orderList = graphicsContentManager.
getDrawingOrders().get(this.getDirection());
}
assert(orderList != null);
drawRecursive(BodyPart.body, null, x, y, drawOnto,
orderList, frameIndex);
}
private void drawRecursive(BodyPart currentPart, BodyPart sourcePart, int originX, int originY,
Graphics drawOnto, List orderList, int frameIndex) {
// we need sourcePart only for preventing going backwards recursively
Bone currentBone = getBone(currentPart);
if (currentBone == null) {
System.out.println("Couldn't find bone for "+currentPart+"!");
}
Point offset = currentBone.getConnectionOffset(currentPart, frameIndex);
if (offset == null) {
System.out.println("Couldn't find offset for "+currentPart+", frame "+frameIndex+"!");
}
assert(offset != null);
for (BodyPart toDraw : orderList) {
for (BodyPart part : currentBone.getConnectionOffsets(frameIndex)) {
if (part != sourcePart && part == toDraw) {
Point partOffset = currentBone.getConnectionOffset(part, frameIndex);
drawRecursive(part, currentPart, originX-(offset.x-partOffset.x),
originY-(offset.y-partOffset.y),
drawOnto, orderList, frameIndex);
}
if (toDraw == currentPart) {
currentBone.draw(drawOnto, originX-offset.x, originY-offset.y, frameIndex);
}
}
}
}
public CharacterClass getType() {
return info.type;
}
@Override
public Path getPath() {
return info.path;
}
@Override
public void setPath(Path path) {
info.path = path;
}
@Override
public int getX() {
return info.x;
}
@Override
public int getY() {
return info.y;
}
@Override
public void resetPath() {
info.path = null;
this.deltaLevel = 0;
System.out.println("Reset path.");
}
@Override
public void resetEmote() {
info.currentEmote = null;
this.frame = 0;
System.out.println("Set frame to 0.");
}
public int getDeltaLevel() {
return this.deltaLevel;
}
public void setDeltaLevel(int deltaLevel) {
this.deltaLevel = deltaLevel;
}
@Override
public boolean hasPath() {
return info.hasPath();
}
public String getName() {
return info.name;
}
public Direction getDirection() {
return info.direction;
}
public void setDirection(Direction direction) {
if (direction == null)
System.out.println("ERROR: setDirection(null)");
assert(direction != null);
info.direction = direction;
}
@Override
public String getEmote() {
return info.currentEmote;
}
@Override
public void setEmote(String emote) {
info.currentEmote = emote;
}
@Override
public boolean hasEmote() {
return info.hasEmote();
}
@Override
public int getFrame() {
return frame;
}
@Override
public void setFrame(int frame) {
this.frame = frame;
}
/** Directly sets the coordinates of this playerGraphics to the given ones.
* If given flag adaptDirection is set, sets the direction accordingly. */
@Override
public void moveAbsolute(int x, int y, boolean adaptDirection) {
if (adaptDirection) {
Direction newDirection = MovingTask.deltaToDirection(
x-info.x, y-info.y);
if (newDirection != null)
info.direction = newDirection;
}
info.x = x;
info.y = y;
}
}
Faunis/src/client/SwingMessageRunnable.java 0000644 0001750 0001750 00000002511 12062726732 020022 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
public class SwingMessageRunnable implements Runnable {
private String message;
private MessageType type;
private GameWindow win;
public SwingMessageRunnable(String message, MessageType type, GameWindow win) {
this.message = message;
this.type = type;
this.win = win;
}
@Override
public void run() {
switch(type) {
case error:
win.logErrorMessage(message);
break;
case system:
win.logSystemMessage(message);
break;
case whisper:
win.logWhisperMessage(message);
break;
case broadcast:
win.logBroadcastMessage(message);
break;
}
}
}
Faunis/src/client/SwingMessageRunnable.java~ 0000644 0001750 0001750 00000001145 12062631610 020210 0 ustar user user package client;
public class SwingMessageRunnable implements Runnable {
private String message;
private MessageType type;
private GameWindow win;
public SwingMessageRunnable(String message, MessageType type, GameWindow win) {
this.message = message;
this.type = type;
this.win = win;
}
@Override
public void run() {
switch(type) {
case error:
win.logErrorMessage(message);
break;
case system:
win.logSystemMessage(message);
break;
case whisper:
win.logWhisperMessage(message);
break;
case broadcast:
win.logBroadcastMessage(message);
break;
}
}
}
Faunis/src/client/MessageType.java 0000644 0001750 0001750 00000001464 12062726764 016200 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
public enum MessageType {
error, system, whisper, broadcast
}
Faunis/src/client/AnimationData.java 0000644 0001750 0001750 00000002122 12061420306 016431 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import communication.enums.AniEndType;
public class AnimationData {
public int numberOfFrames;
public AniEndType endType;
// AniCompoType not included since it's per CharacterClass,
// not per Animation atm
public AnimationData(int numberOfFrames, AniEndType endType) {
this.numberOfFrames = numberOfFrames;
this.endType = endType;
}
}
Faunis/src/client/Animator.java 0000644 0001750 0001750 00000007213 12061420306 015500 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.util.Timer;
import java.util.TimerTask;
import communication.enums.AniEndType;
public class Animator {
protected Object runningMutexKey;
protected boolean stopRunning;
protected AnimatorManager parent;
protected Animateable animateable;
private Timer timer;
private AnimatingTask animatingTask;
protected AniEndType endType;
private long interval;
protected int maxFrameIndex;
/** Creates an Animator, but doesn't start it yet. */
public Animator(AnimatorManager parent, Animateable animateable, long delay,
AniEndType endType, int maxFrameIndex) {
this.parent = parent;
this.animateable = animateable;
this.timer = new Timer();
this.animatingTask = new AnimatingTask();
this.runningMutexKey = new Object();
this.stopRunning = false;
this.endType = endType;
this.interval = delay;
this.maxFrameIndex = maxFrameIndex;
}
/** Starts the Animator. Has to be explicitely called,
* as that isn't done automatically.
* HINT: Doesn't register this Animator at the Mapman, because that must
* already happen while creating the Animator. */
public void start() {
this.timer.scheduleAtFixedRate(animatingTask, 0, interval);
}
public Animateable getAnimateable() {
return animateable;
}
/** asserts that at the method end, no
* timerTask is running. */
public void stop() {
this.timer.cancel();
synchronized(runningMutexKey) {
stopRunning = true;
}
synchronized(animateable) {
animateable.resetEmote();
}
}
public void stopAndUnregister() {
this.stop();
parent.unregisterAnimator(animateable);
}
protected class AnimatingTask extends TimerTask {
@Override
public void run() {
// increase animateable's frame index and
// unregister at parent when animation is over
synchronized(animateable) {
if (!this.isAnimationFinished()) {
this.animate();
return;
}
}
// else:
Object list = parent.getSynchroStuffForAnimatorStop();
assert(list != null);
synchronized(list) {
synchronized(animateable) {
if (this.isAnimationFinished())
stopAndUnregister();
}
}
}
private boolean isAnimationFinished() {
int frame = animateable.getFrame();
if (frame >= maxFrameIndex && endType == AniEndType.revert)
return true;
return false;
}
private void animate() {
synchronized(animateable) {
synchronized(runningMutexKey) {
if (stopRunning)
return;
int frame = animateable.getFrame();
switch (endType) {
case repeat:
System.out.print("Increase frame from "+frame);
frame++;
if (frame > maxFrameIndex)
frame = 0;
System.out.println(" to "+frame);
animateable.setFrame(frame);
break;
case revert:
case end:
if (frame < maxFrameIndex) {
System.out.print("Increase frame from "+frame+" to "+(frame+1));
animateable.setFrame(frame+1);
}
break;
}
}
}
}
}
}
Faunis/src/client/Bone.java 0000644 0001750 0001750 00000007030 12061420306 014606 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import javax.imageio.ImageIO;
import communication.enums.BodyPart;
public class Bone {
private ArrayList images;
private ArrayList> connectionOffsets;
private static ColourBoneTranslator translator = ColourBoneTranslator.getInstance();
private boolean isAnimation;
public Bone(String filePath) {
images = new ArrayList();
try {
File graphicFile = new File(filePath);
BufferedImage image = ImageIO.read(graphicFile);
images.add(image);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Couldn't load picture!");
}
isAnimation = false;
connectionOffsets = new ArrayList>();
readConnectionOffsets();
}
public Bone(String animationPrefix, String fileEnding, int maxFrameIndex) {
images = new ArrayList();
for (int frameIndex = 0; frameIndex <= maxFrameIndex; frameIndex++) {
File frameFile = new File(animationPrefix+frameIndex+fileEnding);
try {
BufferedImage frame = ImageIO.read(frameFile);
images.add(frame);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't load frame!");
}
// TODO
}
isAnimation = true;
connectionOffsets = new ArrayList>();
readConnectionOffsets();
}
public boolean isAnimation() {
return isAnimation;
}
public void readConnectionOffsets() {
for (int frameIndex = 0; frameIndex < images.size(); frameIndex++) {
BufferedImage image = images.get(frameIndex);
HashMap offsets = new HashMap();
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color readColour = new Color(image.getRGB(x, y), true);
BodyPart part = translator.translate(readColour);
if (part != null) {
offsets.put(part, new Point(x, y));
}
}
}
connectionOffsets.add(offsets);
}
}
public Point getConnectionOffset(BodyPart part, int frameIndex) {
assert(connectionOffsets.get(frameIndex).containsKey(part));
return connectionOffsets.get(frameIndex).get(part);
}
public Set getConnectionOffsets(int frameIndex) {
return connectionOffsets.get(frameIndex).keySet();
}
public BufferedImage getImage(int frameIndex) {
return images.get(frameIndex);
}
public void draw(Graphics drawOnto, int x, int y, int frameIndex) {
drawOnto.drawImage(this.images.get(frameIndex), x, y, null);
}
}
Faunis/src/client/GraphWin.java 0000644 0001750 0001750 00000006467 12062201146 015457 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import javax.swing.*;
public abstract class GraphWin {
public boolean repaint = false;
public JFrame win;
public DrawingPanel drawingPanel;
public BufferedImage img;
public Graphics graph;
private int width;
private int height;
@SuppressWarnings("unused")
private String title;
public GraphWin(int width, int height, String title){
// initialize GraphWin
this.width = width;
this.height = height;
this.title = title;
this.drawingPanel = new DrawingPanel();
drawingPanel.setIgnoreRepaint(true);
this.img = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
this.graph = this.img.createGraphics();
drawingPanel.parent = this;
win = new JFrame();
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setSize(width, height);
win.setResizable(false);
win.setTitle(title);
win.getContentPane().add(BorderLayout.CENTER, drawingPanel);
}
public void show(){
win.setVisible(true);
}
public void hide(){
win.setVisible(false);
}
public void setTitle(String title){
win.setTitle(title);
}
public String getTitle(){
return win.getTitle();
}
public int getWidth(){
return this.width;
}
public int getHeight(){
return this.height;
}
public abstract void draw();
public Point mousePos(){
return drawingPanel.getMousePosition();
}
public void repaint(){
drawingPanel.repaint();
}
public void setColor(Color c){
graph.setColor(c);
}
public void setColor(int red, int green, int blue){
Color temp = new Color(red, green, blue);
graph.setColor(temp);
}
public Color getColor(){
return graph.getColor();
}
public int getRed(){
return graph.getColor().getRed();
}
public int getGreen(){
return graph.getColor().getGreen();
}
public int getBlue(){
return graph.getColor().getBlue();
}
public void clear(){
Color temp = graph.getColor();
graph.setColor(Color.white);
graph.fillRect(0, 0, this.getWidth(), this.getHeight());
graph.setColor(temp);
}
public static void delay(long millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
System.out.println("could not delay!");
e.printStackTrace();
}
}
public class DrawingPanel extends JPanel{
private static final long serialVersionUID = 1L;
public GraphWin parent;
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(parent.img, 0, 0, this);
}
}
}
Faunis/src/client/Animateable.java 0000644 0001750 0001750 00000001707 12061157302 016135 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
public interface Animateable {
public boolean hasEmote();
public String getEmote();
public void setEmote(String emote);
public void resetEmote();
public int getFrame();
public void setFrame(int frame);
}
Faunis/src/client/Client.java 0000644 0001750 0001750 00000060324 12062632226 015155 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.SwingUtilities;
import communication.GraphicalPlayerStatus;
import communication.GraphicsContentManager;
import communication.MapInfo;
import communication.butlerToClientOrders.*;
import communication.clientToButlerOrders.*;
import communication.enums.AniEndType;
import communication.enums.ClientStatus;
import communication.movement.Moveable;
import communication.movement.Mover;
import communication.movement.MoverManager;
import communication.movement.MovingTask;
import communication.movement.SoftMovingTask;
public class Client implements MoverManager, AnimatorManager {
private Object connectionModiMutexKey;
private boolean connectionModiOccupied;
private ClientStatus clientStatus;
private ClientSettings clientSettings;
private GraphicsContentManager graphicsContentManager;
private Socket socket;
private ObjectOutputStream output;
protected ObjectInputStream input;
private GameWindow win;
private String activePlayerName;
private String currentMapName;
private HashMap currentPlayers;
private HashMap movingPlayerGraphics;
private HashMap animatedPlayerGraphics;
private Thread serverThread; // steadily replies to requests from the server side
private ServerRunnable serverRunnable; // the serverThread's job
protected boolean stopServerThread;
public final static FileFilter directoryFilter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
};
public static void main(String[] args){
new Client().init();
}
private void run(){
while(true){
GraphWin.delay(clientSettings.delayBetweenFrames());
win.draw();
win.repaint();
}
}
public void init() {
// setup (dis-)connection management stuff:
this.connectionModiMutexKey = new Object();
this.connectionModiOccupied = false;
this.stopServerThread = false;
// set initial clientStatus:
clientStatus = ClientStatus.disconnected;
// create game window:
/* NOTE: Swing is single-threaded. And as I have learned, it
* causes problems even when only one thread is accessing it at all.
* So we introduce the "good" / complicated way to deal with Swing
* stuff: Runnables for the Event Dispatching Thread (EDT).
*/
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
win = new GameWindow(Client.this, 800, 500, "Faunis");
win.show();
}
});
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logSystemMessage("Welcome to Faunis!");
logSystemMessage("Copyright 2012 Simon Ley alias \"skarute\"");
logSystemMessage("Licensed under GNU AGPL v3 or later");
// load client settings:
clientSettings = new ClientSettings();
// check if clientSettings paths exist:
String pathErrorMsg = clientSettings.checkPaths();
if (pathErrorMsg != null)
logErrorMessage(pathErrorMsg);
// load data:
graphicsContentManager = new GraphicsContentManager(
clientSettings.playerGraphicsPath(), clientSettings.imageFileEnding());
graphicsContentManager.loadResourcesForClient();
currentPlayers = new HashMap();
this.movingPlayerGraphics = new HashMap();
this.animatedPlayerGraphics = new HashMap();
this.serverRunnable = new ServerRunnable(this);
connect();
this.run();
}
public PlayerGraphics getPlayerGraphics(String name) {
synchronized(currentPlayers) {
assert(currentPlayers.containsKey(name));
return currentPlayers.get(name);
}
}
public String getCurrentMapName() {
return currentMapName;
}
public String getCurrentPlayerName() {
return activePlayerName;
}
public ClientSettings getClientSettings() {
return clientSettings;
}
@Override
public Object[] getSynchroStuffForMoverStop() {
return new Object[] {movingPlayerGraphics, animatedPlayerGraphics};
}
@Override
public Object getSynchroStuffForAnimatorStop() {
return new Object[] {animatedPlayerGraphics};
}
public GraphicsContentManager getGraphicsContentManager() {
return graphicsContentManager;
}
public ClientStatus getClientStatus() {
return clientStatus;
}
/** locks connectionModiMutexKey; clientStatus */
public boolean connect() {
synchronized(connectionModiMutexKey) {
if (connectionModiOccupied) {
return false;
} else {
connectionModiOccupied = true;
}
}
synchronized(clientStatus) {
if (this.clientStatus != ClientStatus.disconnected) {
logErrorMessage("Couldn't connect since clientStatus is not set to \"disconnected\"!");
connectionModiOccupied = false;
return false;
}
assert(socket == null || socket.isClosed());
// create socket:
try {
this.socket = new Socket(clientSettings.host(), clientSettings.port());
} catch (Exception e) {
logErrorMessage("Socket to game server couldn't be created! Reason: "+e.getLocalizedMessage());
connectionModiOccupied = false;
return false;
}
System.out.println("Socket created.");
// create input and output stream:
System.out.println("Try to create input / output streams...");
try {
this.input = new ObjectInputStream(this.socket.getInputStream());
this.output = new ObjectOutputStream(this.socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
logErrorMessage("Couldn't create input / output!");
connectionModiOccupied = false;
return false;
}
clientStatus = ClientStatus.loggedOut;
System.out.println("Input / output have been created.");
System.out.println("Start serverThread...");
stopServerThread = false;
assert(serverThread == null || !serverThread.isAlive());
this.serverThread = new Thread(this.serverRunnable, "client_serverThread");
this.serverThread.start();
connectionModiOccupied = false;
return true;
}
}
/** locks connectionModiMutexKey; clientStatus, currentPlayers, movingPlayerGraphics */
public boolean disconnect() {
System.out.println("Client: called disconnect()");
synchronized(connectionModiMutexKey) {
if (connectionModiOccupied) {
System.out.println("Client: disconnect(): Connection is already being modified?");
return false;
} else {
connectionModiOccupied = true;
}
}
synchronized(clientStatus) {
if (this.clientStatus == ClientStatus.disconnected) {
logErrorMessage("Client: Couldn't disconnect as clientStatus is already \"disconnected\"");
connectionModiOccupied = false;
return false;
}
// Cause serverThread to terminate
assert(serverThread.isAlive());
stopServerThread = true;
try {
this.input.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Couldn't choke serverThread!");
}
if (Thread.currentThread() != this.serverThread) {
System.out.println("disconnect(): Caller isn't serverThread");
while (this.serverThread.isAlive()) {}
assert(!serverThread.isAlive());
System.out.println("Client: serverThread terminated.");
} else {
System.out.println("disconnect(): Caller is serverThread");
}
stopServerThread = false;
unloadMap();
clientStatus = ClientStatus.disconnected;
input = null;
output = null;
serverThread = null;
connectionModiOccupied = false;
System.out.println("Client: Connection to server terminated.");
return true;
}
}
// Server to client section: #################################################################
class ServerRunnable implements Runnable {
private Client parent;
public ServerRunnable(Client parent) {
this.parent = parent;
}
@Override
public void run() {
// handle Order from parent.input:
while (!stopServerThread) {
Object read = null;
try {
read = parent.input.readObject();
} catch (IOException e) {
logErrorMessage("Couldn't read anymore from server!");
if (!stopServerThread) disconnect();
System.out.println("serverRunnable ends.");
return;
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("ClassNotFoundException!");
}
if (read != null && read instanceof BCOrder)
parent.handleServerOrder((BCOrder)read);
}
System.out.println("serverRunnable ends.");
}
}
/** locks movingPlayerGraphics, animatedPlayerGraphics, playerGraphics */
private void tryStopMovement(PlayerGraphics playerGraphics) {
synchronized(movingPlayerGraphics) {
synchronized(animatedPlayerGraphics) {
synchronized(playerGraphics) {
Mover mover = movingPlayerGraphics.get(playerGraphics);
if (mover != null) {
mover.stop();
unregisterMover(playerGraphics);
// TODO: I think we can sum the above to mover.stopAndUnregister();
System.out.println("Movement stopped.");
}
}
}
}
}
/** locks animatedPlayerGraphics, playerGraphics
* Stops animation, removes emote from playerGraphics and resets frame counter. */
private void tryStopAnimation(PlayerGraphics playerGraphics) {
synchronized(animatedPlayerGraphics) {
synchronized(playerGraphics) {
Animator animator = animatedPlayerGraphics.get(playerGraphics);
if (animator != null) {
animator.stop();
unregisterAnimator(playerGraphics);
// TODO: I think we can sum the above to animator.stopAndUnregister();
System.out.println("Animation stopped.");
}
}
}
}
public void handleServerOrder(BCOrder order) {
assert(order != null);
if (order instanceof BCAddCharOrder) {
addChar((BCAddCharOrder) order);
} else if (order instanceof BCChangeCharOrder) {
changeChar((BCChangeCharOrder) order);
} else if (order instanceof BCRemoveCharOrder) {
removeChar((BCRemoveCharOrder) order);
} else if (order instanceof BCSetMapOrder) {
setMap((BCSetMapOrder) order);
} else if (order instanceof BCSetClientStatusOrder) {
setClientStatus((BCSetClientStatusOrder) order);
} else if (order instanceof BCErrorMessageOrder) {
logErrorMessage((BCErrorMessageOrder) order);
} else if (order instanceof BCSystemMessageOrder) {
logSystemMessage((BCSystemMessageOrder) order);
} else if (order instanceof BCChatMessageOrder) {
showChatMessage((BCChatMessageOrder) order);
} else {
System.out.println("Received unknown server order!");
}
// TODO: Implement the handling of further server orders
}
public void showChatMessage(BCChatMessageOrder order) {
if (order.isBroadcast()) {
SwingUtilities.invokeLater(
new SwingMessageRunnable(order.getFromName()+": "
+order.getMessage(), MessageType.broadcast, win));
} else {
SwingUtilities.invokeLater(
new SwingMessageRunnable(order.getFromName()+": "
+order.getMessage(), MessageType.whisper, win));
}
}
public void logErrorMessage(BCErrorMessageOrder order) {
logErrorMessage(order.getMessage());
}
public void logErrorMessage(String errorMessage) {
SwingUtilities.invokeLater(
new SwingMessageRunnable("ERROR: "+errorMessage, MessageType.error, win));
}
public void logSystemMessage(BCSystemMessageOrder order) {
logSystemMessage(order.getMessage());
}
public void logSystemMessage(String systemMessage) {
SwingUtilities.invokeLater(
new SwingMessageRunnable(systemMessage, MessageType.system, win));
}
/** locks currentPlayers, movingPlayerGraphics, playerGraphics */
public void addChar(BCAddCharOrder order) {
GraphicalPlayerStatus status = order.getGraphStatus();
PlayerGraphics playerGraphics = new PlayerGraphics(status, this);
String playerName = order.getPlayerName();
// Add playerGraphics:
synchronized(currentPlayers) {
assert(! currentPlayers.containsKey(playerName));
currentPlayers.put(playerName, playerGraphics);
}
// If the new playerGraphics is moving, create Mover and add it:
if (playerGraphics.hasPath()) {
tryStartMovement(playerGraphics);
} else if (playerGraphics.hasEmote()) {
tryStartAnimation(playerGraphics, playerGraphics.getEmote());
}
}
/** locks currentPlayers; movingPlayerGraphics; currentPlayers */
public void removeChar(BCRemoveCharOrder order) {
String playerName = order.getPlayerName();
PlayerGraphics playerGraphics;
synchronized(currentPlayers) {
assert(currentPlayers.containsKey(playerName));
playerGraphics = currentPlayers.get(playerName);
}
// Stop possible earlier animation and movement:
tryStopMovement(playerGraphics);
tryStopAnimation(playerGraphics);
synchronized(currentPlayers) {
assert(currentPlayers.containsKey(playerName));
currentPlayers.remove(playerName);
}
}
/** locks currentPlayers; movingPlayerGraphics; currentPlayers */
public void changeChar(BCChangeCharOrder order) {
GraphicalPlayerStatus status = order.getGraphStatus();
PlayerGraphics playerGraphics = new PlayerGraphics(status, this);
String playerName = order.getPlayerName();
PlayerGraphics oldGraphics = null;
synchronized(currentPlayers) {
assert(currentPlayers.containsKey(playerName));
oldGraphics = currentPlayers.get(playerName);
}
System.out.println("old x="+oldGraphics.getX()+", y="+oldGraphics.getY());
System.out.println("new x="+playerGraphics.getX()+", y="+playerGraphics.getY());
// If playerGraphics was moving before, remove Mover:
tryStopMovement(oldGraphics);
tryStopAnimation(oldGraphics);
// see NOTE above
// Replace playerGraphics
synchronized(currentPlayers) {
assert(currentPlayers.containsKey(playerName));
currentPlayers.put(playerName, playerGraphics);
}
// If the new playerGraphics is moving, create Mover and add it:
if (playerGraphics.hasPath()) {
tryStartMovement(playerGraphics);
} else if (playerGraphics.hasEmote()) {
tryStartAnimation(playerGraphics, playerGraphics.getEmote());
}
}
/** locks movingPlayerGraphics, animatedPlayerGraphics, playerGraphics
* Also starts walking animation. */
public void tryStartMovement(PlayerGraphics playerGraphics) {
synchronized(movingPlayerGraphics) {
synchronized(animatedPlayerGraphics) {
synchronized(playerGraphics) {
// TODO: assert that there doesn't already exist a Mover!
if (movingPlayerGraphics.containsKey(playerGraphics)) {
System.out.println("There already exists a Mover!");
return;
}
if (playerGraphics.hasPath() && !playerGraphics.getPath().isEmpty()) {
System.out.print("Start movement...");
int numDeltaLevels = clientSettings.numberOfDeltaLevelStates();
Mover mover = new Mover(this, playerGraphics, 500/numDeltaLevels);//TODO: Zeiteinheit
MovingTask movingTask = new SoftMovingTask(mover, playerGraphics);
mover.setMovingTask(movingTask);
movingPlayerGraphics.put(playerGraphics, mover);
mover.start();
System.out.println(" movement started. Start animation too...");
tryStartAnimation(playerGraphics, "walk");
}
}
}
}
}
/** locks animatedPlayerGraphics, playerGraphics
* Starts the given animation. */
public void tryStartAnimation(PlayerGraphics playerGraphics, String animation) {
synchronized(animatedPlayerGraphics) {
synchronized(playerGraphics) {
// assert that there doesn't already exist an Animator!
if (animatedPlayerGraphics.containsKey(playerGraphics)) {
System.out.println("There already exists a Animator!");
return;
}
System.out.print("Start animation...");
AnimationData animationData = graphicsContentManager.getAnimationData(playerGraphics.getType(),
animation);
AniEndType endType = animationData.endType;
int maxFrameIndex = animationData.numberOfFrames-1;
// TODO: Get animation interval
Animator animator = new Animator(this, playerGraphics, 100, endType, maxFrameIndex);
animatedPlayerGraphics.put(playerGraphics, animator);
animator.start();
System.out.println(" animation started.");
}
}
}
/** locks currentPlayers, movingPlayerGraphics */
private void unloadMap() {
synchronized(currentPlayers) {
synchronized(movingPlayerGraphics) {
for (String playerName : currentPlayers.keySet()) {
PlayerGraphics playerGraphics = currentPlayers.get(playerName);
tryStopMovement(playerGraphics);
}
currentPlayers.clear();
activePlayerName = null;
currentMapName = null;
}
}
}
/** locks currentPlayers, movingPlayerGraphics
* A new map will be loaded: Remove all movements and
* playerGraphics and register them anew from the MapInfo */
public void setMap(BCSetMapOrder order) {
MapInfo mapInfo = order.getMapInfo();
synchronized(currentPlayers) {
synchronized(movingPlayerGraphics) {
unloadMap();
// everything is unloaded, now load:
currentMapName = mapInfo.mapName;
activePlayerName = order.getActivePlayerName();
for (String playerName2 : mapInfo.players.keySet()) {
GraphicalPlayerStatus status = mapInfo.players.get(playerName2);
PlayerGraphics playerGraphics = new PlayerGraphics(status, this);
currentPlayers.put(playerName2, playerGraphics);
tryStartMovement(playerGraphics);
}
}
}
}
/** locks clientStatus, currentPlayers, movingPlayerGraphics */
public void setClientStatus(BCSetClientStatusOrder order) {
ClientStatus newStatus = order.getNewStatus();
if (newStatus == ClientStatus.disconnected) {
// Don't call disconnect() here as the serverRunnable cannot
// force its termination at this point. Instead
// we'll just close the input, causing this serverRunnable to
// call disconnect() in its main loop where it can terminate.
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Couldn't choke serverThread!");
}
return;
}
synchronized(clientStatus) {
if (( clientStatus == ClientStatus.exploring
|| clientStatus == ClientStatus.fighting)
&& newStatus != ClientStatus.exploring
&& newStatus != ClientStatus.fighting)
{
unloadMap();
}
this.clientStatus = newStatus;
}
}
// ################################################################################
/** locks clientStatus, output
* Tries to send given ClientOrder to the own Butler, and returns
* the success thereof as a boolean.*/
public boolean sendOrder(CBOrder c){
synchronized(clientStatus) {
if (clientStatus == ClientStatus.disconnected) {
logErrorMessage("Client: Couldn't send order since there's no connection!");
return false;
}
assert(output != null);
synchronized(output) {
try {
output.writeObject(c);
} catch (IOException e) {
logErrorMessage("Error while sending ClientOrder");
return false;
}
return true;
}
}
}
public boolean parseCommand(String commandPrefix, String[] commandSplitDetails) {
if (commandPrefix.equals("/c")) {
connect();
return true;
} else if (commandPrefix.equals("/i") && commandSplitDetails.length >= 2) {
String loginName = commandSplitDetails[0];
String loginPassword = commandSplitDetails[1];
sendOrder(new CBLoginOrder(loginName, loginPassword));
return true;
} else if (commandPrefix.equals("/o")) {
sendOrder(new CBLogoutOrder());
return true;
} else if (commandPrefix.equals("/x")) {
sendOrder(new CBDisconnectOrder());
return true;
} else if (commandPrefix.equals("/w") && commandSplitDetails.length >= 2) {
String receiver = commandSplitDetails[0];
String message = concatenateHelper(commandSplitDetails, 1);
sendOrder(new CBChatOrder(message, receiver));
return true;
} else if (commandPrefix.equals("/b") && commandSplitDetails.length >= 1) {
String message = concatenateHelper(commandSplitDetails, 0);
sendOrder(new CBChatOrder(message, null));
return true;
} else if (commandPrefix.equals("/l")) {
String playerName = commandSplitDetails[0];
sendOrder(new CBLoadPlayerOrder(playerName));
return true;
} else if (commandPrefix.equals("/u")) {
sendOrder(new CBUnloadPlayerOrder());
return true;
} else if (commandPrefix.equals("/n") && commandSplitDetails.length >= 1) {
String playerName = commandSplitDetails[0];
sendOrder(new CBCreatePlayerOrder(playerName));
return true;
} else if (commandPrefix.equals("/m") && commandSplitDetails.length >= 2) {
int walkX, walkY;
try {
walkX = Integer.parseInt(commandSplitDetails[0]);
walkY = Integer.parseInt(commandSplitDetails[1]);
} catch(NumberFormatException e) {
logErrorMessage("Error while parsing the numbers.");
return false;
}
sendOrder(new CBMoveOrder(walkX, walkY));
return true;
} else if (commandPrefix.equals("/e")) {
String emoteName = null;
if (commandSplitDetails.length >= 1) {
emoteName = commandSplitDetails[0];
}
sendOrder(new CBTriggerEmoteOrder(emoteName));
return true;
} else if (commandPrefix.equals("/s")) {
sendOrder(new CBServerSourceOrder());
return true;
} else {
logErrorMessage("Command couldn't be interpreted.");
return false;
}
// TODO: handle further commands
}
/** locks movingPlayerGraphics, animatedPlayerGraphics
* Unregisters the Mover for given playerGraphics after movement has stopped.
* Also unregisters the Animator.
* Do not call this to stop movement, but call tryStopMovement() instead! */
@Override
public void unregisterMover(Moveable moveable) {
System.out.println("Unregistering Mover...");
assert(moveable instanceof PlayerGraphics);
PlayerGraphics playerGraphics = (PlayerGraphics) moveable;
synchronized(movingPlayerGraphics) {
tryStopAnimation(playerGraphics);
movingPlayerGraphics.remove(playerGraphics);
}
}
/** locks animatedPlayerGraphics
* Unregisters the Animator for given playerGraphics after animation has
* stopped.
Do not call this to stop animation, but call tryStopAnimation() instead! */
@Override
public void unregisterAnimator(Animateable animateable) {
assert(animateable instanceof PlayerGraphics);
PlayerGraphics playerGraphics = (PlayerGraphics) animateable;
synchronized(animatedPlayerGraphics) {
animatedPlayerGraphics.remove(playerGraphics);
}
}
/** locks currentPlayers */
public ArrayList getAllGraphicsToDraw() {
ArrayList result = new ArrayList();
if (currentPlayers == null) return result;
synchronized(currentPlayers) {
for (PlayerGraphics playerGraphics : currentPlayers.values()) {
result.add(playerGraphics);
}
}
return result;
}
private static String concatenateHelper(String[] array, int startIndex) {
assert(startIndex < array.length);
StringBuilder message = new StringBuilder();
message.append(array[startIndex]);
for (int i = startIndex+1; i < array.length; i++) {
message.append(" ");
message.append(array[i]);
}
return message.toString();
}
}
Faunis/src/client/ColourBoneTranslator.java 0000644 0001750 0001750 00000003413 12061420306 020045 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package client;
import java.awt.Color;
import java.util.HashMap;
import communication.enums.BodyPart;
public class ColourBoneTranslator {
private HashMap colourToBone;
private static ColourBoneTranslator instance;
public static ColourBoneTranslator getInstance() {
if (instance == null)
instance = new ColourBoneTranslator();
return instance;
}
private ColourBoneTranslator() {
colourToBone = new HashMap();
colourToBone.put(Color.MAGENTA, BodyPart.head);
colourToBone.put(Color.ORANGE, BodyPart.body);
colourToBone.put(Color.GRAY, BodyPart.tail);
colourToBone.put(Color.GREEN, BodyPart.rightArm);
colourToBone.put(Color.CYAN, BodyPart.leftArm);
colourToBone.put(Color.RED, BodyPart.rightLeg);
colourToBone.put(Color.BLUE, BodyPart.leftLeg);
//colourToBone.put(Color.YELLOW, BodyPart.walkOffset);
// Color.orange RGB: 255, 200, 0
}
public BodyPart translate(Color pixelColour) {
return colourToBone.get(pixelColour);
}
}
Faunis/src/server/ 0000775 0001750 0001750 00000000000 12062730610 013115 5 ustar user user Faunis/src/server/Player.java 0000644 0001750 0001750 00000007244 12061420306 015216 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.io.Serializable;
import communication.GraphicalPlayerStatus;
import communication.enums.CharacterClass;
import communication.enums.Direction;
import communication.movement.Moveable;
import communication.movement.MovingTask;
import communication.movement.Path;
/** Represents a playable character on the server side. */
public class Player implements Serializable, Moveable {
private static final long serialVersionUID = 1L;
private String name; // unique player name
private CharacterClass type;
private String currentMapName;
private String accountName;
private int x;
private int y;
private Direction direction;
private Path path;
private String currentEmote; // do not store emotes of end type "revert" here!
public Player(String name, CharacterClass type, String currentMapName, String accountName) {
this.name = name;
this.type = type;
this.currentMapName = currentMapName;
this.accountName = accountName;
this.direction = Direction.down;
this.x = 5;
this.y = 5;
this.path = null;
}
/** IMPORTANT: GraphicalPlayerStatus has to be independent from the player,
* that means no shared references etc.! */
public GraphicalPlayerStatus getGraphicalPlayerStatus() {
// TODO
GraphicalPlayerStatus graphStatus = new GraphicalPlayerStatus();
graphStatus.name = this.name;
graphStatus.type = this.type;
graphStatus.direction = this.direction;
graphStatus.path = (this.path == null)? null : this.path.copy();
graphStatus.currentEmote = this.currentEmote; // immutable :o]
graphStatus.x = this.x;
graphStatus.y = this.y;
return graphStatus;
}
// getters and setters:
public String getMapName() {
return currentMapName;
}
public CharacterClass getType() {
return type;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getName(){
return this.name;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public void resetPath() {
this.path = null;
}
public boolean hasPath() {
return (path != null);
}
public String getCurrentEmote() {
return currentEmote;
}
public void setCurrentEmote(String emote) {
currentEmote = emote;
}
public void resetEmote() {
this.currentEmote = null;
}
public boolean isAnimating() {
return (currentEmote != null);
}
public void moveAbsolute(int toX, int toY, boolean adaptDirection) {
if (adaptDirection) {
Direction newDirection = MovingTask.deltaToDirection(
toX-this.x, toY-this.y);
if (newDirection != null)
this.direction = newDirection;
}
this.x = toX;
this.y = toY;
}
public void setMapName(String mapName) {
this.currentMapName = mapName;
}
}
Faunis/src/server/MainServer.java 0000644 0001750 0001750 00000046026 12061740004 016036 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import javax.swing.JFrame;
import server.mapmanToButlerOrders.MBStopThreadOrder;
import communication.GraphicsContentManager;
import communication.Map;
import communication.enums.CharacterClass;
import communication.movement.Path;
/** The main program; Holds all references and manages file access. */
public class MainServer {
private ServerSettings serverSettings;
private Reception reception;
private List butlers; // To avoid deadlocks, these resources have to
// be always locked from top to bottom
private HashMap accnameToButler;
private HashMap mapnameToMapman;
private HashMap activePlayernameToPlayer;
private HashMap loggedAccnameToAccount;
private GraphicsContentManager graphicsContentManager;
private HashSet allExistingPlayerNames;
public final static FileFilter directoryFilter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
};
public static void main(String[] args) {
MainServer server = new MainServer();
server.run();
}
public void run() {
System.out.println("Welcome to the Faunis server!");
System.out.println("Copyright 2012 Simon Ley alias \"skarute\"");
System.out.println("Licensed under GNU AGPL v3 or later");
serverSettings = new ServerSettings();
butlers = new ArrayList();
accnameToButler = new HashMap();
mapnameToMapman = new HashMap();
activePlayernameToPlayer = new HashMap();
loggedAccnameToAccount = new HashMap();
System.out.println("account storage in "+serverSettings.accountPath());
// Check if serverSettings paths exist:
serverSettings.checkPaths();
allExistingPlayerNames = loadAllExistingPlayerNames();
reception = new Reception(this, serverSettings.receptionPort());
System.out.println("Reception has been created.");
JFrame control = new JFrame("MainServer");
control.setSize(200, 200);
control.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
control.setVisible(true);
// load content:
graphicsContentManager = new GraphicsContentManager(
serverSettings.playerGraphicsPath(), serverSettings.imageFileEnding());
graphicsContentManager.loadResourcesForServer();
// TODO
// DEBUG - load testing content:
String starterRegion = serverSettings.starterRegion();
Map starterMap = new Map(starterRegion, 50, 50);
MapManager starterMapman = new MapManager(this, starterMap);
synchronized(mapnameToMapman) {
this.mapnameToMapman.put(starterRegion, starterMapman);
}
}
public ServerSettings getServerSettings() {
return serverSettings;
}
public GraphicsContentManager getGraphicsContentManager() {
return graphicsContentManager;
}
/** locks mapnameToMapman */
public MapManager getMapman(String mapName) {
synchronized(mapnameToMapman) {
return mapnameToMapman.get(mapName);
}
}
/** locks activePlayernameToPlayer
* returns the active Player instance with the given name */
public Player getActivePlayerByName(String playerName) {
synchronized(activePlayernameToPlayer) {
return activePlayernameToPlayer.get(playerName);
}
}
/** locks activePlayerNameToPlayer; accNameToButler */
public Result getButlerByPlayerName(String playerName) {
Player player = getActivePlayerByName(playerName);
if (player == null) {
String error = "getButlerByPlayerName(): Player with given name isn't active!";
return new Result(null, error);
}
String accountName = player.getAccountName();
synchronized(accnameToButler) {
return new Result(accnameToButler.get(accountName), null);
}
}
/** locks activePlayerNameToPlayer; mapnameToMapman */
public Result getMapmanByPlayerName(String playerName) {
Player player = getActivePlayerByName(playerName);
if (player == null) {
String error = "getMapmanByPlayerName(): Player with given name isn't active!";
return new Result(null, error);
}
String mapName = player.getMapName();
synchronized(mapnameToMapman) {
return new Result(mapnameToMapman.get(mapName), null);
}
}
/** locks butlers
* Creates a butler who looks after the querying client at given socket.
* That doesn't mean that the client is logged in already! */
public Butler createButler(Socket c){
Butler b = new Butler(this, c);
synchronized(butlers) {
butlers.add(b);
}
System.out.println("new butler for client at port "+c.getPort());
return b;
}
/** locks butlers */
public void deleteButler(Butler b){
synchronized(butlers) {
assert(butlers.contains(b));
butlers.remove(b);
}
System.out.println("Butler at port " + b.getClientSocket().getPort()+" destroyed!");
}
/** Creates a new account, but doesn't load it. */
public Result createAccount(String name, String password){
// TODO: Synchronise on file system? Or else two accounts could be
// created under the same name, for example!
// Check if an account already exists under given name:
if (!existAccountSubdir(name)){
// account doesn't yet exist, create it (without players)
Account acc = new Account(name, password, new ArrayList());
boolean createAccSuccess=createAccountSubdir(acc);
if (createAccSuccess){
System.out.println("Account "+name+" successfully created.");
return new Result(true, null);
}
else{
String error = "Account couldn't be created!?";
return new Result(null, error);
}
} else {
String error = "Account name already exists!";
return new Result(null, error);
}
}
/** locks accnameToButler, loggedAccnameToAccount
* logs out the account with the given name
* Requirement: No more active players */
public Result logoutAccount(String accountName){
if (!loggedIn(accountName)){
String error = "This account isn't even logged in!";
return new Result(null, error);
}
// Check if there's still an active player:
Account account;
synchronized (loggedAccnameToAccount) {
account = this.loggedAccnameToAccount.get(accountName);
}
Player activePlayer = account.getActivePlayer();
if (activePlayer != null) {
String error = "Can't log out since there's still an active player!";
return new Result(null, error);
}
// log out:
//saveAccount(acc); TODO
synchronized(accnameToButler) {
synchronized (loggedAccnameToAccount) {
assert(loggedAccnameToAccount.containsKey(accountName));
assert(accnameToButler.containsKey(accountName));
loggedAccnameToAccount.remove(accountName);
accnameToButler.remove(accountName);
}
}
System.out.println("Account "+accountName+" successfully logged out.");
return new Result(true, null);
}
/** locks loggedAccounts, accnameToButler */
public Result loginAccount(Butler butler, String name, String password){
// logs in the account with the given data
if (!existAccountSubdir(name)){
String error = "Login to non-existent account failed!";
return new Result(null, error);
} else if (loggedIn(name)){
String error = "This account is already logged in!";// TODO: Privacy?!
return new Result(null, error);
} else {
// log in:
Result result = loadAccount(name, password);
if (result.successful()) {
Account account = result.getResult();
synchronized(accnameToButler) {
synchronized(loggedAccnameToAccount) {
assert(!loggedAccnameToAccount.containsKey(name));
assert(!accnameToButler.containsKey(name));
loggedAccnameToAccount.put(name, account);
accnameToButler.put(name, butler);
}
}
System.out.println("Account "+name+" successfully logged in.");
return new Result(account, null);
} else {
System.out.println("There were errors while logging in "+name+".");
return new Result(null, result.getErrorMessage());
}
}
}
/** locks loggedAccounts
* Checks if an account of given name is already logged in. */
private boolean loggedIn(String name){
synchronized(loggedAccnameToAccount) {
if (loggedAccnameToAccount.containsKey(name))
return true;
return false;
}
}
/** checks if a directory for given account name exists */
private boolean existAccountSubdir(String accountName){
File accountSubdir = new File(serverSettings.accountPath()+accountName+"/");
System.out.println("Check if "+accountSubdir.getPath()+" exists...");
return (accountSubdir.exists() && accountSubdir.isDirectory());
}
/** Pure function.
* Loads an account from hard disk if exists and given login data is valid.
* Additionally returns the loaded account if successful. */
private Result loadAccount(String name, String password){
if (!existAccountSubdir(name)){
String error = "Account "+name+" couldn't be loaded since it does not exist!";
return new Result(null, error);
}
String accountPath = serverSettings.accountPath();
// read account.txt:
File accFile = new File(accountPath+name+"/account.txt");
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(accFile)));
} catch (FileNotFoundException e) {
System.out.println("Couldn't find account.txt for "+name);
e.printStackTrace();
return new Result(null, "Error while reading account data!");
}
String readPassword;
try {
readPassword = reader.readLine();
reader.close();
} catch (IOException e) {
System.out.println("Error while reading account data!");
e.printStackTrace();
return new Result(null, "Error while reading account data!");
}
// compare passwords
if (!password.equals(readPassword)){
System.out.println("Password invalid, since "+password+" != "+readPassword);
return new Result(null, "Invalid login data!");
}
/*
Login data valid, read further data:
Read player names
NOTE: The serialised player objects of the files in the directory
"players" will only be loaded when a player is activated;
Here we will just collect the player names.
*/
File accPlayerDir = new File(accountPath+name+"/players/");
File[] accPlayers = accPlayerDir.listFiles();
ArrayList playerNames = new ArrayList();
for (File p : accPlayers){
playerNames.add(p.getName());
}
// Finally create the account object:
Account acc = new Account(name, password, playerNames);
System.out.println("Account "+name+" successfully loaded.");
return new Result(acc, null);
}
/** locks allExistingPlayerNames, account, player
* Creates a new player on hard disk. You then still have to load it
* by calling loadAndActivatePlayer().*/
public Result createNewPlayer(Account account, String playerName, CharacterClass type) {
String starterRegion = serverSettings.starterRegion();
synchronized(allExistingPlayerNames) {
synchronized(account) {
Player player;
String accountName = account.getName();
int maxPlayers = serverSettings.maxPlayersPerAccount();
ArrayList playerNames = account.getPlayerNames();
if (playerNames.size() >= maxPlayers) {
String error = "You cannot exceed the limit of "+maxPlayers+" players per account!";
return new Result(null, error);
}
if (allExistingPlayerNames.contains(playerName)) {
String error = "A player with the name "+playerName+" already exists!";
return new Result(null, error);
}
player = new Player(playerName, type, starterRegion, accountName);
boolean success = savePlayer(player);
if (success) {
playerNames.add(playerName);
allExistingPlayerNames.add(playerName);
return new Result(true, null);
} else {
String error = "The player could not be created!";
return new Result(null, error);
}
}
}
}
/** locks player
* Saves the current state of given player. */
public boolean savePlayer(Player player) {
synchronized(player) {
Path oldPath = player.getPath();
if (oldPath != null) System.out.println("WARNING: MainServer.savePlayer(): Player was still moving!");
// It's bad if there's still a path referenced in the given player object:
// On no account may it be serialised with the player
player.resetPath();
player.resetEmote();
String accountName = player.getAccountName();
String playerName = player.getName();
assert(existAccountSubdir(accountName));
String playerDirString = playerDirString(accountName, playerName);
if (!existPlayerDir(accountName, playerName))
createPlayerDir(accountName, playerName);
File playerFile = new File(playerDirString+"/"+playerName);
FileOutputStream fos;
ObjectOutputStream oos;
try {
fos = new FileOutputStream(playerFile);
oos = new ObjectOutputStream(fos);
oos.writeObject(player);
oos.close();
} catch (IOException e) {
e.printStackTrace();
player.setPath(oldPath);
return false;
}
player.setPath(oldPath);
}
System.out.println("MainServer: Player successfully saved.");
return true;
}
/** locks account, activePlayernameToPlayer
* Loads a player object from disk and returns it, or returns null if it failed.
* => To be called by butlers!
* NOTE: The butler will yet have to assign the player to a mapman
*/
public Result loadAndActivatePlayer(Account account, String playerName) {
if (account.getActivePlayer() != null) {
String error = "Couldn't load "+playerName+" since there's still another player active!";
return new Result(null, error);
}
if (!account.getPlayerNames().contains(playerName)) {
String error = "Couldn't load "+playerName+" since a player of that name doesn't exist!";
return new Result(null, error);
}
assert(this.existPlayerDir(account.getName(), playerName));
FileInputStream fis;
ObjectInputStream ois;
Player player = null;
try {
fis = new FileInputStream(playerDirString(account.getName(), playerName)+"/"+playerName);
ois = new ObjectInputStream(fis);
player = (Player) ois.readObject();
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
assert(player != null);
synchronized(account) {
account.setActivePlayer(player);
}
synchronized(activePlayernameToPlayer) {
activePlayernameToPlayer.put(playerName, player);
}
return new Result(player, null);
}
/** locks activePlayernameToPlayer, player, loggedAccnameToAccount, account
* => to be called by butlers!
* Saves and unloads given player.
* NOTE: Requires that given player is not registered at any mapmans
* ( -> task of the butler to care about that)*/
public void unloadPlayer(Player player) {
String accountName;
synchronized(activePlayernameToPlayer) {
synchronized(player) {
this.savePlayer(player);
accountName = player.getAccountName();
activePlayernameToPlayer.remove(player.getName());
}
}
Account account;
synchronized(loggedAccnameToAccount) {
account = loggedAccnameToAccount.get(accountName);
}
synchronized(account) {
account.setActivePlayer(null);
}
}
/** Creates the directory structure in the account directory for given account.
* Returns true if successful, else false. */
private boolean createAccountSubdir(Account newAccount){
String accountPath = serverSettings.accountPath();
String newAccountName = newAccount.getName();
String newAccountPassword = newAccount.getPassword();
File newAccountDir = new File(accountPath+newAccountName+"/");
File newAccountPlayersDir = new File(accountPath+newAccountName+"/players");
File newAccountFile = new File(accountPath+newAccountName+"/account.txt");
boolean check = false;
check = newAccountDir.mkdir();
check = check && newAccountPlayersDir.mkdir();
if (!check) return false;
try {
PrintWriter pw = new PrintWriter(newAccountFile);
pw.println(newAccountPassword);
pw.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
private String playerDirString(String accountName, String playerName) {
return serverSettings.accountPath()+accountName+"/players/"+playerName;
}
private boolean existPlayerDir(String accountName, String playerName){
File playerDir = new File(playerDirString(accountName, playerName));
return (playerDir.exists() && playerDir.isDirectory());
}
private void createPlayerDir(String accountName, String playerName) {
assert(!existPlayerDir(accountName, playerName));
File playerDir = new File(playerDirString(accountName, playerName));
playerDir.mkdir();
}
/** NOTE: Doesn't wait until all threads have terminated. */
public void shutdownAll() {
reception.shutdown();
synchronized(butlers) {
for (Butler butler : butlers) {
butler.put(new MBStopThreadOrder(null));
}
}
}
private HashSet loadAllExistingPlayerNames() {
HashSet result = new HashSet();
File accountDir = new File(serverSettings.accountPath());
File[] accounts = accountDir.listFiles(directoryFilter);
for (File account : accounts) {
File playersDir = new File(account.getPath()+"/players");
File[] players = playersDir.listFiles(directoryFilter);
for (File player : players) {
boolean added = result.add(player.getName());
if (!added)
System.out.println("ERROR: Couldn't add "+player.getName());
}
}
return result;
}
}
Faunis/src/server/Reception.java 0000644 0001750 0001750 00000005342 12060472556 015724 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/** Exactly one instance is created.
* Receives clients that want to connect to the game,
* Creates butlers for them and redirects them to a new port.
*/
public class Reception{
protected boolean stopRunning;
protected MainServer parent;
private int port; // reception port where clients come to
private Thread recThread; // runs in the background and listens at the port
private Runnable recRunnable; // the job of recThread
protected ServerSocket recSocket; //
public Reception(MainServer parent, int port){
this.stopRunning = false;
this.parent = parent;
this.port = port;
try {
this.recSocket = new ServerSocket(this.port);
} catch(Exception e) {
e.printStackTrace();
System.out.println("Couldn't create reception socket!");
}
this.recRunnable = new RecRunnable();
this.recThread = new Thread(this.recRunnable);
this.recThread.start();
}
class RecRunnable implements Runnable {
public void run() {
System.out.println("Reception runnable runs");
while(!stopRunning){
// Steadily listens at recSocket:
// accept() blocks the execution until there's
// an incoming client.
// Negotiates a connection (clientSocket) with new clients
// and creates a new butler for them.
Socket clientSocket;
try {
clientSocket = recSocket.accept();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Error while listening to the reception socket!");
return;
}
System.out.println("Client heard, create butler ...");
parent.createButler(clientSocket);
}
}
}
/** NOTE: Execution doesn't wait for reception to terminate. */
public void shutdown() {
stopRunning = true;
try {
recSocket.close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't choke reception thread!");
}
}
}
Faunis/src/server/Account.java 0000644 0001750 0001750 00000003174 12060472556 015371 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.util.ArrayList;
public class Account {
private String name;
private String password;
private ArrayList playerNames; // Names of the players as they exist in
// the directory "players"
private Player activePlayer; // player which is currently loaded and controlled
public Account(String name, String password, ArrayList playerNames){
this.name = name;
this.password = password;
this.playerNames = playerNames;
this.activePlayer = null;
}
public String getName(){
return this.name;
}
public String getPassword(){
return this.password;
}
public ArrayList getPlayerNames() {
return this.playerNames;
}
public Player getActivePlayer() {
return this.activePlayer;
}
public void setActivePlayer(Player player) {
this.activePlayer = player;
}
}
Faunis/src/server/mapmanToButlerOrders/ 0000775 0001750 0001750 00000000000 12062730610 017226 5 ustar user user Faunis/src/server/mapmanToButlerOrders/MBMapInfoOrder.java 0000644 0001750 0001750 00000002123 12061574442 022641 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
import communication.MapInfo;
public class MBMapInfoOrder extends MBOrder {
private MapInfo mapInfo;
public MBMapInfoOrder(MapManager source, MapInfo mapInfo) {
super(source);
this.mapInfo = mapInfo;
}
public MapInfo getMapInfo() {
return mapInfo;
}
}
Faunis/src/server/mapmanToButlerOrders/MBOrder.java 0000644 0001750 0001750 00000001766 12061574242 021401 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
public abstract class MBOrder {
private MapManager source;
MBOrder(MapManager source) {
this.source = source;
}
public MapManager getSource() {
return source;
}
}
Faunis/src/server/mapmanToButlerOrders/MBChatMessageOrder.java 0000644 0001750 0001750 00000003147 12061574342 023502 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
import server.butlerToMapmanOrders.BMChatMessageOrder;
public class MBChatMessageOrder extends MBOrder {
private String fromPlayername;
private String toPlayername;
private String message;
public MBChatMessageOrder(MapManager source, String from, String to, String msg) {
super(source);
this.fromPlayername = from;
this.toPlayername = to;
this.message = msg;
}
public MBChatMessageOrder(MapManager source, BMChatMessageOrder order) {
super(source);
this.fromPlayername = order.getFromName();
this.toPlayername = order.getToName();
this.message = order.getMessage();
}
public String getFromPlayername() {
return fromPlayername;
}
public String getToPlayername() {
return toPlayername;
}
public String getMessage() {
return message;
}
}
Faunis/src/server/mapmanToButlerOrders/MBErrorMessageOrder.java 0000644 0001750 0001750 00000002131 12061574354 023707 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
public class MBErrorMessageOrder extends MBOrder {
private String errorMessage;
public MBErrorMessageOrder(MapManager source, String errorMessage) {
super(source);
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
}
Faunis/src/server/mapmanToButlerOrders/MBCharAtOtherMapmanOrder.java 0000644 0001750 0001750 00000002515 12061574500 024606 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
/** When the player steps on a link on the map, the MapManager tells the Butler
* with this order that he should assign the player to a new MapManager. The
* Butler has to do that on his own by calling unregister() and register().
*/
public class MBCharAtOtherMapmanOrder extends MBOrder {
private MapManager newMapman;
public MBCharAtOtherMapmanOrder(MapManager source, MapManager newMapman) {
super(source);
this.newMapman = newMapman;
}
public MapManager getNewMapman() {
return newMapman;
}
}
Faunis/src/server/mapmanToButlerOrders/MBAddCharOrder.java 0000644 0001750 0001750 00000003012 12061574274 022577 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import communication.GraphicalPlayerStatus;
import server.MapManager;
/** NOTE: The butler has to check if the source matches his registered
* active map manager, or else it may be that a former MapManager still sends
* events which don't belong to the new map anymore! */
public class MBAddCharOrder extends MBOrder {
private String playerName;
private GraphicalPlayerStatus graphStatus;
public MBAddCharOrder(MapManager source, String playerName, GraphicalPlayerStatus graphStatus) {
super(source);
this.playerName = playerName;
this.graphStatus = graphStatus;
}
public String getPlayerName() {
return playerName;
}
public GraphicalPlayerStatus getGraphStatus() {
return graphStatus;
}
}
Faunis/src/server/mapmanToButlerOrders/MBChangeCharOrder.java 0000644 0001750 0001750 00000002561 12061574306 023300 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
import communication.GraphicalPlayerStatus;
/** A character has changed its graphical status */
public class MBChangeCharOrder extends MBOrder {
private String playerName;
private GraphicalPlayerStatus graphStatus;
public MBChangeCharOrder(MapManager source, String playerName, GraphicalPlayerStatus graphStatus) {
super(source);
this.playerName = playerName;
this.graphStatus = graphStatus;
}
public String getPlayerName() {
return playerName;
}
public GraphicalPlayerStatus getGraphStatus() {
return graphStatus;
}
}
Faunis/src/server/mapmanToButlerOrders/MBRemoveCharOrder.java 0000644 0001750 0001750 00000002111 12061574324 023337 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
public class MBRemoveCharOrder extends MBOrder {
private String playerName;
public MBRemoveCharOrder(MapManager source, String playerName) {
super(source);
this.playerName = playerName;
}
public String getPlayerName() {
return playerName;
}
}
Faunis/src/server/mapmanToButlerOrders/MBStopThreadOrder.java 0000644 0001750 0001750 00000002050 12061574410 023357 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.mapmanToButlerOrders;
import server.MapManager;
/** Used to stop the serverThread of a butler who is waiting to take orders
* out of his BlockingQueue. */
public class MBStopThreadOrder extends MBOrder {
public MBStopThreadOrder(MapManager source) {
super(source);
}
}
Faunis/src/server/Butler.java 0000644 0001750 0001750 00000040450 12061600440 015212 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import server.butlerToMapmanOrders.*;
import server.mapmanToButlerOrders.*;
import communication.butlerToClientOrders.*;
import communication.clientToButlerOrders.*;
import communication.enums.CharacterClass;
import communication.enums.ClientStatus;
/** The butler looks after the needs of a client.
* He receives the socket that the reception has already created.
* He represents his client on the server side. */
public class Butler {
private Object shutdownMutexKey;
private boolean shutdownOccupied;
protected MainServer parent;
private Socket clientSocket; // the socket through which to communicate with the client
protected ObjectInputStream clientInput; // to read from the socket
protected ObjectOutputStream clientOutput; // to write to the socket; synchronised access only
private Thread clientThread; // steadily observes and handles the requests from the client
private ClientRunnable clientRunnable; // the job of clientThread
protected boolean stopRunning = false; // indicates termination to clientThread and serverThread
protected Account loggedAccount; // Account, unter dem der Client gerade eingeloggt ist (kann auch null sein)
protected Player activePlayer;
protected MapManager activeMapman;
protected BlockingQueue serverOrders;
private Thread serverThread; // steadily observes and handles the requests from server side
private ServerRunnable serverRunnable; // the job of serverThread
public Butler(MainServer parent, Socket clientSocket){
this.shutdownMutexKey = new Object();
this.shutdownOccupied = false;
this.parent = parent;
this.clientSocket = clientSocket;
this.serverOrders = new ArrayBlockingQueue(50);
// create input and output streams from clientSocket
System.out.println("Butler: Try to create input / output streams...");
try {
this.clientOutput = new ObjectOutputStream(this.clientSocket.getOutputStream());
this.clientInput = new ObjectInputStream(this.clientSocket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
System.out.println("Couldn't create input / output stream!");
}
System.out.println("Input and output streams have been created.");
// create and start clientThread
this.clientRunnable = new ClientRunnable(this);
this.clientThread = new Thread(this.clientRunnable);
this.clientThread.start();
// create and start serverThread
this.serverRunnable = new ServerRunnable(this);
this.serverThread = new Thread(this.serverRunnable);
this.serverThread.start();
}
public boolean assertActivePlayer() {
if (activePlayer == null) {
sendErrorMessage("Command requires loaded player!");
return false;
}
return true;
}
public boolean assertLoggedAccount() {
if (loggedAccount == null) {
sendErrorMessage("Command requires logged account!");
return false;
}
return true;
}
/** Shuts this butler down. Only one thread is allowed
* to call this simultaneously. All others are rejected (returns false),
* thus not blocked by calling this.
* This concurrency problem is solved by shutdownMutexKey
* (the lock to synchronise on before shutdownOccupied is read),
* and shutdownOccupied which indicates if the method is already
* being called.
* Asserts that clientThread and serverThread terminate,
* unless it isn't one of them that calls this method, in
* which case only the termination of the other is asserted
* and it is the responsibility of the caller to terminate afterwards
* immediately. Thus, wherever clientThread / serverThread calls shutdown(),
* it must terminate thereafter. */
public boolean shutdown(){
synchronized(shutdownMutexKey) {
if (shutdownOccupied == true)
return false;
else
shutdownOccupied = true;
}
System.out.println("Butler: Initiate shutdown...");
// Save data, log out, unregister butler etc.
if (loggedAccount != null)
logoutAccount();
parent.deleteButler(this);
// Ask both threads of this butler to terminate:
stopRunning = true;
// Since both threads may be stuck waiting / listening,
// we have to enforce termination! (:<
// Enforce termination of serverThread by a special order:
serverOrders.clear();
serverOrders.add(new MBStopThreadOrder(null));
// Enforce termination of clientThread by closing clientInput:
try {
clientInput.close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't choke clientThread!");
}
if (Thread.currentThread() == this.clientThread) {
// Only wait for serverThread to terminate:
while (serverThread.isAlive()) {}
} else if (Thread.currentThread() == this.serverThread) {
// Only wait for clientThread to terminate:
while (clientThread.isAlive()) {}
} else {
// wait for both threads to terminate:
while (serverThread.isAlive() || clientThread.isAlive()) {}
}
shutdownOccupied = false;
return true;
}
// #################################################################################
// Client -> Butler -> MapMan, Server side:
// #################################################################################
public void loadActivePlayer(CBLoadPlayerOrder order) {
if (!assertLoggedAccount())
return;
else if (activePlayer != null) {
sendErrorMessage("Butler: Couldn't load player: There's already one loaded.");
return;
}
String playerName = order.getPlayerName();
Result query = parent.loadAndActivatePlayer(loggedAccount, playerName);
if (!query.successful()) {
sendErrorMessage(query.getErrorMessage());
return;
}
activePlayer = query.getResult();
// Assign player to mapman:
String mapname = activePlayer.getMapName();
assert(mapname != null);
assert(activeMapman == null);
activeMapman = parent.getMapman(mapname);
addPlayerToMapman(activeMapman, false);
// => the mapman will send a MBMapInfoOrder
// to the butler who passes it on to the client
sendOrderToClient(new BCSetClientStatusOrder(ClientStatus.exploring));
System.out.println("Butler: Player "+playerName+" successfully loaded.");
}
/** Registers the active player at given mapman.
* By doing so, the mapman will consequently send a MBMapInfoOrder
* to the butler, who passes it on to the client.*/
private void addPlayerToMapman(MapManager mapman, boolean addPlayerMapEntry) {
assert(activePlayer != null);
assert(mapman != null);
mapman.put(new BMRegisterOrder(this, activePlayer, addPlayerMapEntry));
}
private void removePlayerFromMapman(MapManager mapman, boolean removePlayerMapEntry) {
assert(activePlayer != null);
assert(mapman != null);
mapman.put(new BMUnregisterOrder(this, activePlayer, removePlayerMapEntry));
}
public void unloadActivePlayer() {
if (!assertLoggedAccount()) return;
if (!assertActivePlayer()) return;
assert(activeMapman != null);
removePlayerFromMapman(activeMapman, false);
parent.unloadPlayer(activePlayer);
activePlayer = null;
activeMapman = null;
sendOrderToClient(new BCSetClientStatusOrder(ClientStatus.noCharLoaded));
System.out.println("Butler: Unloaded player.");
}
public void loginAccount(CBLoginOrder order) {
if (loggedAccount == null) {
Result query = parent.loginAccount(this, order.getName(),
order.getPassword());
if (query.successful()) {
loggedAccount = query.getResult();
sendOrderToClient(new BCSetClientStatusOrder(ClientStatus.noCharLoaded));
} else {
sendErrorMessage(query.getErrorMessage());
}
} else {
sendErrorMessage("Butler: Couldn't log in since account seems to be already logged in.");
}
}
public void logoutAccount() {
if (loggedAccount != null) {
if (activePlayer != null) {
unloadActivePlayer();
}
Result result = parent.logoutAccount(loggedAccount.getName());
if (!result.successful()) {
sendErrorMessage(result.getErrorMessage());
return;
}
loggedAccount = null;
sendOrderToClient(new BCSetClientStatusOrder(ClientStatus.loggedOut));
} else {
sendErrorMessage("Butler: Couldn't log out since there's no account logged in!");
}
}
public void createNewPlayer(CBCreatePlayerOrder order) {
if (!assertLoggedAccount()) return;
Result result = parent.createNewPlayer(loggedAccount,
order.getPlayerName(), CharacterClass.arctos);
if (!result.successful())
sendErrorMessage(result.getErrorMessage());
}
public void moveChar(CBMoveOrder order) {
if (!assertLoggedAccount()) return;
if (!assertActivePlayer()) return;
activeMapman.put(new BMMoveOrder(activePlayer, this, order));
}
/** friendly shutdown: Tells client that he's now disconnected */
public void disconnect() {
sendOrderToClient(new BCSetClientStatusOrder(ClientStatus.disconnected));
if (!stopRunning) shutdown();
}
public void forwardChatOrder(CBChatOrder order) {
if (!assertActivePlayer()) return;
activeMapman.put(new BMChatMessageOrder(this, order, activePlayer.getName()));
}
public void forwardEmoteOrder(CBTriggerEmoteOrder order) {
if (!assertActivePlayer()) return;
activeMapman.put(new BMTriggerEmoteOrder(this, activePlayer,
order.getEmote()));
}
public Socket getClientSocket(){
return this.clientSocket;
}
// job of clientThread
private class ClientRunnable implements Runnable {
private Butler myButler;
public ClientRunnable(Butler parent){
this.myButler = parent;
}
@Override
public void run() {
// Interpret client's requests:
while(!stopRunning){
Object read = null;
try {
read = clientInput.readObject();
} catch(IOException e) {
// Connection broken -> delete butler
System.out.println("connection reset!!");
if (!stopRunning) //nobody ordered a shutdown,
shutdown(); //so we will cause that on ourselves
return;
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
if (read != null && read instanceof CBOrder){
// a clientOrder has been read:
myButler.handleClientOrder((CBOrder) read);
}
}
}
}
protected void handleClientOrder(CBOrder read) {
if (read instanceof CBDisconnectOrder){
System.out.println("CBDisconnectOrder");
disconnect();
} else if (read instanceof CBCreatePlayerOrder) {
System.out.println("CBCreatePlayerOrder");
createNewPlayer((CBCreatePlayerOrder) read);
} else if (read instanceof CBChatOrder){
System.out.println("CBChatOrder");
forwardChatOrder((CBChatOrder) read);
} else if (read instanceof CBLoginOrder){
System.out.println("CBLoginOrder");
loginAccount((CBLoginOrder) read);
} else if (read instanceof CBLogoutOrder){
System.out.println("CBLogoutOrder");
logoutAccount();
} else if (read instanceof CBLoadPlayerOrder) {
System.out.println("CBLoadPlayerOrder");
loadActivePlayer((CBLoadPlayerOrder) read);
} else if (read instanceof CBUnloadPlayerOrder) {
System.out.println("CBUnloadPlayerOrder");
unloadActivePlayer();
} else if (read instanceof CBMoveOrder) {
System.out.println("CBMoveOrder");
moveChar((CBMoveOrder) read);
} else if (read instanceof CBTriggerEmoteOrder) {
System.out.println("CBTriggerEmoteOrder");
forwardEmoteOrder((CBTriggerEmoteOrder) read);
} else if (read instanceof CBServerSourceOrder) {
System.out.println("CBServerSourceOrder");
sendOrderToClient(new BCSystemMessageOrder(
"Server source code at "+parent.getServerSettings().serverSourceAt()));
}
}
// #################################################################################
// Mapman -> Butler -> Client side:
// #################################################################################
public void put(MBOrder order) {
try {
serverOrders.put(order);
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't pass server order to the butler!");
}
}
// job of serverThread
private class ServerRunnable implements Runnable {
private Butler myButler;
public ServerRunnable(Butler parent) {
this.myButler = parent;
}
@Override
public void run() {
// Handle queries lying in serverOrders
MBOrder order;
while (!stopRunning) {
order = null;
try {
order = myButler.serverOrders.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (order != null)
myButler.handleServerOrder(order);
}
}
}
protected void handleServerOrder(MBOrder order) {
// Assert that given order comes from our active mapman!
// the only exception is if it's set to null (needed for chat orders, f.ex.)
if (order.getSource() != null && order.getSource() != this.activeMapman) {
System.out.println("Butler: Received order from foreign mapman!");
return;
}
if (order instanceof MBAddCharOrder) {
clientAddChar((MBAddCharOrder) order);
} else if (order instanceof MBChangeCharOrder) {
clientChangeChar((MBChangeCharOrder) order);
} else if (order instanceof MBRemoveCharOrder) {
clientRemoveChar((MBRemoveCharOrder) order);
} else if (order instanceof MBChatMessageOrder) {
clientChatMessage((MBChatMessageOrder) order);
} else if (order instanceof MBMapInfoOrder) {
clientMapInfo((MBMapInfoOrder) order);
} else if (order instanceof MBCharAtOtherMapmanOrder) {
changeMapman((MBCharAtOtherMapmanOrder) order);
} else if (order instanceof MBStopThreadOrder) {
// If nobody else caused a shutdown, we will do
if (!stopRunning) shutdown();
} else if (order instanceof MBErrorMessageOrder) {
sendErrorMessage(((MBErrorMessageOrder) order).getErrorMessage());
}
// TODO: Handle further serverside orders
}
/** locks clientOutput */
private void sendOrderToClient(BCOrder order) {
assert(clientOutput != null);
synchronized(clientOutput) {
if (order == null) {
System.out.println("Order is null!");
}
try {
clientOutput.writeObject(order);
} catch (IOException e) {
System.out.println("Butler: Couldn't pass order to client!");
}
}
}
private void clientChatMessage(MBChatMessageOrder order) {
String toName = order.getToPlayername();
boolean isBroadcast = (toName == null || toName.equals(""));
sendOrderToClient(new BCChatMessageOrder(order.getMessage(),
order.getFromPlayername(),
isBroadcast));
}
private void clientAddChar(MBAddCharOrder order) {
sendOrderToClient(new BCAddCharOrder(order));
}
private void clientChangeChar(MBChangeCharOrder order) {
sendOrderToClient(new BCChangeCharOrder(order));
}
private void clientRemoveChar(MBRemoveCharOrder order) {
sendOrderToClient(new BCRemoveCharOrder(order));
}
private void clientMapInfo(MBMapInfoOrder order) {
sendOrderToClient(new BCSetMapOrder(order, activePlayer.getName()));
}
private void changeMapman(MBCharAtOtherMapmanOrder order) {
MapManager oldMapman = order.getSource();
assert(this.activeMapman == oldMapman);
MapManager newMapman = order.getNewMapman();
this.activeMapman = null;
removePlayerFromMapman(oldMapman, true);
addPlayerToMapman(newMapman, true);
this.activeMapman = newMapman;
}
private void sendErrorMessage(String errorMessage) {
sendOrderToClient(new BCErrorMessageOrder(errorMessage));
}
}
Faunis/src/server/butlerToMapmanOrders/ 0000775 0001750 0001750 00000000000 12062730610 017226 5 ustar user user Faunis/src/server/butlerToMapmanOrders/BMUnregisterOrder.java 0000644 0001750 0001750 00000002417 12061574172 023445 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import server.Butler;
import server.Player;
public class BMUnregisterOrder extends BMOrder {
private Player player;
private boolean removePlayerMapEntry;
public BMUnregisterOrder(Butler source, Player player, boolean removePlayerMapEntry) {
super(source);
this.player = player;
this.removePlayerMapEntry = removePlayerMapEntry;
}
public Player getPlayer() {
return player;
}
public boolean getRemovePlayerMapEntry() {
return removePlayerMapEntry;
}
}
Faunis/src/server/butlerToMapmanOrders/BMOrder.java 0000644 0001750 0001750 00000001746 12061574046 021401 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import server.Butler;
public abstract class BMOrder {
private Butler source;
BMOrder(Butler source) {
this.source = source;
}
public Butler getSource() {
return source;
}
}
Faunis/src/server/butlerToMapmanOrders/BMTriggerEmoteOrder.java 0000644 0001750 0001750 00000002213 12061574120 023676 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import server.Butler;
import server.Player;
public class BMTriggerEmoteOrder extends BMOrder {
private Player player;
private String emote;
public BMTriggerEmoteOrder(Butler source, Player player, String emote) {
super(source);
this.player = player;
this.emote = emote;
}
public Player getPlayer() {
return player;
}
public String getEmote() {
return emote;
}
}
Faunis/src/server/butlerToMapmanOrders/BMMoveOrder.java 0000644 0001750 0001750 00000003036 12061574146 022223 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import communication.clientToButlerOrders.CBMoveOrder;
import server.Butler;
import server.Player;
public class BMMoveOrder extends BMOrder {
private Player player;
private int xTarget;
private int yTarget;
public BMMoveOrder(Player player, Butler source, CBMoveOrder order) {
super(source);
this.player = player;
this.xTarget = order.getXTarget();
this.yTarget = order.getYTarget();
}
public BMMoveOrder(Player player, Butler source, int xTarget, int yTarget) {
super(source);
this.player = player;
this.xTarget = xTarget;
this.yTarget = yTarget;
}
public int getXTarget() {
return xTarget;
}
public int getYTarget() {
return yTarget;
}
public Player getPlayer() {
return player;
}
}
Faunis/src/server/butlerToMapmanOrders/BMRegisterOrder.java 0000644 0001750 0001750 00000002371 12061574162 023100 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import server.Butler;
import server.Player;
public class BMRegisterOrder extends BMOrder {
private Player player;
private boolean addPlayerMapEntry;
public BMRegisterOrder(Butler source, Player player, boolean addPlayerMapEntry) {
super(source);
this.player = player;
this.addPlayerMapEntry = addPlayerMapEntry;
}
public Player getPlayer() {
return player;
}
public boolean getAddPlayerMapEntry() {
return addPlayerMapEntry;
}
}
Faunis/src/server/butlerToMapmanOrders/BMChatMessageOrder.java 0000644 0001750 0001750 00000003303 12061574130 023467 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import communication.clientToButlerOrders.CBChatOrder;
import server.Butler;
/** Represents a chat order which is forwarded from Butler to Mapman or
* between Mapmans. Note that the corresponding Butler for toName is not yet
* identified, therefore the forwarding. */
public class BMChatMessageOrder extends BMOrder {
private String message;
private String toName;
private String fromName;
public BMChatMessageOrder(Butler source, String message, String toName, String fromName) {
super(source);
this.message = message;
this.toName = toName;
this.fromName = fromName;
}
public BMChatMessageOrder(Butler source, CBChatOrder order, String fromName) {
super(source);
this.message = order.getMessage();
this.toName = order.getToName();
this.fromName = fromName;
}
public String getMessage() {
return message;
}
public String getToName() {
return toName;
}
public String getFromName() {
return fromName;
}
}
Faunis/src/server/butlerToMapmanOrders/BMMapInfoOrder.java 0000644 0001750 0001750 00000001652 12061574456 022654 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server.butlerToMapmanOrders;
import server.Butler;
public class BMMapInfoOrder extends BMOrder {
public BMMapInfoOrder(Butler source) {
super(source);
}
}
Faunis/src/server/MapManager.java 0000644 0001750 0001750 00000030055 12061600440 015765 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import client.AnimationData;
import communication.GraphicalPlayerStatus;
import communication.GraphicsContentManager;
import communication.Map;
import communication.MapInfo;
import communication.enums.AniEndType;
import communication.enums.CharacterClass;
import communication.movement.Moveable;
import communication.movement.Mover;
import communication.movement.MoverManager;
import communication.movement.MovingTask;
import communication.movement.Path;
import communication.movement.PathFactory;
import communication.movement.RoughMovingTask;
import server.butlerToMapmanOrders.*;
import server.mapmanToButlerOrders.*;
/** The map manager manages everything that happens on a map. There is exactly
* one map manager for each map in the game. All players / butlers on a map have
* to be registered at the map manager of that map. */
public class MapManager implements MoverManager {
private MainServer parent;
private Map map;
private Thread thread;
private Runnable runnable;
protected BlockingQueue orders;
private HashMap registeredPlayers;
private HashMap playerNameToPlayer;
private HashMap movingPlayers;
public MapManager(MainServer parent, Map map) {
this.parent = parent;
this.map = map;
this.registeredPlayers = new HashMap();
this.playerNameToPlayer = new HashMap();
this.movingPlayers = new HashMap();
orders = new ArrayBlockingQueue(50);
runnable = new MapManRunnable(this);
thread = new Thread(runnable);
thread.start();
}
private void tryStopMovement(Player player) {
synchronized(movingPlayers) {
synchronized(player) {
Mover mover = movingPlayers.get(player);
if (mover != null) {
mover.stop();
unregisterMover(player);
// TODO: I think we can sum the above to mover.stopAndUnregister();
}
}
}
}
public Object[] getSynchroStuffForMoverStop() {
return new Object[] {movingPlayers};
}
@Override
public void unregisterMover(Moveable moveable) {
assert(moveable instanceof Player);
Player movingPlayer = (Player) moveable;
synchronized(movingPlayers) {
movingPlayers.remove(movingPlayer);
}
// TODO: Check if the player has landed on a link to another map
}
public void put(BMOrder order) {
try {
orders.put(order);
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't pass the order to the mapman!");
}
}
public Map getMap() {
return map;
}
public String getMapName() {
return map.getName();
}
protected void handleButlerOrder(BMOrder order) {
assert(order != null);
if (order instanceof BMMapInfoOrder) {
MapInfo mapInfo = getMapInfo();
order.getSource().put(new MBMapInfoOrder(this, mapInfo));
}
else if (order instanceof BMRegisterOrder)
registerPlayer((BMRegisterOrder) order);
else if (order instanceof BMUnregisterOrder)
unregisterPlayer((BMUnregisterOrder) order);
else if (order instanceof BMMoveOrder)
movePlayer((BMMoveOrder) order);
else if (order instanceof BMChatMessageOrder)
bMChatMessageOrder((BMChatMessageOrder) order);
else if (order instanceof BMTriggerEmoteOrder)
bMTriggerEmoteOrder((BMTriggerEmoteOrder) order);
// TODO Handle further orders
}
private void bMTriggerEmoteOrder(BMTriggerEmoteOrder order) {
// TODO
// determine if emote is valid for given player type:
Player player = order.getPlayer();
CharacterClass type = player.getType();
String emote = order.getEmote();
if (emote == null || emote.equals("")) {
// if an emote had been stored, delete it and notify all butlers
deleteEmote(player);
return;
}
GraphicsContentManager contentManager = parent.getGraphicsContentManager();
Set emotes = contentManager.getAvailableAnimations(type);
if (!emotes.contains(emote)) {
order.getSource().put(new MBErrorMessageOrder(this, "Emote is invalid!"));
return;
}
// determine emote's AniEndType:
AnimationData animationData = contentManager.getAnimationData(type, emote);
assert(animationData != null);
AniEndType endType = animationData.endType;
if (endType != AniEndType.revert) {
// store emote in player and notify all
storeEmote(player, emote);
} else {
// fire and forget
fireEmote(player, emote);
}
}
/** locks registeredPlayers, player */
private void fireEmote(Player player, String emote) {
synchronized(registeredPlayers) {
synchronized(player) {
GraphicalPlayerStatus status = player.getGraphicalPlayerStatus();
status.currentEmote = emote;
notifyAllCharChanged(player.getName(), status);
}
}
}
/** locks registeredPlayers, player */
private void deleteEmote(Player player) {
synchronized(registeredPlayers) {
synchronized(player) {
if (player.getCurrentEmote() != null) {
player.setCurrentEmote(null);
notifyAllCharChanged(player.getName(), player.getGraphicalPlayerStatus());
}
}
}
}
/** locks registeredPlayers, player */
private void storeEmote(Player player, String emote) {
synchronized(registeredPlayers) {
synchronized(player) {
player.setCurrentEmote(emote);
notifyAllCharChanged(player.getName(), player.getGraphicalPlayerStatus());
}
}
}
/** locks registeredPlayers */
private void notifyAllCharChanged(String playerName, GraphicalPlayerStatus status) {
synchronized(registeredPlayers ) {
for (Butler butler : registeredPlayers.values()) {
butler.put(new MBChangeCharOrder(this, playerName,
status));
}
}
}
private void bMChatMessageOrder(BMChatMessageOrder order) {
System.out.println("Mapman forwards chat message");
String playerName = order.getToName();
if (playerName == null || playerName.equals("")) {
// broadcast message to all players of this mapman
synchronized(registeredPlayers) {
for (Butler butler : registeredPlayers.values()) {
butler.put(new MBChatMessageOrder(this, order));
}
}
return;
}
// else find the butler that corresponds to playerName
Player player;
synchronized(playerNameToPlayer) {
player = playerNameToPlayer.get(playerName);
}
if (player != null) {
Butler butler;
synchronized(registeredPlayers ) {
butler = registeredPlayers.get(player);
}
butler.put(new MBChatMessageOrder(this, order));
} else {
Result butlerQuery = parent.getButlerByPlayerName(playerName);
if (!butlerQuery.successful()) {
String error = "Couldn't deliver message to given player.";
order.getSource().put(new MBErrorMessageOrder(this, error));
return;
}
Butler butler = butlerQuery.getResult();
// Since the butler will only listen to his mapman,
// we have to show him that it's okay by setting
// the source to null:
butler.put(new MBChatMessageOrder(null, order));
}
}
private void movePlayer(BMMoveOrder order) {
assert(order != null);
Player player = order.getPlayer();
synchronized (registeredPlayers) {
assert(registeredPlayers.containsKey(player));
}
// stop possible earlier movement:
tryStopMovement(player);
// stop possible earlier emote:
deleteEmote(player);
// if we are already at our target then return:
if (order.getXTarget() == player.getX()
&& order.getYTarget() == player.getY())
return;
String playerName;
GraphicalPlayerStatus status;
// build path and set its reference in the player object:
synchronized(player) {
Path path = PathFactory.createAirlinePath(player.getX(), player.getY(),
order.getXTarget(), order.getYTarget());
player.setPath(path);
playerName = player.getName();
status = player.getGraphicalPlayerStatus();
}
// create new mover for player and add it:
Mover mover;
synchronized(movingPlayers) {
mover = new Mover(this, player, 500); // TODO Zeiteinheit
MovingTask movingTask = new RoughMovingTask(mover, player);
mover.setMovingTask(movingTask);
movingPlayers.put(player, mover);
}
// inform every registered butler about the change:
synchronized(registeredPlayers) {
notifyAllCharChanged(playerName, status);
}
mover.start();
// TODO
}
private void registerPlayer(BMRegisterOrder order) {
assert(order != null);
synchronized(registeredPlayers) {
synchronized(playerNameToPlayer) {
Player player = order.getPlayer();
String playername = player.getName();
Butler butler = order.getSource();
synchronized(player) {
assert(!player.hasPath());
}
assert(!playerNameToPlayer.containsKey(playername));
playerNameToPlayer.put(playername, player);
assert(!registeredPlayers.containsKey(player));
registeredPlayers.put(player, butler);
if (order.getAddPlayerMapEntry()) {
synchronized(player) {
assert(player.getMapName() == null);
player.setMapName(this.map.getName());
}
}
// send map information to the new player's butler:
MapInfo mapInfo = getMapInfo();
butler.put(new MBMapInfoOrder(this, mapInfo));
// send information about the new player to all other butlers:
for (Butler butler2:registeredPlayers.values()) {
if (butler2 != butler)
butler2.put(new MBAddCharOrder(this, playername,
player.getGraphicalPlayerStatus()));
// TODO
}
}
}
}
private void unregisterPlayer(BMUnregisterOrder order) {
assert(order != null);
synchronized(registeredPlayers) {
synchronized(playerNameToPlayer) {
Player player = order.getPlayer();
String playername = player.getName();
assert(playerNameToPlayer.containsKey(playername));
// stop possible earlier movement:
tryStopMovement(player);
// stop possible earlier emote:
deleteEmote(player);
playerNameToPlayer.remove(playername);
assert(registeredPlayers.containsKey(player));
registeredPlayers.remove(player);
if (order.getRemovePlayerMapEntry()) {
synchronized(player) {
assert(player.getMapName() != null);
player.setMapName(null);
}
}
// inform every registered butler about the leave:
for (Butler butler2:registeredPlayers.values()) {
butler2.put(new MBRemoveCharOrder(this, playername));
}
}
}
}
private MapInfo getMapInfo() {
HashMap players = new HashMap();
synchronized(registeredPlayers) {
for (Player player:registeredPlayers.keySet()) {
GraphicalPlayerStatus graphStatus = player.getGraphicalPlayerStatus();
players.put(player.getName(), graphStatus);
}
}
return new MapInfo(map.getName(), players);
}
private class MapManRunnable implements Runnable {
private MapManager myMapman;
public MapManRunnable(MapManager parent) {
this.myMapman = parent;
}
@Override
public void run() {
BMOrder order;
while (true) {
order = null;
try {
order = myMapman.orders.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (order != null)
myMapman.handleButlerOrder(order);
}
}
}
}
Faunis/src/server/ServerSettings.java 0000644 0001750 0001750 00000005267 12061600730 016755 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
import java.io.File;
public class ServerSettings {
private String classPath; // paths are declared in constructor below;
private String serverDataPath; // NOTE: All paths must end in "/"
private String clientDataPath;
private String accountPath;
private String playerGraphicsPath;
private String imageFileEnding = ".png";
private String starterRegion = "greenFields";
private int maxPlayersPerAccount = 3;
private int receptionPort = 1024;
private String serverSourceAt = "http://savannah.nongnu.org";
public ServerSettings() {
try {
classPath = getClass().getProtectionDomain().
getCodeSource().getLocation().toURI().getPath();
} catch (Exception e) {
e.printStackTrace();
return;
}
//classPath = classPath.substring(1); // Windows error only?
File classPathFile = new File(classPath);
String parentPath = classPathFile.getParent()+"/";
serverDataPath = parentPath+"serverData/";
clientDataPath = parentPath+"clientData/";
accountPath = serverDataPath+"accounts/";
playerGraphicsPath = clientDataPath+"playerGraphics/";
}
public String accountPath() {
return accountPath;
}
public String starterRegion() {
return starterRegion;
}
public String imageFileEnding() {
return imageFileEnding;
}
public String playerGraphicsPath() {
return playerGraphicsPath;
}
public int maxPlayersPerAccount() {
return maxPlayersPerAccount;
}
public String serverSourceAt() {
return serverSourceAt;
}
public int receptionPort() {
return receptionPort;
}
public void checkPaths() {
File accountDir = new File(accountPath);
if (! (accountDir.exists() && accountDir.isDirectory()))
System.out.println("WARNING: Account directory "
+accountPath+"doesn't exist!");
File playerGraphicsDir = new File(accountPath);
if (! (playerGraphicsDir.exists() && playerGraphicsDir.isDirectory()))
System.out.println("WARNING: Player graphics directory "
+playerGraphicsPath+" doesn't exist!");
}
}
Faunis/src/server/Result.java 0000644 0001750 0001750 00000002671 12061600440 015236 0 ustar user user /* Copyright 2012 Simon Ley alias "skarute"
*
* This file is part of Faunis.
*
* Faunis 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.
*
* Faunis 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 Faunis. If not, see
* .
*/
package server;
/** Represents a result of the main server's methods when the method
* may fail.
* The method execution was successful if errorMessage is null,
* you will then find the requested object in the "requested" field.
* If it failed, errorMessage contains a string with the reason. */
public class Result {
private T result;
private String errorMessage;
public Result(T requested, String errorMessage) {
this.result = requested;
this.errorMessage = errorMessage;
}
public T getResult() {
return result;
}
public String getErrorMessage() {
return errorMessage;
}
public boolean successful() {
return (errorMessage == null);
}
}
Faunis/clientData/ 0000775 0001750 0001750 00000000000 12062730610 013070 5 ustar user user Faunis/clientData/playerGraphics/ 0000775 0001750 0001750 00000000000 12062730610 016045 5 ustar user user Faunis/clientData/playerGraphics/arctos/ 0000775 0001750 0001750 00000000000 12062730610 017340 5 ustar user user Faunis/clientData/playerGraphics/arctos/stand/ 0000775 0001750 0001750 00000000000 12062730610 020451 5 ustar user user Faunis/clientData/playerGraphics/arctos/stand/leftArm/ 0000775 0001750 0001750 00000000000 12062730610 022043 5 ustar user user Faunis/clientData/playerGraphics/arctos/stand/leftArm/up.png 0000644 0001750 0001750 00000001345 12062724432 023203 0 ustar user user PNG
IHDR
bOK bKGD Ԃ pHYs t tfx vpAg
̽ TIDAT(c`?Qʠ ͑#!&Wf!pBӨFdk)2F*]g+0B9y - S- zTXtcomment xڅRMk@rjmzKKA5v"pm#[}b}i-2|-e_{|
Mm]eSQ388cI!k["1/ 8mLRpzΚT
$\g7VSzRfz9JGl3L8{qNDhZJ쭲SRfqF= b ďqB+[e
VNɕ03xݡC=jjbdRthSQ'3?WvՏzSwGHºzנ¾jzyT
fkWsDPOu:IJ"Cs쥄7aqaz*TA#}j(:C&2X82tYj^|z| %tEXtdate:create 2012-12-14T22:19:50+01:00Z %tEXtdate:modify 2012-11-01T22:52:54+01:00D IENDB` Faunis/clientData/playerGraphics/arctos/stand/leftArm/right.png 0000644 0001750 0001750 00000001335 12062724432 023673 0 ustar user user PNG
IHDR (վ bKGD Ԃ pHYs t tfx vpAg O~ LIDATc`@G2200ɀ|3001!}Cl'ԊPPPw-F4( \)ԉ zTXtcomment xڅRMk@rjmzKKA5v"pm#[}b}i-2|-e_{|
Mm]eSQ388cI!k["1/ 8mLRpzΚT
$\g7VSzRfz9JGl3L8{qNDhZJ쭲SRfqF= b ďqB+[e
VNɕ03xݡC=jjbdRthSQ'3?WvՏzSwGHºzנ¾jzyT
fkWsDPOu:IJ"Cs쥄7aqaz*TA#}j(:C&2X82tYj^|z| %tEXtdate:create 2012-12-14T22:19:50+01:00Z %tEXtdate:modify 2012-11-01T23:06:18+01:00/ IENDB` Faunis/clientData/playerGraphics/arctos/stand/leftArm/down.png 0000644 0001750 0001750 00000001344 12062724432 023525 0 ustar user user PNG
IHDR r bKGD Ԃ pHYs t tfx vpAg Uf SIDAT8c`/?Z'`Bl`|HX5Er2mgu@FBk6 |'9q'B
.Sk zTXtcomment xڅRMk@rjmzKKA5v"pm#[}b}i-2|-e_{|
Mm]eSQ388cI!k["1/ 8mLRpzΚT
$\g7VSzRfz9JGl3L8{qNDhZJ쭲SRfqF= b ďqB+[e
VNɕ03xݡC=jjbdRthSQ'3?WvՏzSwGHºzנ¾jzyT
fkWsDPOu:IJ"Cs쥄7aqaz*TA#}j(:C&2X82tYj^|z| %tEXtdate:create 2012-12-14T22:19:50+01:00Z %tEXtdate:modify 2012-11-01T21:22:58+01:00KW IENDB` Faunis/clientData/playerGraphics/arctos/stand/leftArm/left.png 0000644 0001750 0001750 00000001334 12062724432 023507 0 ustar user user PNG
IHDR (վ bKGD Ԃ pHYs t tfx vpAg O~ KIDATc`@G2200ɀ|3001!}C(k ڃHE&