commit 5bece4198068c5a2c9541c275397338b08f10081 Author: alterdekim Date: Thu Jan 23 01:14:04 2025 +0300 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d091b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +.idea +*.iws +*.iml +*.ipr +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..eac044d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2e6540 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +![](https://p.w0n.zip/file/b4rB1e) +![](https://p.w0n.zip/file/bkRQ6a) +![](https://p.w0n.zip/file/b6QPna) +# Shararam server +> Feels like a distant memory \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e78e55a --- /dev/null +++ b/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + + com.alterdekim.game + rtmpSpring + 1.0.0 + jar + + + + gitea + https://gitea.awain.net/api/packages/alterwain/maven + + + + + + gitea + https://gitea.awain.net/api/packages/alterwain/maven + + + gitea + https://gitea.awain.net/api/packages/alterwain/maven + + + + + https://blog.awain.net + + + + Michael Wain + alterwain@protonmail.com + The God + https://blog.awain.net + + + + + org.springframework.boot + spring-boot-starter-parent + 3.0.0 + + + + 1.8 + 1.8 + UTF-8 + + + + + com.alterdekim.game + actionScriptDecompiler + 0.0.10 + + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + + + log4j + log4j + 1.2.17 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + + 3.1.1.RELEASE + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + 8.0.33 + + + org.springframework.boot + spring-boot-starter-security + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.11.1 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.11.1 + + + org.projectlombok + lombok + true + 1.18.28 + provided + + + org.javatuples + javatuples + 1.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 14 + 14 + + + + + \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/Application.java b/src/main/java/com/alterdekim/game/Application.java new file mode 100644 index 0000000..bd833c6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/Application.java @@ -0,0 +1,39 @@ +package com.alterdekim.game; + +import com.alterdekim.game.storage.StorageService; +import org.apache.catalina.connector.Connector; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.filter.CharacterEncodingFilter; + +@EnableScheduling +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + CommandLineRunner init(StorageService storageService) { + return (args) -> { + storageService.init(); + }; + } + + @Bean + public ConfigurableServletWebServerFactory webServerFactory() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); + factory.addConnectorCustomizers(connector -> { + connector.setProperty("relaxedQueryChars", "[]"); + connector.setProperty("relaxedPathChars", "[]"); + }); + return factory; + } +} diff --git a/src/main/java/com/alterdekim/game/component/GameServer.java b/src/main/java/com/alterdekim/game/component/GameServer.java new file mode 100644 index 0000000..6664a6a --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/GameServer.java @@ -0,0 +1,230 @@ +package com.alterdekim.game.component; + +import com.alterdekim.game.component.game.*; +import com.alterdekim.game.component.rtmp.ConnectedProcessor; +import com.alterdekim.game.message.*; +import com.alterdekim.game.message.amf.AMFDeserializer; +import com.alterdekim.game.message.amf.AMFObject; +import com.alterdekim.game.message.amf.AMFSerializer; +import com.alterdekim.game.message.amf.AMFValueType; +import com.alterdekim.game.security.AuthenticationUtil; +import com.alterdekim.game.service.LocationService; +import com.alterdekim.game.service.UserService; +import com.alterdekim.game.utils.GameUtils; +import com.alterdekim.game.utils.StringUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.parameters.P; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class GameServer { + + @Autowired + private UserService users; + + @Autowired + private LocationService locationService; + + private Map players; + + public GameServer() { + this.players = new HashMap<>(); + } + + public void onConnect(long playerId, String hwId, ConnectedProcessor connectedProcessor) { + log.warn("GameServer.onConnect() connect: {}", playerId); + Optional username = users.getUsernameById(playerId); + if( username.isEmpty() || !AuthenticationUtil.hwIdAuth(users.findByUsername(username.get()), hwId) ) { + connectedProcessor.close(); + return; + } + this.players.put(playerId, new Player(connectedProcessor, username.get())); + } + + public void onMessage(long playerId, List params ) { + try { + log.info("GameServer.onMessage() pid: {}, params: {}", playerId, params); + switch(CommandType.fromString( (String) params.get(0).getValue() )) { + case UserCommand: + processUserMessage(playerId, AMFDeserializer.deserialize(params.subList(1, params.size()), UserMessage.class)); + break; + case SetLocation: + setPlayerLocation(playerId, AMFDeserializer.deserialize(params.subList(1, params.size()), SetLocationMessage.class)); + break; + case SetUserAvatarState: + setPlayerAvatarState(playerId, AMFDeserializer.deserialize(params.subList(1, params.size()), SetPlayerAvatarState.class)); + break; + case SetUserAvatarPosition: + setPlayerAvatarPosition(playerId, AMFDeserializer.deserialize(params.subList(1, params.size()), SetPlayerAvatarPosition.class)); + break; + default: + log.warn("GameServer.onMessage() unknown command: {}", params.get(0)); + } + } catch (Exception e) { + log.error("GameServer.onMessage() error: {}", e.getMessage(), e); + } + } + + private void setPlayerAvatarPosition(long playerId, SetPlayerAvatarPosition message) { + players.get(playerId).setX((Double) message.getCoordinates().get("x").getValue()); + players.get(playerId).setY((Double) message.getCoordinates().get("y").getValue()); + this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarPosition, + new SetPlayerPosition( + playerId, + new Point((Double) message.getCoordinates().get("x").getValue(), (Double) message.getCoordinates().get("y").getValue()), + message.getTweenerId().longValue() + ) + ); + } + + private void setPlayerAvatarState(long playerId, SetPlayerAvatarState message) { + players.get(playerId).setState(message.getState()); + this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarState, + new SetPlayerState(playerId, message.getState()) + ); + } + + private void processUserMessage(long playerId, UserMessage message) throws JsonProcessingException { + log.info("GameServer.processUserMessage() message: {}", message); + switch (UserCommandType.fromString(message.getCommandType())) { + case GetUserInitData: // todo: add more info about user (see decompiled sources) + String r = new XmlMapper().registerModule(new JavaTimeModule()).writeValueAsString(users.getUserInitInfoByUserId(playerId)); + this.sendResult(playerId, message.getTransactionId(), r); + break; + case UserFriendsGet: // todo: implement + this.sendResult(playerId, message.getTransactionId(), new HashMap<>()); + break; + case UserFriendsRequests: // todo: implement + this.sendResult(playerId, message.getTransactionId(), new HashMap<>()); + break; + case GetClubMap: // todo: implement + this.sendResult(playerId, message.getTransactionId(), ""); + break; + case GetFriendPanelData: + this.sendResult(playerId, message.getTransactionId(), ""); + break; + case UpdateUserData: + this.sendResult(playerId, message.getTransactionId(), null); + break; + case GetUserLocation: + double prevLocation = (Double) message.getArguments().get(2).getValue(); + if( prevLocation == 0.0d ) { + this.sendResult(playerId, + message.getTransactionId(), + new XmlMapper().writeValueAsString( + locationService.getDefaultLocation() + ) + ); + } + break; + case ChatMessage: + String text = (String) message.getArguments().get(1).getValue(); + sendInPlayersLocation(playerId, CommandType.ChatMessage, new ChatMessage(playerId, text)); + break; + case Shoot: + if( message.getSrv().equals("ROOM") ) { + double x = (double) message.getArguments().get(1).getValue(); + double y = (double) message.getArguments().get(2).getValue(); + sendInPlayersLocation(playerId, CommandType.Shoot, new Shoot(playerId, x, y)); + break; + } + this.sendInPlayersLocation(playerId, CommandType.SetUserAvatarState, + new SetPlayerState(playerId, players.get(playerId).getState()) + ); + break; + case GetUserInfo: + getUserInfo(playerId, message); + break; + } + } + + // todo: add more info about user (see decompiled sources) + private void getUserInfo(long playerId, UserMessage message) throws JsonProcessingException { + String r = new XmlMapper().registerModule(new JavaTimeModule()).writeValueAsString(users.getUserInfoByUserId(playerId)); + this.sendResult(playerId, message.getTransactionId(), r); + } + + private void setPlayerLocation(long playerId, SetLocationMessage message) { + Player p = this.players.get(playerId); + this.deleteSelf(playerId, p.getLocationId()); + int prevLocation = p.getLocationId(); + p.setLocationId(GameUtils.extractLocationId(message.getLocation())); + p.setX((Double) message.getCoordinates().get("x").getValue()); + p.setY((Double) message.getCoordinates().get("y").getValue()); + p.setState(message.getStartState()); + this.updateLocationPlayers(playerId, prevLocation); + this.sendResult(playerId, message.getTransactionId(), ""); + } + + private void deleteSelf(long playerId, int locationId) { + this.sendInLocation(locationId, CommandType.RemoveUserFromLocation, playerId); + } + + private void updateLocationPlayers(long playerId, int prevLocation) { + int locationId = this.players.get(playerId).getLocationId(); + this.sendInLocation(prevLocation, CommandType.RemoveUserFromLocation, playerId); + for( int i = 0; i < 5; i++ ) { + this.sendInPlayersLocation(playerId, CommandType.AddUserToLocation, new AddUserToLocation( + playerId, + users.getAvatarById(playerId, this.players.get(playerId)) + )); + } + this.players.keySet().forEach(pid -> { + Player p = this.players.get(pid); + if( p.getLocationId() != locationId ) return; + this.call(playerId, CommandType.AddUserToLocation, new AddUserToLocation( + pid, + users.getAvatarById(pid, this.players.get(pid)) + )); + this.call(playerId, CommandType.SetUserAvatarState, new SetPlayerState( + pid, + this.players.get(pid).getState() + )); + }); + } + + private void sendInPlayersLocation(long playerId, CommandType type, Object obj) { + this.sendInLocation(players.get(playerId).getLocationId(), type, obj); + } + + private void sendInLocation(int locationId, CommandType type, Object obj) { + players.keySet().forEach(k -> { + if( locationId != players.get(k).getLocationId() ) return; + this.call(k, type, obj); + }); + } + + private void sendResult(long playerId, double tid, Object obj) { + this.sendMessage(playerId, new CallMessage("_result", tid, null, obj)); + } + + private void call(long playerId, CommandType function, Object obj) { + this.sendMessage(playerId, new CallMessage(function.getValue(), 0d, null, obj)); + } + + private void sendMessage(long playerId, Object obj) { + try { + this.players.get(playerId) + .getConnection() + .write_packet(AMFSerializer.serialize(obj)); + } catch (IOException e) { + log.error("Unable to send message: {}", e.getMessage()); + } + } + + public void onDisconnect(long playerId) { + log.warn("GameServer.onDisconnect() close: {}", playerId); + if( !this.players.containsKey(playerId) ) return; + this.deleteSelf(playerId, this.players.get(playerId).getLocationId()); + this.players.remove(playerId); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/component/RTMPListener.java b/src/main/java/com/alterdekim/game/component/RTMPListener.java new file mode 100644 index 0000000..83e2c59 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/RTMPListener.java @@ -0,0 +1,66 @@ +package com.alterdekim.game.component; + +import com.alterdekim.game.component.rtmp.ConnectedProcessor; +import com.alterdekim.game.component.rtmp.ConnectionProcessor; +import com.alterdekim.game.component.rtmp.ConnectionState; +import com.alterdekim.game.component.rtmp.HandshakeProcessor; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class RTMPListener extends Thread { + + private Socket sock; + + private RTMPServer parent; + + @Getter + @Setter + private Integer uid; + private InputStream in; + private OutputStream out; + + private ConnectionState connectionState; + + private final Map connectionProcessors = new HashMap<>(); + + public RTMPListener(Socket sock, RTMPServer parent, Integer uid, GameServer gameServer) { + try { + this.uid = uid; + this.parent = parent; + this.sock = sock; + this.in = sock.getInputStream(); + this.out = sock.getOutputStream(); + this.connectionState = ConnectionState.HANDSHAKE; + this.connectionProcessors.put(ConnectionState.HANDSHAKE, new HandshakeProcessor(this.in, this.out, this.sock)); + this.connectionProcessors.put(ConnectionState.CONNECTED, new ConnectedProcessor(this.in, this.out, this.sock, gameServer)); + } catch( Exception e ) { + log.error("RTMPListener error", e); + } + } + + @Override + public void run() { + try { + while(!sock.isClosed()) { + this.connectionState = this.connectionProcessors.get(this.connectionState).process(); + } + this.parent.close(this.getUid()); + } catch( Exception e ) { + log.error("RTMPListener.run() error", e); + } finally { + try { + sock.close(); + } catch (Exception e) { + log.error("RTMPListener.run() error", e); + } + } + } +} diff --git a/src/main/java/com/alterdekim/game/component/RTMPServer.java b/src/main/java/com/alterdekim/game/component/RTMPServer.java new file mode 100644 index 0000000..1221d5c --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/RTMPServer.java @@ -0,0 +1,60 @@ +package com.alterdekim.game.component; + +import com.alterdekim.game.config.ServerConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +@Slf4j +@Component +public class RTMPServer { + + @Autowired + private ServerConfig config; + + private final ConcurrentHashMap clients; + private ServerSocket serverSocket; + + @Autowired + private GameServer gameServer; + + + public RTMPServer() { + this.clients = new ConcurrentHashMap<>(); + } + + public void close( Integer id ) { + clients.remove(id); + } + + @Scheduled(fixedDelay = 5000) + public void start() { + try { + this.serverSocket = new ServerSocket(config.getGameServerPort()); + log.info("RTMPServer.start() started"); + while(true) { + Integer guid = (int) (Math.random() * 10000); + RTMPListener listener = new RTMPListener(serverSocket.accept(), this, guid, gameServer); + log.info("RTMPServer.start() got client"); + this.clients.put(guid, listener); + listener.start(); + } + } catch (IOException e) { + log.error("RTMPServer.start() error", e); + } finally { + try { + serverSocket.close(); + } catch (IOException e) { + log.error("RTMPServer.server.close() error", e); + } + } + } +} diff --git a/src/main/java/com/alterdekim/game/component/StartUpListener.java b/src/main/java/com/alterdekim/game/component/StartUpListener.java new file mode 100644 index 0000000..3187a02 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/StartUpListener.java @@ -0,0 +1,346 @@ +package com.alterdekim.game.component; + +import com.alterdekim.flash.decompiler.FlashDecompiler; +import com.alterdekim.flash.decompiler.ShockwaveFile; +import com.alterdekim.flash.decompiler.compiler.FlashCompiler; +import com.alterdekim.flash.decompiler.mapper.FlashMapper; +import com.alterdekim.flash.decompiler.mapper.OutputObject; +import com.alterdekim.flash.decompiler.tag.DoAction; +import com.alterdekim.flash.decompiler.tag.TagType; +import com.alterdekim.flash.decompiler.translator.Flash2Java; +import com.alterdekim.flash.decompiler.translator.Java2Flash; +import com.alterdekim.game.component.game.BodyPartType; +import com.alterdekim.game.config.ServerConfig; +import com.alterdekim.game.entity.*; +import com.alterdekim.game.repository.BodyPartRepository; +import com.alterdekim.game.repository.SmileRepository; +import com.alterdekim.game.service.*; +import com.alterdekim.game.storage.StorageProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.*; + +@Slf4j +@Component +public class StartUpListener { + + private static final String ADMIN_USERNAME = "admin"; + + @Autowired + private ServerConfig config; + + @Autowired + private StorageProperties storageProperties; + + @Autowired + private UserService userService; + + @Autowired + private MRTService mrtService; + + @Autowired + private MRService mrService; + + @Autowired + private TRService trService; + + @Autowired + private ROService roService; + + @Autowired + private GoodsService goodsService; + + @Autowired + private GoodTagService goodTagService; + + @Autowired + private CatalogService catalogService; + + @Autowired + private GoodMagicService goodMagicService; + + @Autowired + private GoodSmileService goodSmileService; + + @Autowired + private GCDService gcdService; + + @Autowired + private MAService maService; + + @Autowired + private AchievementService achievementService; + + @Autowired + private BackgroundService backgroundService; + + @Autowired + private LibraryService libraryService; + + @Autowired + private SportInventoryService sportInventoryService; + + @Autowired + private VisaService visaService; + + @Autowired + private MissionService missionService; + + @Autowired + private GAAccountService gaAccountService; + + @Autowired + private PriceExchangeService priceExchangeService; + + @Autowired + private PriceLicenseService priceLicenseService; + + @Autowired + private PhoneIconService phoneIconService; + + @Autowired + private GameIconService gameIconService; + + @Autowired + private IncompatibleService incompatibleService; + + @Autowired + private LevelExperienceService levelExperienceService; + + @Autowired + private SmileService smileService; + + @Autowired + private AvatarInventoryService avatarInventoryService; + + @Autowired + private LocationService locationService; + + @Autowired + private PreloaderService preloaderService; + + @Autowired + private PromotionService promotionService; + + @Autowired + private PromotionBannerService promotionBannerService; + + @EventListener + public void onApplicationEvent(ContextRefreshedEvent event) { + + // todo: compile other swf's + if( userService.findByUsername(ADMIN_USERNAME) != null ) { + Map root = new HashMap<>(); + Map mr = new HashMap<>(); + mr.put("MR", FlashMapper.objToMap(mrService.getAll())); + mr.put("RO", FlashMapper.objToMap(roService.getAll())); + mr.put("TR", FlashMapper.objToMap(trService.getAll())); + mr.put("MRT", FlashMapper.simpleToMap(mrtService.getAll())); + root.put("resources", mr); + + try { + Files.write(Path.of("cache/resources.swf"), new FlashCompiler(new Java2Flash().convert(root)).compile(), StandardOpenOption.CREATE); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + if( userService.findByUsername(ADMIN_USERNAME) != null ) return; + String pwd = UUID.randomUUID().toString(); + log.info("Your admin account password is: {}\nChange it later.", pwd); + userService.saveUser(ADMIN_USERNAME, pwd); + + log.info("Moving pre-compiled data to our database."); + try { + processResources(storageProperties.getLocation() + File.separator + config.getDefaultResourcesPath()); + processBase(storageProperties.getLocation() + File.separator + config.getDefaultBasePath()); + // todo: implements miniquests + String miniquests = storageProperties.getLocation() + File.separator + config.getDefaultMiniquestsPath(); + processCatalogs(storageProperties.getLocation() + File.separator + config.getDefaultCatalogsPath()); + } catch (IOException e) { + log.error("Unable to move pre-compiled data to the server's database: {}", e.getMessage()); + } + log.info("Pushing default data blobs to the database"); + pushPreloaders(); + pushPromotions(); + pushPromotionBanners(); + + pushBodyParts(); + pushLocations(); + log.info("Initialization complete"); + } + + private void pushPromotionBanners() { + promotionBannerService.savePromotionBanner(new PromotionBanner(9841, true)); + promotionBannerService.savePromotionBanner(new PromotionBanner(9955, true)); + promotionBannerService.savePromotionBanner(new PromotionBanner(12512, true)); + } + + private void pushPromotions() { + promotionService.savePromotion(new Promotion(5778, "1", true)); + promotionService.savePromotion(new Promotion(5891, "1", true)); + promotionService.savePromotion(new Promotion(9818, "1", true)); + } + + private void pushPreloaders() { + preloaderService.savePreloader(new Preloader(6857, 2000, true)); + preloaderService.savePreloader(new Preloader(11774, 2000, true)); + preloaderService.savePreloader(new Preloader(11994, 2000, true)); + } + + private void pushLocations() { + locationService.addLocation(new Location(47l, 3849l, 400.0d, 250.0d, "Площадь у больнички", true, true)); + } + + private void pushBodyParts() { + List l = new ArrayList<>(); + + l.add(new BodyPart(56L, 56, true, BodyPartType.Body)); + l.add(new BodyPart(60L, 60, true, BodyPartType.Body)); + l.add(new BodyPart(64L, 64, true, BodyPartType.Body)); + l.add(new BodyPart(69L, 69, true, BodyPartType.Body)); + l.add(new BodyPart(73L, 73, true, BodyPartType.Body)); + l.add(new BodyPart(77L, 77, true, BodyPartType.Body)); + l.add(new BodyPart(83L, 83, true, BodyPartType.Body)); + l.add(new BodyPart(89L, 89, true, BodyPartType.Body)); + + l.add(new BodyPart(55L, 55, true, BodyPartType.Legs)); + l.add(new BodyPart(62L, 62, true, BodyPartType.Legs)); + l.add(new BodyPart(66L, 66, true, BodyPartType.Legs)); + l.add(new BodyPart(71L, 71, true, BodyPartType.Legs)); + l.add(new BodyPart(75L, 75, true, BodyPartType.Legs)); + l.add(new BodyPart(80L, 80, true, BodyPartType.Legs)); + l.add(new BodyPart(86L, 86, true, BodyPartType.Legs)); + l.add(new BodyPart(92L, 92, true, BodyPartType.Legs)); + + l.add(new BodyPart(78L, 78, true, BodyPartType.Ears)); + l.add(new BodyPart(84L, 84, true, BodyPartType.Ears)); + l.add(new BodyPart(90L, 90, true, BodyPartType.Ears)); + l.add(new BodyPart(95L, 95, true, BodyPartType.Ears)); + l.add(new BodyPart(96L, 96, true, BodyPartType.Ears)); + l.add(new BodyPart(97L, 97, true, BodyPartType.Ears)); + + l.add(new BodyPart(59L, 59, false, BodyPartType.Eyes)); + l.add(new BodyPart(61L, 61, false, BodyPartType.Eyes)); + l.add(new BodyPart(65L, 65, false, BodyPartType.Eyes)); + l.add(new BodyPart(70L, 70, false, BodyPartType.Eyes)); + l.add(new BodyPart(74L, 74, false, BodyPartType.Eyes)); + l.add(new BodyPart(79L, 79, false, BodyPartType.Eyes)); + l.add(new BodyPart(85L, 85, false, BodyPartType.Eyes)); + l.add(new BodyPart(91L, 91, false, BodyPartType.Eyes)); + + l.add(new BodyPart(58L, 58, false, BodyPartType.Nose)); + l.add(new BodyPart(68L, 68, false, BodyPartType.Nose)); + l.add(new BodyPart(82L, 82, false, BodyPartType.Nose)); + l.add(new BodyPart(88L, 88, false, BodyPartType.Nose)); + l.add(new BodyPart(94L, 94, false, BodyPartType.Nose)); + + l.add(new BodyPart(57L, 57, false, BodyPartType.Mouth)); + l.add(new BodyPart(67L, 67, false, BodyPartType.Mouth)); + l.add(new BodyPart(81L, 81, false, BodyPartType.Mouth)); + l.add(new BodyPart(87L, 87, false, BodyPartType.Mouth)); + l.add(new BodyPart(93L, 93, false, BodyPartType.Mouth)); + + l.add(new BodyPart(63L, 63, false, BodyPartType.Beak)); + l.add(new BodyPart(72L, 72, false, BodyPartType.Beak)); + l.add(new BodyPart(76L, 76, false, BodyPartType.Beak)); + + l.add(new BodyPart(99L, 99, false, BodyPartType.Horn)); + l.add(new BodyPart(100L, 100, false, BodyPartType.Horn)); + l.add(new BodyPart(101L, 101, false, BodyPartType.Horn)); + l.add(new BodyPart(102L, 102, false, BodyPartType.Horn)); + l.add(new BodyPart(103L, 103, false, BodyPartType.Horn)); + + l.add(new BodyPart(457L, 457, false, BodyPartType.Caps)); + l.add(new BodyPart(458L, 458, false, BodyPartType.Caps)); + l.add(new BodyPart(459L, 459, false, BodyPartType.Caps)); + l.add(new BodyPart(460L, 460, false, BodyPartType.Caps)); + l.add(new BodyPart(461L, 461, false, BodyPartType.Caps)); + l.add(new BodyPart(462L, 462, false, BodyPartType.Caps)); + l.add(new BodyPart(463L, 463, false, BodyPartType.Caps)); + l.add(new BodyPart(464L, 464, false, BodyPartType.Caps)); + + l.forEach(i -> avatarInventoryService.saveBodyPart(i)); + } + + private Map decompile(String path) throws IOException { + FlashDecompiler decompiler = new FlashDecompiler(); + ShockwaveFile file = decompiler.loadFromFile(new File(path)); + DoAction da = (DoAction)file.getTags().stream().filter((p) -> p.getType() == TagType.DoAction).findFirst().get(); + return new Flash2Java(da).convert(); + } + + private void processResources(String swf) throws IOException { + log.info("Processing resources"); + + Map o = decompile(swf); + + FlashMapper.mapToObj( + o, + List.of( + new OutputObject(MediaResourcePath.class, true, mrtService), + new OutputObject(TextResource.class, false, trService), + new OutputObject(MediaResource.class, false, mrService), + new OutputObject(ResourceObject.class, false, roService) + ) + ); + } + + private void processCatalogs(String swf) throws IOException { + log.info("Processing catalogs"); + Map o = decompile(swf); + + FlashMapper.mapToObj( + o, + List.of( + new OutputObject(GoodTag.class, true, goodTagService), + new OutputObject(Good.class, false, goodsService), + new OutputObject(Catalog.class, false, catalogService), + new OutputObject(GoodMagic.class, false, goodMagicService), + new OutputObject(GoodSmile.class, false, goodSmileService), + new OutputObject(GoodClubDancefloor.class, false, gcdService) + ) + ); + } + + // todo: implement other stuff from base.swf + private void processBase(String swf) throws IOException { + log.info("Processing base"); + + Map o = decompile(swf); + + FlashMapper.mapToObj( + o, + List.of( + new OutputObject(MagicAbility.class, false, maService), + new OutputObject(Achievement.class, false, achievementService), + new OutputObject(Background.class, true, backgroundService), + new OutputObject(Library.class, false, libraryService), + new OutputObject(SportInventory.class, true, sportInventoryService), + new OutputObject(Visa.class, false, visaService), + new OutputObject(CatalogObject.class, false, catalogService), + new OutputObject(Mission.class, false, missionService), + new OutputObject(GAAccount.class, true, gaAccountService), + new OutputObject(PriceExchange.class, false, priceExchangeService), + new OutputObject(PriceLicense.class, false, priceLicenseService), + new OutputObject(PhoneIcon.class, true, phoneIconService), + new OutputObject(GameIcon.class, true, gameIconService), + new OutputObject(Incompatible.class, false, incompatibleService), + new OutputObject(LevelExperience.class, true, levelExperienceService), + new OutputObject(Smile.class, false, smileService) + ) + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/component/game/AddUserToLocation.java b/src/main/java/com/alterdekim/game/component/game/AddUserToLocation.java new file mode 100644 index 0000000..1b81091 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/AddUserToLocation.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class AddUserToLocation { + private Long playerId; + private PlayerAvatar avatar; +} diff --git a/src/main/java/com/alterdekim/game/component/game/AvatarInventoryItem.java b/src/main/java/com/alterdekim/game/component/game/AvatarInventoryItem.java new file mode 100644 index 0000000..b3af9d8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/AvatarInventoryItem.java @@ -0,0 +1,55 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@Getter +public class AvatarInventoryItem { + @JacksonXmlProperty(localName = "ID", isAttribute = true) + private Long id; + + @JacksonXmlProperty(localName = "TextResourceID", isAttribute = true) + private Integer textResourceID; + + @JacksonXmlProperty(localName = "BodyPartId", isAttribute = true) + private Integer bodyPartId; + + @JacksonXmlProperty(localName = "MediaResourceID", isAttribute = true) + private Integer mediaResourceID; + + @JacksonXmlProperty(localName = "IsUsed", isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isUsed; + + @JacksonXmlProperty(localName = "IsColorable", isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isColorable; + + @JacksonXmlProperty(localName = "Color", isAttribute = true) + private Integer color; + + @JacksonXmlProperty(localName = "IsLimited", isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isLimited; + + @JacksonXmlProperty(localName = "Date", isAttribute = true) + private LocalDateTime date; + + @JacksonXmlProperty(localName = "Pollution", isAttribute = true) + private Integer pollution; + + @JacksonXmlProperty(localName = "IsBodyPart", isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isBodyPart; + + @JacksonXmlProperty(localName = "GoodID", isAttribute = true) + public Long getGoodId() { + return this.id; + } +} diff --git a/src/main/java/com/alterdekim/game/component/game/AvatarInventoryType.java b/src/main/java/com/alterdekim/game/component/game/AvatarInventoryType.java new file mode 100644 index 0000000..c2fa81d --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/AvatarInventoryType.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.component.game; + +public enum AvatarInventoryType { + Clothes, + Magic, + Smile, + House, + HouseFurniture, + Club, + ClubFurniture, + BodyParts +} diff --git a/src/main/java/com/alterdekim/game/component/game/BodyAvatarItem.java b/src/main/java/com/alterdekim/game/component/game/BodyAvatarItem.java new file mode 100644 index 0000000..e43a330 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/BodyAvatarItem.java @@ -0,0 +1,38 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.message.amf.AMFKey; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@AllArgsConstructor +public class BodyAvatarItem { + @AMFKey(name = "BodyPartTypeId") + private Integer bodyPartTypeId; + + @AMFKey(name = "Id") + private Long id; + + @AMFKey(name = "Color") + private Integer color; + + @AMFKey(name = "IsBodyPart") + private Boolean isBodyPart; + + @AMFKey(name = "BodyPartId") + private Integer bodyPartId; + + @AMFKey(name = "GoodID") + private Long goodId; + + @AMFKey(name = "MediaResourceID") + private Integer mediaResourceId; + + @AMFKey(name = "LayerID") + private Integer layerId; + + @AMFKey(name = "GoodTypeID") + private Integer goodTypeId; +} diff --git a/src/main/java/com/alterdekim/game/component/game/BodyPartType.java b/src/main/java/com/alterdekim/game/component/game/BodyPartType.java new file mode 100644 index 0000000..55c3ea5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/BodyPartType.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum BodyPartType { + Body(3, 202, true, 20), + Legs(2, 201, true, 10), + Ears(8, 206, false, 50), + Eyes(7, 205, false, 30), + Nose(6, 204, false, 40), + Mouth(5, 203, false, 26), + Beak(11, 208, false, 41), + Horn(12, 209, false, 55), + Caps(4, 207, false, 45); + + private final int value; + private final long mediaResourceId; + private final boolean isShape; + private final int layerId; +} diff --git a/src/main/java/com/alterdekim/game/component/game/CatalogType.java b/src/main/java/com/alterdekim/game/component/game/CatalogType.java new file mode 100644 index 0000000..2f80950 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/CatalogType.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.component.game; + +public enum CatalogType { + + Collection("collection"), + Filter("filter"), + Shop("shop"); + + public final String value; + + CatalogType(String value) { + this.value = value; + } + + public static CatalogType fromType(String c) { + for (CatalogType type : values()) { + if (type.value.equals(c)) { + return type; + } + } + return CatalogType.Shop; + } +} diff --git a/src/main/java/com/alterdekim/game/component/game/ChatMessage.java b/src/main/java/com/alterdekim/game/component/game/ChatMessage.java new file mode 100644 index 0000000..5803b53 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/ChatMessage.java @@ -0,0 +1,11 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ChatMessage { + private Long playerId; + private String text; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/component/game/ClubAccessType.java b/src/main/java/com/alterdekim/game/component/game/ClubAccessType.java new file mode 100644 index 0000000..f1a29cf --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/ClubAccessType.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum ClubAccessType { + OPEN(1), + CLOSED(0), + FRIENDS_ONLY(2); + + private final Integer value; +} diff --git a/src/main/java/com/alterdekim/game/component/game/CommandType.java b/src/main/java/com/alterdekim/game/component/game/CommandType.java new file mode 100644 index 0000000..62b4b23 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/CommandType.java @@ -0,0 +1,26 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum CommandType { + UserCommand("$"), + SetLocation("_LS"), + RemoveUserFromLocation("_UL"), + AddUserToLocation("_UE"), + SetUserAvatarState("_S"), + SetUserAvatarPosition("_P"), + ChatMessage("_C"), + Shoot("_NUS"), + Unknown(""); + + private final String value; + public static CommandType fromString(String s) { + for( CommandType t : values() ) { + if( t.getValue().equals(s) ) return t; + } + return CommandType.Unknown; + } +} diff --git a/src/main/java/com/alterdekim/game/component/game/GoodClothType.java b/src/main/java/com/alterdekim/game/component/game/GoodClothType.java new file mode 100644 index 0000000..681f50a --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/GoodClothType.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum GoodClothType { + Clothes(1, 212, 36), + Caps(4, 211, 45), + Glasses(14, 210, 31), + Boots(32, 213, 11), + Bangles(36, 214, 56), + Suits(40, 429, 27); + + private final int val; + private final long tabMediaResourceId; + private final int layerId; +} diff --git a/src/main/java/com/alterdekim/game/component/game/InitMagicAbility.java b/src/main/java/com/alterdekim/game/component/game/InitMagicAbility.java new file mode 100644 index 0000000..e16e8f4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/InitMagicAbility.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class InitMagicAbility { + @JacksonXmlProperty(localName = "Id", isAttribute = true) + private Integer id; + @JacksonXmlProperty(localName = "ExpirationDate", isAttribute = true) + private String expirationDate; + @JacksonXmlProperty(localName = "IsLimited", isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isLimited; +} diff --git a/src/main/java/com/alterdekim/game/component/game/LocationObject.java b/src/main/java/com/alterdekim/game/component/game/LocationObject.java new file mode 100644 index 0000000..483459d --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/LocationObject.java @@ -0,0 +1,54 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@JacksonXmlRootElement(localName = "location") +public class LocationObject { + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Long id; + + @JacksonXmlProperty(isAttribute = true, localName = "IsHome") + @JsonSerialize(using = NumericBooleanSerializer.class) + private Boolean isHome; + + @JacksonXmlProperty(isAttribute = true, localName = "IsClub") + @JsonSerialize(using = NumericBooleanSerializer.class) + private Boolean isClub; + + @JacksonXmlProperty(isAttribute = true, localName = "IsLocked") + @JsonSerialize(using = NumericBooleanSerializer.class) + private Boolean isLocked; + + @JacksonXmlProperty(isAttribute = true, localName = "IsVisaAccessPermitted") + @JsonSerialize(using = NumericBooleanSerializer.class) + private Boolean isVisaAccessPermitted; + + @JacksonXmlProperty(isAttribute = true, localName = "MediaResourceID") + private Long mediaResourceId; + + @JacksonXmlProperty(isAttribute = true) + private Double x; + + @JacksonXmlProperty(isAttribute = true) + private Double y; + + @JacksonXmlProperty(isAttribute = true, localName = "Name") + private String name; + + //todo: add location objects + @JacksonXmlProperty(isAttribute = false, localName = "object") + @JacksonXmlElementWrapper(useWrapping = false, localName = "object") + private List objects; +} diff --git a/src/main/java/com/alterdekim/game/component/game/LocationObjectInstance.java b/src/main/java/com/alterdekim/game/component/game/LocationObjectInstance.java new file mode 100644 index 0000000..8b66a34 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/LocationObjectInstance.java @@ -0,0 +1,7 @@ +package com.alterdekim.game.component.game; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class LocationObjectInstance { +} diff --git a/src/main/java/com/alterdekim/game/component/game/Player.java b/src/main/java/com/alterdekim/game/component/game/Player.java new file mode 100644 index 0000000..83a8ae3 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/Player.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.component.rtmp.ConnectedProcessor; +import lombok.*; + +@ToString +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class Player { + private final ConnectedProcessor connection; + private final String username; + private double x; + private double y; + private int locationId; + private double state; +} diff --git a/src/main/java/com/alterdekim/game/component/game/PlayerAvatar.java b/src/main/java/com/alterdekim/game/component/game/PlayerAvatar.java new file mode 100644 index 0000000..9c8dae8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/PlayerAvatar.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.message.amf.AMFKey; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.util.Map; + +@ToString +@Getter +@AllArgsConstructor +public class PlayerAvatar { + @AMFKey(name = "RoleFlags") + private String roleFlags; + + @AMFKey(name = "User") + private UserAvatar user; +} diff --git a/src/main/java/com/alterdekim/game/component/game/PlayerProperties.java b/src/main/java/com/alterdekim/game/component/game/PlayerProperties.java new file mode 100644 index 0000000..7339ac4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/PlayerProperties.java @@ -0,0 +1,38 @@ +package com.alterdekim.game.component.game; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum PlayerProperties { + LocationServerName(PlayerPropertyType.String), + RoleFlags(PlayerPropertyType.Enum), + InstructorLevel(PlayerPropertyType.Integer), + MagicLevel(PlayerPropertyType.Integer), + RaceLevel(PlayerPropertyType.Integer), + UsualTickets(PlayerPropertyType.Integer), + MagicTickets(PlayerPropertyType.Integer), + Experience(PlayerPropertyType.Integer), + Level(PlayerPropertyType.Integer), + //PositiveEnergy(PlayerPropertyType.Integer), + VisaId(PlayerPropertyType.Integer), + CurrentUseCount(PlayerPropertyType.Integer), + LastNewspaperId(PlayerPropertyType.Integer), + IsClubLocked(PlayerPropertyType.Boolean), + ClubAccessType(PlayerPropertyType.Enum), + IsMinorSecretAgent(PlayerPropertyType.Boolean), + HasSnFriends(PlayerPropertyType.Boolean), + IsHomeLocked(PlayerPropertyType.Boolean), + IsLimited(PlayerPropertyType.Boolean), + CampId(PlayerPropertyType.Integer), + NiftTotalCount(PlayerPropertyType.Integer), + DateRegistered(PlayerPropertyType.Date), + MinsLeft(PlayerPropertyType.Integer), + TimesUsed(PlayerPropertyType.Integer), + PhoneCardBalance(PlayerPropertyType.Integer), + WeaponsCount(PlayerPropertyType.Integer), + OxygenUnits(PlayerPropertyType.Integer); + + private final PlayerPropertyType valueType; +} diff --git a/src/main/java/com/alterdekim/game/component/game/PlayerPropertyType.java b/src/main/java/com/alterdekim/game/component/game/PlayerPropertyType.java new file mode 100644 index 0000000..dbe5446 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/PlayerPropertyType.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.component.game; + +public enum PlayerPropertyType { + String, + Integer, + Enum, + Date, + Boolean +} diff --git a/src/main/java/com/alterdekim/game/component/game/Point.java b/src/main/java/com/alterdekim/game/component/game/Point.java new file mode 100644 index 0000000..b933004 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/Point.java @@ -0,0 +1,11 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Point { + private Double x; + private Double y; +} diff --git a/src/main/java/com/alterdekim/game/component/game/RoleFlags.java b/src/main/java/com/alterdekim/game/component/game/RoleFlags.java new file mode 100644 index 0000000..24c362c --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/RoleFlags.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum RoleFlags { + NONE(0), + MEMBER(2), + SA(8), + MODERATOR(131072), + ADMINISTRATOR(262144), + CHIEF(524288), + HIDDEN(1048576); + + private final Integer value; + + public static RoleFlags fromId(int id) { + for (RoleFlags type : values()) { + if (type.value == id) { + return type; + } + } + return RoleFlags.NONE; + } +} diff --git a/src/main/java/com/alterdekim/game/component/game/SetPlayerPosition.java b/src/main/java/com/alterdekim/game/component/game/SetPlayerPosition.java new file mode 100644 index 0000000..a737ad6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/SetPlayerPosition.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SetPlayerPosition { + private Long userId; + private Point point; + private Long tweenerId; +} diff --git a/src/main/java/com/alterdekim/game/component/game/SetPlayerState.java b/src/main/java/com/alterdekim/game/component/game/SetPlayerState.java new file mode 100644 index 0000000..1131e85 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/SetPlayerState.java @@ -0,0 +1,11 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SetPlayerState { + private Long playerId; + private Double state; +} diff --git a/src/main/java/com/alterdekim/game/component/game/Shoot.java b/src/main/java/com/alterdekim/game/component/game/Shoot.java new file mode 100644 index 0000000..5d766ab --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/Shoot.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Shoot { + private Long playerId; + private Double x; + private Double y; +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserAccount.java b/src/main/java/com/alterdekim/game/component/game/UserAccount.java new file mode 100644 index 0000000..0745e62 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserAccount.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.component.game; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class UserAccount { + @JacksonXmlProperty(localName = "PhoneCardBalance", isAttribute = true) + private Integer phoneCardBalance; + + @JacksonXmlProperty(localName = "WeaponsCount", isAttribute = true) + private Integer weaponsCount; + + @JacksonXmlProperty(localName = "OxigenUnits", isAttribute = true) + private Integer oxygenUnits; +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserAvatar.java b/src/main/java/com/alterdekim/game/component/game/UserAvatar.java new file mode 100644 index 0000000..5e2d6c1 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserAvatar.java @@ -0,0 +1,31 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.message.amf.AMFKey; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.Map; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class UserAvatar { + @AMFKey(name = "Level") + private Integer level; + + @AMFKey(name = "Name") + private String name; + + private Double x; + + private Double y; + + @AMFKey(name = "State") + private Double state; + + @AMFKey(name = "Body") + private Map body; +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserCommandType.java b/src/main/java/com/alterdekim/game/component/game/UserCommandType.java new file mode 100644 index 0000000..1762087 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserCommandType.java @@ -0,0 +1,29 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum UserCommandType { + GetUserInitData("_I"), + UserFriendsGet("_UFG"), + UserFriendsRequests("_UFR"), + GetClubMap("_CM"), + GetFriendPanelData("_SNFP"), + UpdateUserData("_D"), + GetUserLocation("_LG"), + ChatMessage("_C"), + Shoot("_NUS"), + GetUserInfo("_UI"), + Unknown(""); + + private final String value; + + public static UserCommandType fromString(String s) { + for( UserCommandType t : values() ) { + if( t.getValue().equals(s) ) return t; + } + return UserCommandType.Unknown; + } +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserInfo.java b/src/main/java/com/alterdekim/game/component/game/UserInfo.java new file mode 100644 index 0000000..d3e276c --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserInfo.java @@ -0,0 +1,120 @@ +package com.alterdekim.game.component.game; + +import com.alterdekim.game.xml.ClubAccessTypeSerializer; +import com.alterdekim.game.xml.LocalDateTimeSerializer; +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.alterdekim.game.xml.RoleFlagsSerializer; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@Getter +@Setter +public class UserInfo { + @JacksonXmlProperty(isAttribute = true, localName = "UserId") + private Long userId; + + @JacksonXmlProperty(isAttribute = true, localName = "RoleFlags") + @JsonSerialize(using = RoleFlagsSerializer.class) + private RoleFlags roleFlags; + + @JacksonXmlProperty(isAttribute = true, localName = "IsLimited") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isLimited; + + @JacksonXmlProperty(isAttribute = true, localName = "IsOnline") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isOnline; + + @JacksonXmlProperty(isAttribute = true, localName = "IsMinorSecretAgent") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isMinorSecretAgent; + + @JacksonXmlProperty(isAttribute = true, localName = "IsHomeLocked") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isHomeLocked; + + @JacksonXmlProperty(isAttribute = true, localName = "IsClubLocked") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isClubLocked; + + @JacksonXmlProperty(isAttribute = true, localName = "IsClubPresent") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isClubPresent; + + @JacksonXmlProperty(isAttribute = true, localName = "ClubAccessType") + @JsonSerialize(using = ClubAccessTypeSerializer.class) + private ClubAccessType clubAccessType; + + @JacksonXmlProperty(isAttribute = true, localName = "IsFriend") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isFriend; + + @JacksonXmlProperty(isAttribute = true, localName = "Experience") + private Integer experience; + + @JacksonXmlProperty(isAttribute = true, localName = "Level") + private Integer level; + + @JacksonXmlProperty(isAttribute = true, localName = "MagicLevel") + private Integer magicLevel; + + @JacksonXmlProperty(isAttribute = true, localName = "RaceLevel") + private Integer raceLevel; + + @JacksonXmlProperty(isAttribute = true, localName = "PhoneId") + private Integer phoneId; + + @JacksonXmlProperty(isAttribute = true, localName = "BackgroundId") + private Integer backgroundId; + + @JacksonXmlProperty(isAttribute = true, localName = "CampId") + private Integer campId; + + @JacksonXmlProperty(isAttribute = true, localName = "NiftTotalCount") + private Integer niftTotalCount; + + @JacksonXmlProperty(isAttribute = true, localName = "LocationServerName") + private String locationServerName; + + @JacksonXmlProperty(isAttribute = true, localName = "DateRegistered") + @JsonSerialize(using = LocalDateTimeSerializer.class) + private LocalDateTime dateRegistered; + + @JacksonXmlProperty(isAttribute = true, localName = "InstructorLevel") + private Integer instructorLevel; + + @JacksonXmlProperty(isAttribute = true, localName = "UsualTickets") + private Integer usualTickets; + + @JacksonXmlProperty(isAttribute = true, localName = "MagicTickets") + private Integer magicTickets; + + @JacksonXmlProperty(isAttribute = true, localName = "VisaId") + private Integer visaId; + + @JacksonXmlProperty(isAttribute = true, localName = "CurrentUseCount") + private Integer currentUseCount; + + @JacksonXmlProperty(isAttribute = true, localName = "LastNewspaperId") + private Integer lastNewspaperId; + + @JacksonXmlProperty(isAttribute = true, localName = "HasSnFriends") + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean hasSnFriends; + + @JacksonXmlProperty(isAttribute = true, localName = "MinsLeft") + private Integer minsLeft; + + @JacksonXmlProperty(isAttribute = true, localName = "TimesUsed") + private Integer timesUsed; + + @JacksonXmlProperty(isAttribute = true, localName = "Name") + private String name; +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserInitInfo.java b/src/main/java/com/alterdekim/game/component/game/UserInitInfo.java new file mode 100644 index 0000000..5faf795 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserInitInfo.java @@ -0,0 +1,32 @@ +package com.alterdekim.game.component.game; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +@JsonRootName("response") +public class UserInitInfo { + @JacksonXmlProperty(localName = "account") + private UserAccount userAccount; + + @JacksonXmlProperty(localName = "user") + private UserInfo userInfo; + + @JacksonXmlProperty(localName = "user_phone") + private UserPhone userPhone; + + @JsonProperty(value = "user_magic_abilities") + @JacksonXmlElementWrapper(useWrapping = true, localName = "user_magic_abilities") + private List magicAbilities; + + @JsonProperty(value = "avatar") + @JacksonXmlElementWrapper(useWrapping = true, localName = "avatar") + private List avatar; +} diff --git a/src/main/java/com/alterdekim/game/component/game/UserPhone.java b/src/main/java/com/alterdekim/game/component/game/UserPhone.java new file mode 100644 index 0000000..01f1f17 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/UserPhone.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.component.game; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class UserPhone { + @JacksonXmlProperty(localName = "BufferSize", isAttribute = true) + private Integer bufferSize; + + @JacksonXmlProperty(localName = "SkinMediaResourceID", isAttribute = true) + private Long skinMediaResourceId; + + @JacksonXmlProperty(localName = "PhoneId", isAttribute = true) + private Integer phoneId; + + @JacksonXmlProperty(localName = "Balance", isAttribute = true) + private Integer balance; +} diff --git a/src/main/java/com/alterdekim/game/component/rtmp/ConnectedProcessor.java b/src/main/java/com/alterdekim/game/component/rtmp/ConnectedProcessor.java new file mode 100644 index 0000000..1a4ea23 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/rtmp/ConnectedProcessor.java @@ -0,0 +1,190 @@ +package com.alterdekim.game.component.rtmp; + +import com.alterdekim.game.component.GameServer; +import com.alterdekim.game.message.*; +import com.alterdekim.game.message.amf.*; +import com.alterdekim.game.message.serializer.BinaryMessageSerializer; +import com.alterdekim.game.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.javatuples.Pair; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Slf4j +public class ConnectedProcessor extends ConnectionProcessor { + + private final Long startTimestamp; + private ConnectedState state; + private List message; + private Integer expectedMessageLength; + private Integer lastStreamId; + private byte[] previousHeader; + private GameServer gameServer; + private Long playerId; + + public ConnectedProcessor(InputStream inputStream, OutputStream outputStream, Socket sock, GameServer gameServer) { + super(inputStream, outputStream, sock); + this.state = ConnectedState.WAIT_FOR_MESSAGE; + this.message = new ArrayList<>(); + this.gameServer = gameServer; + this.previousHeader = new byte[11]; + this.startTimestamp = System.currentTimeMillis(); + } + + @Override + public ConnectionState getState() { + return ConnectionState.CONNECTED; + } + + @Override + public ConnectionState process() throws IOException { + int b = this.getInputStream().read(); + if (b == -1) { + close(); return ConnectionState.CONNECTED; + } + if( this.state == ConnectedState.WAIT_FOR_MESSAGE && b != 0x03 && b != 0x43 && b != 0x83 && b != 0x02 && b != 0xC3 ) { + log.info("process() WAIT_FOR_MESSAGE not 03,43,83"); + return ConnectionState.CONNECTED; + } + this.state = switch(state) { + case WAIT_FOR_MESSAGE -> processWaitForMessage(parseChunkHeader(b)); + case LISTENS_FOR_MESSAGE -> processListensForMessage((byte) b); + }; + return ConnectionState.CONNECTED; + } + + private ConnectedState processWaitForMessage(Pair packetHeader) { + try { + byte[] header = switch (packetHeader.getValue0()) { + case Full -> new byte[11]; + case WithoutStreamId -> new byte[7]; + case OnlyTimestamp -> new byte[3]; + default -> new byte[0]; + }; + if(this.getInputStream().read(header) == -1) return ConnectedState.WAIT_FOR_MESSAGE; + if(header.length == 11) this.lastStreamId = BinaryMessageSerializer.deserializeInteger(Arrays.copyOfRange(header, 7, 11)); + this.message.clear(); + this.expectedMessageLength = BinaryMessageSerializer.deserializeInteger( + Arrays.copyOfRange( header.length >= 7 ? header : this.previousHeader, 3, 6) + ); + if( header.length >= 7 ) this.previousHeader = header; + if( header.length == 11 && packetHeader.getValue1() == 2 ) { + byte[] b = new byte[4]; + if( this.getInputStream().read(b) == -1 ) return ConnectedState.WAIT_FOR_MESSAGE; + this.getOutputStream().write(new byte[] {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, b[0], b[1], b[2], b[3]}); + this.getOutputStream().flush(); + return ConnectedState.WAIT_FOR_MESSAGE; + } + return ConnectedState.LISTENS_FOR_MESSAGE; + } catch (IOException e) { + log.error("ConnectedProcessor.processWaitForMessage()", e); + } + return ConnectedState.WAIT_FOR_MESSAGE; + } + + private ConnectedState processListensForMessage(byte b) { + if(this.message.size() >= this.expectedMessageLength) { + this.processMessage(); + return ConnectedState.WAIT_FOR_MESSAGE; + } + if( b == (byte) 0xC3 ) { this.expectedMessageLength--; return ConnectedState.LISTENS_FOR_MESSAGE; } + this.message.add(b); + if(this.message.size() >= this.expectedMessageLength) { + this.processMessage(); + return ConnectedState.WAIT_FOR_MESSAGE; + } + return ConnectedState.LISTENS_FOR_MESSAGE; + } + + private void processMessage() { + byte[] bytes = SerializerUtils.bytesToPrimitive(message); + log.info("ConnectedProcessor.processMessage() messageString: {}", StringUtils.bytesToHex(bytes)); + try { + List mr = AMFDeserializer.deserialize(message); + if( !mr.isEmpty() && + mr.get(0).getType() == AMFValueType.STRING && + mr.get(0).getValue().equals("connect") ) { + this.playerId = Long.parseLong((String) mr.get(3).getValue()); + this.gameServer.onConnect(this.playerId, (String) mr.get(5).getValue(), this); + this.getOutputStream().write( StringUtils.hexStringToByteArray("020000000000040500000000002625A0020000000000050600000000002625A00202000000000004010000000000001000030000000000F214000000000200075F726573756C74003FF0000000000000030006666D7356657202000E464D532F342C302C302C31313231000C6361706162696C697469657300406FE0000000000000046D6F6465003FF00000000000000000090300056C6576656C0200067374617475730004636F646502001D4E6574436F6E6E656374696F6E2E436F6E6E6563742E53756363657373000B6465736372697074696F6E020015436F6E6E656374696F6E207375636365656465642E000E6F626A656374456E636F64696E670000000000000000000004646174610800000000000776657273696F6E02000A342C302C302C31313231000009000009") ); + this.getOutputStream().flush(); + return; + } + this.gameServer.onMessage(this.playerId, mr); + } catch (Exception e) { + log.error("ConnectedProcessor.processMessage() error: {}", e.getMessage(), e); + } + } + + // returns chunk type and stream id + private Pair parseChunkHeader(int b) { + return Pair.with(ChunkHeaderType.fromInt((b & 0xC0) >> 6), b & 0x3F); + } + + @Override + public void write(List bytes) throws IOException { + List l = new ArrayList<>(Arrays.asList((byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x14, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00)); + l.addAll(bytes); + super.write(l); + } + + public void write_packet(List bytes) throws IOException { + List l = new ArrayList<>(Collections.singletonList((byte) 0x03)); + List data = new ArrayList<>(); + int cnt = 0; + for( int i = 0; i < bytes.size(); i++ ) { + data.add(bytes.get(i)); + if( i >= 4095 && ((i+1) % 4096 == 0) && (i+1) < bytes.size() ) { + data.add((byte) 0xC3); + cnt++; + } + } + l.addAll(int2bytes((int) ((System.currentTimeMillis() - this.startTimestamp) / 1000L), 3)); + l.addAll(int2bytes(data.size() - cnt, 3)); + l.add((byte) 0x14); + l.addAll(int2bytes(this.lastStreamId, 4)); + l.addAll(data); + super.write(l); + } + + private List int2bytes(int u, int n) { + List b = new ArrayList<>(); + for( int i = n-1; i >= 0; i-- ) { + b.add((byte) ((u >> i*8) & 0xFF)); + } + return b; + } + + @Override + public void close() { + this.gameServer.onDisconnect(this.playerId); + super.close(); + } + + @AllArgsConstructor + private enum ChunkHeaderType { + Full(0), + WithoutStreamId(1), + OnlyTimestamp(2), + UsePrevious(3), + Unknown(4); + + private final int type; + + private static ChunkHeaderType fromInt(int b) { + for( ChunkHeaderType c : values() ) { + if( c.type == b ) return c; + } + return Unknown; + } + } +} diff --git a/src/main/java/com/alterdekim/game/component/rtmp/ConnectedState.java b/src/main/java/com/alterdekim/game/component/rtmp/ConnectedState.java new file mode 100644 index 0000000..307c57f --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/rtmp/ConnectedState.java @@ -0,0 +1,6 @@ +package com.alterdekim.game.component.rtmp; + +public enum ConnectedState { + WAIT_FOR_MESSAGE, + LISTENS_FOR_MESSAGE +} diff --git a/src/main/java/com/alterdekim/game/component/rtmp/ConnectionProcessor.java b/src/main/java/com/alterdekim/game/component/rtmp/ConnectionProcessor.java new file mode 100644 index 0000000..17235cf --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/rtmp/ConnectionProcessor.java @@ -0,0 +1,40 @@ +package com.alterdekim.game.component.rtmp; + +import com.alterdekim.game.message.SerializerUtils; +import com.alterdekim.game.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.List; + +@Slf4j +@Getter +@AllArgsConstructor +public abstract class ConnectionProcessor { + private InputStream inputStream; + private OutputStream outputStream; + private Socket sock; + + public abstract ConnectionState getState(); + public abstract ConnectionState process() throws IOException; + + public void write(List bytes) throws IOException { + log.info("Packet sent: {}", StringUtils.bytesToHex(SerializerUtils.bytesToPrimitive(bytes))); + this.outputStream.write(SerializerUtils.bytesToPrimitive(bytes)); + this.outputStream.flush(); + } + + public void close() { + log.info("ConnectionProcessor.close()"); + try { + this.sock.close(); + } catch (IOException e) { + log.error("ConnectionProcessor.close()", e); + } + } +} diff --git a/src/main/java/com/alterdekim/game/component/rtmp/ConnectionState.java b/src/main/java/com/alterdekim/game/component/rtmp/ConnectionState.java new file mode 100644 index 0000000..f43de55 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/rtmp/ConnectionState.java @@ -0,0 +1,6 @@ +package com.alterdekim.game.component.rtmp; + +public enum ConnectionState { + HANDSHAKE, + CONNECTED +} diff --git a/src/main/java/com/alterdekim/game/component/rtmp/HandshakeProcessor.java b/src/main/java/com/alterdekim/game/component/rtmp/HandshakeProcessor.java new file mode 100644 index 0000000..4462ff4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/rtmp/HandshakeProcessor.java @@ -0,0 +1,77 @@ +package com.alterdekim.game.component.rtmp; + + +import com.alterdekim.game.message.ByteMessage; +import com.alterdekim.game.message.SerializerUtils; +import com.alterdekim.game.message.serializer.BinaryMessageSerializer; +import com.alterdekim.game.utils.ByteUtils; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.IntStream; + +@Slf4j +public class HandshakeProcessor extends ConnectionProcessor { + + private HandshakeState handshakeState; + + public HandshakeProcessor(InputStream inputStream, OutputStream outputStream, Socket sock) { + super(inputStream, outputStream, sock); + this.handshakeState = HandshakeState.C0S0; + } + + @Override + public ConnectionState getState() { + return ConnectionState.HANDSHAKE; + } + + @Override + public ConnectionState process() throws IOException { + switch (handshakeState) { + case C0S0: + processFirstMessage(); + break; + case C1S1: + processSecondMessage(); + break; + case C2S2: + return processThirdMessage(); + } + return getState(); + } + + private void processFirstMessage() throws IOException { + byte[] b = new byte[1]; + if( this.getInputStream().read(b) == -1 || b[0] != 0x03 ) {close(); return;} + this.getOutputStream().write(b); + this.handshakeState = HandshakeState.C1S1; + } + + private void processSecondMessage() throws IOException { + byte[] b = new byte[1536]; + if( this.getInputStream().read(b) == -1 ) { close(); return; } + this.getOutputStream().write(b); + this.handshakeState = HandshakeState.C2S2; + } + + private ConnectionState processThirdMessage() throws IOException { + byte[] b = new byte[1536]; + if( this.getInputStream().read(b) == -1 ) { close(); return ConnectionState.HANDSHAKE; } + List response = new ArrayList<>(); + IntStream.range(0, 1536).forEach(i -> response.add( (byte) ((Math.random() * 253) + 1) ) ); + write(response); + return ConnectionState.CONNECTED; + } + + private enum HandshakeState { + C0S0, + C1S1, + C2S2 + } +} diff --git a/src/main/java/com/alterdekim/game/config/ServerConfig.java b/src/main/java/com/alterdekim/game/config/ServerConfig.java new file mode 100644 index 0000000..4678a0e --- /dev/null +++ b/src/main/java/com/alterdekim/game/config/ServerConfig.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +public class ServerConfig { + @Value("${srv.rtmpPort}") private Integer gameServerPort; + @Value("${srv.resPathSWF}") private String defaultResourcesPath; + @Value("${srv.basePathSWF}") private String defaultBasePath; + @Value("${srv.catPathSWF}") private String defaultCatalogsPath; + @Value("${srv.mqPathSWF}") private String defaultMiniquestsPath; +} diff --git a/src/main/java/com/alterdekim/game/controller/AsyncController.java b/src/main/java/com/alterdekim/game/controller/AsyncController.java new file mode 100644 index 0000000..de71e4d --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/AsyncController.java @@ -0,0 +1,121 @@ +package com.alterdekim.game.controller; + +import com.alterdekim.game.controller.result.async.*; +import com.alterdekim.game.entity.User; +import com.alterdekim.game.security.AuthenticationUtil; +import com.alterdekim.game.service.*; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +@RestController +public class AsyncController { + + @Autowired + private MiniquestService miniquestService; + + @Autowired + private PhoneMessageService phoneMessageService; + + @Autowired + private PostcardService postcardService; + + @Autowired + private PreloaderService preloaderService; + + @Autowired + private PromotionBannerService promotionBannerService; + + @Autowired + private PromotionService promotionService; + + @Autowired + private UserService userService; + + @PostMapping("/async/Ping") + @ResponseBody + public ResponseEntity ping() { + return ResponseEntity.ok(""); + } + + @PostMapping("/async/ServerAction") + @ResponseBody + public ResponseEntity serverAction() { + try { + //User user = AuthenticationUtil.authProfile(userService).orElse(userService.findByUsername("jerk")); + + User user = AuthenticationUtil.authProfile(userService).get(); + + XmlMapper xmlMapper = new XmlMapper(); + xmlMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); + xmlMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + ServerActionResult serverActionResult = new ServerActionResult(); + serverActionResult.setSn_status(new SnStatus(false)); + serverActionResult.setUser_name(new UserName(user.getUsername())); + serverActionResult.setFlags(new Flags(true, 13)); + serverActionResult.setRequests(new Grants(0)); + serverActionResult.setGrants(new Grants(0)); + List items = new ArrayList<>(); + items.add(new ConfigItem(1, ServerActionConfigType.IsPreloaderEnabled, "1", "bool")); // play with it + items.add(new ConfigItem(2, ServerActionConfigType.SynchronizeAvatarRotation, "1", "bool")); + items.add(new ConfigItem(3, ServerActionConfigType.InitialVolumeValue, "30", "number")); + items.add(new ConfigItem(4, ServerActionConfigType.IsStartupHomeLocation, "0", "bool")); + items.add(new ConfigItem(5, ServerActionConfigType.AccessRoleFlags, "0", "number")); + items.add(new ConfigItem(6, ServerActionConfigType.IsInternational, "0", "bool")); // play with it + items.add(new ConfigItem(7, ServerActionConfigType.TypeWeapon, "1", "number")); + items.add(new ConfigItem(8, ServerActionConfigType.StatisticsSendInterval, "300", "number")); + items.add(new ConfigItem(9, ServerActionConfigType.SwfVersion, "", "string")); + items.add(new ConfigItem(10, ServerActionConfigType.LanguageId, "1", "number")); // play with it + items.add(new ConfigItem(11, ServerActionConfigType.SnId, "1", "number")); + items.add(new ConfigItem(12, ServerActionConfigType.AutoServerSelectionAllowed, "0", "bool")); + List s_items = new ArrayList<>(); + s_items.add(new ServerItem(1, 202, 5, "rtmp://localhost:8888/shararam", 0, 1, 5, 5.1)); + + serverActionResult.setPhone(new PhoneMessages(phoneMessageService.getAllPhoneMessages())); + serverActionResult.setMiniquest(miniquestService.getAllMiniquests()); + serverActionResult.setPostcard(new PostcardMessages(postcardService.getAllPostcards())); + serverActionResult.setPreloader(preloaderService.getAllPreloaders()); + serverActionResult.setPromotion(promotionService.getAllPromotions()); + serverActionResult.setPromotion_banner(promotionBannerService.getAllPromotionBanners()); + + + List tutorials = new ArrayList<>(); + tutorials.add(new Tutorial(-1, 1)); + tutorials.add(new Tutorial(1, 1)); + tutorials.add(new Tutorial(2, 1)); + tutorials.add(new Tutorial(3, 1)); + tutorials.add(new Tutorial(4, 1)); + tutorials.add(new Tutorial(5, 1)); + serverActionResult.setTutorial(tutorials); + + List cdatas = new ArrayList<>(); + cdatas.add(new ServerActionConfig(items)); + cdatas.add(new ServerActionSystem(1, "cache/resources.swf")); // swf/cache/rus/resources[19].swf + cdatas.add(new ServerActionUser(user.getId(), AuthenticationUtil.getToken(user), AuthenticationUtil.getToken(user), 2)); + cdatas.add(new ServerActionServers(s_items)); + serverActionResult.setCdata(cdatas); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_XML) + .body(xmlMapper.writeValueAsString(serverActionResult)); + } catch (JsonProcessingException e) { + log.error(e.getMessage(), e); + } + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/alterdekim/game/controller/CacheController.java b/src/main/java/com/alterdekim/game/controller/CacheController.java new file mode 100644 index 0000000..f8ef309 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/CacheController.java @@ -0,0 +1,55 @@ +package com.alterdekim.game.controller; + +import com.alterdekim.flash.decompiler.compiler.FlashCompiler; +import com.alterdekim.flash.decompiler.mapper.FlashMapper; +import com.alterdekim.flash.decompiler.translator.Java2Flash; +import com.alterdekim.game.service.MRService; +import com.alterdekim.game.service.MRTService; +import com.alterdekim.game.service.ROService; +import com.alterdekim.game.service.TRService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +public class CacheController { + + @Autowired + private MRService mrService; + + @Autowired + private MRTService mrtService; + + @Autowired + private ROService roService; + + @Autowired + private TRService trService; + + @GetMapping("/cache/resources.swf") + public ResponseEntity generateResources() { + try { + byte[] b = Files.readAllBytes(Path.of("cache/resources.swf")); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/x-shockwave-flash")) + .contentLength(b.length) + .body(b); + } catch (IOException e) { + log.error("generateResources error: {}", e.getMessage()); + } + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/controller/FileServerController.java b/src/main/java/com/alterdekim/game/controller/FileServerController.java new file mode 100644 index 0000000..3fc0c52 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/FileServerController.java @@ -0,0 +1,48 @@ +package com.alterdekim.game.controller; + +import com.alterdekim.game.storage.FileSystemStorageService; +import com.alterdekim.game.storage.StorageFileNotFoundException; +import com.alterdekim.game.storage.StorageProperties; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.nio.file.Files; +import java.nio.file.Path; + +@Slf4j +@RestController +@EnableConfigurationProperties(StorageProperties.class) +public class FileServerController { + + @Autowired + private FileSystemStorageService storageService; + + @RequestMapping(value = "/static/{*resPath}", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity serveFile(@PathVariable("resPath") String resPath, HttpServletRequest request) { + try { + String filename = request.getRequestURL().substring(request.getRequestURL().indexOf("/static/")+8); + Path path = storageService.load(filename); + return ResponseEntity.ok().contentType(switch(resPath.substring(1, 4)) { + case "swf" -> MediaType.parseMediaType("application/x-shockwave-flash"); + case "css" -> MediaType.parseMediaType("text/css"); + case "_js" -> MediaType.parseMediaType("text/javascript"); + case "img" -> MediaType.parseMediaType("image/jpeg"); + default -> MediaType.TEXT_PLAIN; + }).body(Files.readAllBytes(path)); + } catch (Exception e) { + log.error(e.getMessage()); + } + return ResponseEntity.notFound().build(); + } + + @ExceptionHandler(StorageFileNotFoundException.class) + public ResponseEntity handleStorageFileNotFound(StorageFileNotFoundException exc) { + return ResponseEntity.notFound().build(); + } +} + diff --git a/src/main/java/com/alterdekim/game/controller/SignUpController.java b/src/main/java/com/alterdekim/game/controller/SignUpController.java new file mode 100644 index 0000000..5149f2a --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/SignUpController.java @@ -0,0 +1,122 @@ +package com.alterdekim.game.controller; + +import com.alterdekim.game.component.game.AvatarInventoryType; +import com.alterdekim.game.controller.result.signup.*; +import com.alterdekim.game.entity.AvatarInventory; +import com.alterdekim.game.entity.User; +import com.alterdekim.game.security.AuthenticationUtil; +import com.alterdekim.game.service.AvatarInventoryService; +import com.alterdekim.game.service.IncompatibleService; +import com.alterdekim.game.service.UserService; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Base64; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Controller +public class SignUpController { + + @Autowired + private AvatarInventoryService avatarInventoryService; + + @Autowired + private IncompatibleService incompatibleService; + + @Autowired + private UserService userService; + + @PostMapping("/ConstructorACHandler.ashx") + public ResponseEntity avatarController() { + + AvatarController controller = new AvatarController( + "Smesharik", + new AvatarObject( + new Incompatibles(incompatibleService.getAll()), + List.of( + new AvatarObjectSection("Shape", "Образ", avatarInventoryService.getShapeGroups()), + new AvatarObjectSection("BodyParts", "Части тела", avatarInventoryService.getBodyPartGroups()), + new AvatarObjectSection("Inventory", "Вещи", avatarInventoryService.getAvatarInventoryGroups()) + ) + ) + ); + try { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body( + new XmlMapper().writeValueAsString(controller) + ); + } catch (JsonProcessingException e) { + log.error("AvatarConstructor controller error: {}", e.getMessage()); + } + return ResponseEntity.noContent().build(); + } + + @PostMapping("/signup") + public ResponseEntity signup( + @RequestParam("username") String username, + @RequestParam("password") String password, + @RequestParam("avatar") String avatar) { + avatar = new String(Base64.getDecoder().decode(avatar)); + + XmlMapper xmlMapper = new XmlMapper(); + xmlMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); + xmlMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + SignUpAvatar av = null; + try { + av = xmlMapper.readValue(avatar, SignUpAvatar.class); + } catch (JsonProcessingException e) { + log.error(e.getMessage()); + return ResponseEntity.badRequest().body("bad_avatar"); + } + User existingUser = userService.findByUsername(username); + + if(existingUser != null && existingUser.getUsername() != null && !existingUser.getUsername().isEmpty() ) { + return ResponseEntity.badRequest().body("already_exists"); + } + + if(!AuthenticationUtil.checkCredentials(username, password)) { + return ResponseEntity.badRequest().body("bad_credentials_format"); + } + + long userId = userService.saveUser(username, password); + + try { + av.getInventory() + .stream() + .filter(i -> i.getID() != 98) + .map(i -> new AvatarInventory(userId, i.getID().longValue(), true, AvatarInventoryType.Clothes)) + .forEach(i -> avatarInventoryService.addGoodToInventory(i)); + + av.getBodyParts() + .stream() + .filter(i -> i.getID() != 98) + .map(i -> new AvatarInventory(userId, i.getID().longValue(), true, AvatarInventoryType.BodyParts, i.getColor() == null ? 0 : i.getColor())) + .forEach(i -> avatarInventoryService.addGoodToInventory(i)); + } catch (Exception e) { + userService.removeUser(userId); + return ResponseEntity.badRequest().body("bad_avatar"); + } + + return ResponseEntity.ok("ok"); + } + + @GetMapping("/login") + public String loginPage(Model model) { + Optional u = AuthenticationUtil.authProfile(userService); + model.addAttribute("status", u.isPresent() ? "login_success" : "login_failed"); + return "login"; + } +} diff --git a/src/main/java/com/alterdekim/game/controller/StaticController.java b/src/main/java/com/alterdekim/game/controller/StaticController.java new file mode 100644 index 0000000..2b58e7b --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/StaticController.java @@ -0,0 +1,51 @@ +package com.alterdekim.game.controller; + +import com.alterdekim.game.service.GoodsService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.io.File; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Controller +public class StaticController { + + @Autowired + private GoodsService goodsService; + + + @GetMapping("/main") + public String mainPage() { + return "main"; + } + + @GetMapping("/") + public String defPage() { + return "index"; + } + + @GetMapping("/push") + public String pushPage() { + return "db"; + } + + @GetMapping("/crossdomain.xml") + public ResponseEntity crossdomain() { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n"); + } + +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ConfigItem.java b/src/main/java/com/alterdekim/game/controller/result/async/ConfigItem.java new file mode 100644 index 0000000..059d406 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ConfigItem.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +@JsonRootName(value = "item") +public class ConfigItem { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer Id; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private ServerActionConfigType Parameter; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private String Value; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private String Type; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/Flags.java b/src/main/java/com/alterdekim/game/controller/result/async/Flags.java new file mode 100644 index 0000000..80e8d2d --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/Flags.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.controller.result.async; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class Flags { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean IsUserDetailsMissing; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer EntranceCount; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/Grants.java b/src/main/java/com/alterdekim/game/controller/result/async/Grants.java new file mode 100644 index 0000000..58b9989 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/Grants.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + + +@AllArgsConstructor +public class Grants { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer ReceivingCount; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/PhoneMessages.java b/src/main/java/com/alterdekim/game/controller/result/async/PhoneMessages.java new file mode 100644 index 0000000..b61af0c --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/PhoneMessages.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.controller.result.async; + +import com.alterdekim.game.entity.PhoneMessage; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "phone") +public class PhoneMessages { + @JacksonXmlProperty(localName = "messages") + @JacksonXmlElementWrapper(useWrapping = true, localName = "messages") + private List messages; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/PostcardMessages.java b/src/main/java/com/alterdekim/game/controller/result/async/PostcardMessages.java new file mode 100644 index 0000000..4de9e78 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/PostcardMessages.java @@ -0,0 +1,24 @@ +package com.alterdekim.game.controller.result.async; + +import com.alterdekim.game.entity.PhoneMessage; +import com.alterdekim.game.entity.Postcard; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "postcard") +public class PostcardMessages { + @JacksonXmlProperty(localName = "messages") + @JacksonXmlElementWrapper(useWrapping = true, localName = "messages") + private List messages; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionCData.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionCData.java new file mode 100644 index 0000000..119d34b --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionCData.java @@ -0,0 +1,3 @@ +package com.alterdekim.game.controller.result.async; + +public abstract class ServerActionCData {} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfig.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfig.java new file mode 100644 index 0000000..f7b97b8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfig.java @@ -0,0 +1,22 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "config") +public class ServerActionConfig extends ServerActionCData { + @JacksonXmlProperty(localName = "item") + @JacksonXmlElementWrapper(useWrapping = false, localName = "item") + private List items; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfigType.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfigType.java new file mode 100644 index 0000000..4967fcf --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionConfigType.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.controller.result.async; + +public enum ServerActionConfigType { + IsPreloaderEnabled, + SynchronizeAvatarRotation, + InitialVolumeValue, + IsStartupHomeLocation, + AccessRoleFlags, + IsInternational, + TypeWeapon, + StatisticsSendInterval, + SwfVersion, + LanguageId, + SnId, + AutoServerSelectionAllowed +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionResult.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionResult.java new file mode 100644 index 0000000..485ee46 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionResult.java @@ -0,0 +1,69 @@ +package com.alterdekim.game.controller.result.async; + +import com.alterdekim.game.entity.Miniquest; +import com.alterdekim.game.entity.Preloader; +import com.alterdekim.game.entity.Promotion; +import com.alterdekim.game.entity.PromotionBanner; +import com.alterdekim.game.xml.CryptoSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "response") +public class ServerActionResult { + + @JsonProperty + private SnStatus sn_status; + + @JsonProperty + private UserName user_name; + + @JsonProperty + private List preloader; + + @JsonProperty + private Flags flags; + + @JacksonXmlProperty(localName = "tutorial") + @JacksonXmlElementWrapper(useWrapping = true, localName = "tutorial") + private List tutorial; + + @JsonProperty + private Grants grants; + + @JsonProperty + private Grants requests; + + @JsonProperty + private List promotion; + + @JsonProperty + private List promotion_banner; + + @JsonProperty + private PhoneMessages phone; + + @JsonProperty + private PostcardMessages postcard; + + @JsonProperty + @JacksonXmlElementWrapper(useWrapping = true, localName = "miniquest") + private List miniquest; + + @JacksonXmlProperty(localName = "cdata") + @JsonSerialize(using = CryptoSerializer.class) + @JacksonXmlElementWrapper(useWrapping = false, localName = "cdata") + private List cdata; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionServers.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionServers.java new file mode 100644 index 0000000..4c10156 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionServers.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "servers") +public class ServerActionServers extends ServerActionCData { + @JacksonXmlProperty(localName = "item") + @JacksonXmlElementWrapper(useWrapping = false, localName = "item") + private List items; +} + diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionSystem.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionSystem.java new file mode 100644 index 0000000..5df40bc --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionSystem.java @@ -0,0 +1,31 @@ +package com.alterdekim.game.controller.result.async; + + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "system") +public class ServerActionSystem extends ServerActionCData { + + @JacksonXmlProperty(isAttribute = true) + private Integer RVersion; + + @JacksonXmlProperty(isAttribute = true) + private String RPath; + + @JacksonXmlProperty(isAttribute = true, localName = "ServerDate") + public String ServerDate() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = new Date(); + return format.format(date); + } +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerActionUser.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionUser.java new file mode 100644 index 0000000..7c47fb2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerActionUser.java @@ -0,0 +1,26 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@AllArgsConstructor +@NoArgsConstructor +@JsonRootName(value = "user") +public class ServerActionUser extends ServerActionCData { + + @JacksonXmlProperty(isAttribute = true) + private Long UserId; + + @JacksonXmlProperty(isAttribute = true) + private String hwId; + + @JacksonXmlProperty(isAttribute = true) + private String ticketId; + + @JacksonXmlProperty(isAttribute = true) + private Integer RoleFlags; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/ServerItem.java b/src/main/java/com/alterdekim/game/controller/result/async/ServerItem.java new file mode 100644 index 0000000..8048f47 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/ServerItem.java @@ -0,0 +1,35 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +@JsonRootName(value = "item") +public class ServerItem { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer Id; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer TRId; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer RId; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private String RTMPUrl; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer Load; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer FriendsCount; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer ClubsCount; + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Double Weight; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/SnStatus.java b/src/main/java/com/alterdekim/game/controller/result/async/SnStatus.java new file mode 100644 index 0000000..00db0e6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/SnStatus.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.controller.result.async; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class SnStatus { + @JsonProperty + @JsonSerialize(using= NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true) + private Boolean IsBinded; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/Tutorial.java b/src/main/java/com/alterdekim/game/controller/result/async/Tutorial.java new file mode 100644 index 0000000..a293c51 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/Tutorial.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class Tutorial { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer Id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private Integer State; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/UserName.java b/src/main/java/com/alterdekim/game/controller/result/async/UserName.java new file mode 100644 index 0000000..9503f44 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/UserName.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.controller.result.async; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class UserName { + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + private String Value; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/async/UserResult.java b/src/main/java/com/alterdekim/game/controller/result/async/UserResult.java new file mode 100644 index 0000000..c34d962 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/async/UserResult.java @@ -0,0 +1,155 @@ +package com.alterdekim.game.controller.result.async; + + +import com.alterdekim.game.component.game.ClubAccessType; +import com.alterdekim.game.component.game.RoleFlags; +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@JsonRootName(value = "user") +public class UserResult { + + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "UserId") + private Long id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "Name") + private String username; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "LocationServerName") + private String locationServerName; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "RoleFlags") + private RoleFlags roleFlags; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "InstructorLevel") + private Integer instructorLevel; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "MagicLevel") + private Integer magicLevel; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "RaceLevel") + private Integer raceLevel; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "PhoneID") + private Integer phoneID; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "BackgroundID") + private Integer backgroundID; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "UsualTickets") + private Integer usualTickets; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "MagicTickets") + private Integer magicTickets; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "Experience") + private Integer experience; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "Level") + private Integer level; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "PositiveEnergy") + private Integer positiveEnergy; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "VisaId") + private Integer visaId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "CurrentUseCount") + private Integer currentUseCount; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "LastNewspaperId") + private Integer lastNewspaperId; + + @JsonSerialize(using= NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsClubPresent") + private Boolean isClubPresent; + + @JsonSerialize(using = NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsClubLocked") + private Boolean isClubLocked; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "ClubAccessType") + private ClubAccessType clubAccessType; + + @JsonSerialize(using= NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsMinorSecretAgent") + private Boolean isMinorSecretAgent; + + @JsonSerialize(using= NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "HasSnFriends") + private Boolean hasSnFriends; + + @JsonSerialize(using= NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsHomeLocked") + private Boolean isHomeLocked; + + @JsonSerialize(using = NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsLimited") + private Boolean isLimited; + + @JsonSerialize(using = NumericBooleanSerializer.class) + @JacksonXmlProperty(isAttribute = true, localName = "IsOnline") + private Boolean isOnline; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "CampId") + private Integer campId; + + @Column(nullable = false) + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "NiftTotalCount") + private Integer niftTotalCount; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "DateRegistered") + private Date dateRegistered; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "MinsLeft") + private Integer minsLeft; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "TimesUsed") + private Integer timesUsed; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "IsFriend") + private Boolean isFriend; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPart.java b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPart.java new file mode 100644 index 0000000..1370666 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPart.java @@ -0,0 +1,24 @@ +package com.alterdekim.game.controller.result.signup; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class AvatarBodyPart { + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Integer id; + + @JacksonXmlProperty(isAttribute = true, localName = "Color") + private Integer color; + + @JacksonXmlProperty(isAttribute = true, localName = "IsUsed") + @JsonSerialize(using = NumericBooleanSerializer.class) + private Boolean isUsed; + + @JacksonXmlProperty(isAttribute = true, localName = "URL") + private String url; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPartGroup.java b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPartGroup.java new file mode 100644 index 0000000..4eff16c --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarBodyPartGroup.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class AvatarBodyPartGroup { + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Integer id; + + @JacksonXmlProperty(isAttribute = true, localName = "Name") + private String name; + + @JacksonXmlProperty(isAttribute = true, localName = "URL") + private String url; + + @JacksonXmlProperty(isAttribute = true, localName = "LayerID") + private Integer layerId; + + @JacksonXmlProperty(localName = "part") + @JacksonXmlElementWrapper(useWrapping = false, localName = "part") + private List parts; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/AvatarController.java b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarController.java new file mode 100644 index 0000000..1d0ad3f --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarController.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@JacksonXmlRootElement(localName = "root") +public class AvatarController { + @JacksonXmlProperty(isAttribute = true, localName = "UserName") + private String username; + + @JacksonXmlProperty(isAttribute = false, localName = "Avatar") + private AvatarObject avatar; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObject.java b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObject.java new file mode 100644 index 0000000..b1737d7 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObject.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.*; + +import java.util.List; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class AvatarObject { + @JacksonXmlProperty(isAttribute = false, localName = "Incompatibles") + private Incompatibles incompatibles; + + @JacksonXmlProperty(localName = "item") + @JacksonXmlElementWrapper(useWrapping = false, localName = "item") + private List items; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObjectSection.java b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObjectSection.java new file mode 100644 index 0000000..03c5ce8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/AvatarObjectSection.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.*; + +import java.util.List; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class AvatarObjectSection { + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private String id; + @JacksonXmlProperty(isAttribute = true, localName = "Name") + private String name; + + @JacksonXmlProperty(localName = "group") + @JacksonXmlElementWrapper(useWrapping = false, localName = "group") + private List groups; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/Incompatibles.java b/src/main/java/com/alterdekim/game/controller/result/signup/Incompatibles.java new file mode 100644 index 0000000..6cca39d --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/Incompatibles.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.controller.result.signup; + +import com.alterdekim.game.entity.Incompatible; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class Incompatibles { + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private final String id = "Incompatibles"; + + @JacksonXmlProperty(localName = "incompatible") + @JacksonXmlElementWrapper(useWrapping = false, localName = "incompatible") + private List items; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatar.java b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatar.java new file mode 100644 index 0000000..b8d4226 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatar.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import lombok.*; + +import java.util.List; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@JacksonXmlRootElement(localName = "UserAvatar") +public class SignUpAvatar { + @JsonProperty(value = "BodyParts") + @JacksonXmlElementWrapper(useWrapping = true, localName = "BodyParts") + private List BodyParts; + @JsonProperty(value = "Inventory") + @JacksonXmlElementWrapper(useWrapping = true, localName = "Inventory") + private List Inventory; + @JacksonXmlProperty(localName = "pixels") + private String pixels; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarBodyPart.java b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarBodyPart.java new file mode 100644 index 0000000..ce88417 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarBodyPart.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.*; + +@Getter +@AllArgsConstructor +@Setter +@NoArgsConstructor +@ToString +public class SignUpAvatarBodyPart { + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "Color") + private Integer Color; + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Integer ID; +} diff --git a/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarInventory.java b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarInventory.java new file mode 100644 index 0000000..97b99b2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/controller/result/signup/SignUpAvatarInventory.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.controller.result.signup; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class SignUpAvatarInventory { + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "ActionID") + private Integer ActionID; + @JsonProperty + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Integer ID; +} diff --git a/src/main/java/com/alterdekim/game/crypto/RC4.java b/src/main/java/com/alterdekim/game/crypto/RC4.java new file mode 100644 index 0000000..7ca7b6a --- /dev/null +++ b/src/main/java/com/alterdekim/game/crypto/RC4.java @@ -0,0 +1,49 @@ +package com.alterdekim.game.crypto; + +public class RC4 { + private final byte[] S = new byte[256]; + private final byte[] T = new byte[256]; + private final int keylen; + + public RC4(final byte[] key) { + if (key.length < 1 || key.length > 256) { + throw new IllegalArgumentException( + "key must be between 1 and 256 bytes"); + } else { + keylen = key.length; + for (int i = 0; i < 256; i++) { + S[i] = (byte) i; + T[i] = key[i % keylen]; + } + int j = 0; + byte tmp; + for (int i = 0; i < 256; i++) { + j = (j + S[i] + T[i]) & 0xFF; + tmp = S[j]; + S[j] = S[i]; + S[i] = tmp; + } + } + } + + public byte[] encrypt(final byte[] plaintext) { + final byte[] ciphertext = new byte[plaintext.length]; + int i = 0, j = 0, k, t; + byte tmp; + for (int counter = 0; counter < plaintext.length; counter++) { + i = (i + 1) & 0xFF; + j = (j + S[i]) & 0xFF; + tmp = S[j]; + S[j] = S[i]; + S[i] = tmp; + t = (S[i] + S[j]) & 0xFF; + k = S[t]; + ciphertext[counter] = (byte) (plaintext[counter] ^ k); + } + return ciphertext; + } + + public byte[] decrypt(final byte[] ciphertext) { + return encrypt(ciphertext); + } +} diff --git a/src/main/java/com/alterdekim/game/entity/Achievement.java b/src/main/java/com/alterdekim/game/entity/Achievement.java new file mode 100644 index 0000000..a001c8e --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Achievement.java @@ -0,0 +1,37 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="achievements") +@FlashClass(name = "ac") +public class Achievement { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "TypeId") + private Integer typeId; + + @Column(nullable = false) + @FlashField(name = "TRId") + private Integer trId; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mrId; +} diff --git a/src/main/java/com/alterdekim/game/entity/AvatarInventory.java b/src/main/java/com/alterdekim/game/entity/AvatarInventory.java new file mode 100644 index 0000000..aefc10a --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/AvatarInventory.java @@ -0,0 +1,51 @@ +package com.alterdekim.game.entity; + + +import com.alterdekim.game.component.game.AvatarInventoryType; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="avatar_inventory") +public class AvatarInventory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long Id; + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private Long goodId; + + @Column(nullable = false) + private Boolean isUsed; + + @Enumerated(value = EnumType.STRING) + private AvatarInventoryType type; + + @Column(nullable = false) + private Integer color = 0; + + public AvatarInventory(Long userId, Long goodId, Boolean isUsed, AvatarInventoryType type) { + this.userId = userId; + this.goodId = goodId; + this.isUsed = isUsed; + this.type = type; + } + + public AvatarInventory(Long userId, Long goodId, Boolean isUsed, AvatarInventoryType type, Integer color) { + this.userId = userId; + this.goodId = goodId; + this.isUsed = isUsed; + this.type = type; + this.color = color; + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/entity/Background.java b/src/main/java/com/alterdekim/game/entity/Background.java new file mode 100644 index 0000000..312b38e --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Background.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="backgrounds") +@FlashClass(name = "bg") +public class Background { + + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashValue(type = FlashValueType.Value) + @Column(nullable = false) + private Integer mediaResourceId; +} diff --git a/src/main/java/com/alterdekim/game/entity/BodyPart.java b/src/main/java/com/alterdekim/game/entity/BodyPart.java new file mode 100644 index 0000000..60e2fae --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/BodyPart.java @@ -0,0 +1,29 @@ +package com.alterdekim.game.entity; + + +import com.alterdekim.game.component.game.BodyPartType; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="bodyparts") +public class BodyPart { + @Id + private Long id; + + @Column(nullable = false) + private Integer mediaResourceId; + + @Column(nullable = false) + private Boolean isColorable; + + @Enumerated(EnumType.STRING) + private BodyPartType type; +} diff --git a/src/main/java/com/alterdekim/game/entity/Catalog.java b/src/main/java/com/alterdekim/game/entity/Catalog.java new file mode 100644 index 0000000..f9a0703 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Catalog.java @@ -0,0 +1,49 @@ +package com.alterdekim.game.entity; + + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import com.alterdekim.game.component.game.CatalogType; +import com.alterdekim.game.mapper.String2CatalogTypeConverter; +import com.alterdekim.game.mapper.String2DateConverter; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.Date; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="catalogs") +@FlashClass(name = "c") +public class Catalog { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Enumerated(EnumType.STRING) + @FlashField(name = "Type", useCustomConverter = true, converter = String2CatalogTypeConverter.class) + private CatalogType type; + + @Column(nullable = true) + @FlashField(name = "TRId") + private Integer trId; + + @Column(nullable = true) + @FlashField(name = "MRId") + private Integer mrId; + + @Column(nullable = true) + @FlashField(name = "Tag") + private Integer tag; + + @Column(nullable = true) + @FlashField(name = "PublishDate", useCustomConverter = true, converter = String2DateConverter.class) + private LocalDateTime publishDate; +} diff --git a/src/main/java/com/alterdekim/game/entity/CatalogObject.java b/src/main/java/com/alterdekim/game/entity/CatalogObject.java new file mode 100644 index 0000000..e36f098 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/CatalogObject.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="catalogs_o") +@FlashClass(name = "ct") +public class CatalogObject { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "TypeId") + private Integer typeId; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mrId; +} diff --git a/src/main/java/com/alterdekim/game/entity/GAAccount.java b/src/main/java/com/alterdekim/game/entity/GAAccount.java new file mode 100644 index 0000000..1be1933 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GAAccount.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="ga_account") +@FlashClass(name = "ga") +public class GAAccount { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashValue(type = FlashValueType.Value) + @Column(nullable = false) + private String value; +} diff --git a/src/main/java/com/alterdekim/game/entity/GameIcon.java b/src/main/java/com/alterdekim/game/entity/GameIcon.java new file mode 100644 index 0000000..200a400 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GameIcon.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "game_icons") +@FlashClass(name = "gi") +public class GameIcon { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashValue(type = FlashValueType.Value) + private Integer iconMRId; +} diff --git a/src/main/java/com/alterdekim/game/entity/Good.java b/src/main/java/com/alterdekim/game/entity/Good.java new file mode 100644 index 0000000..4472815 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Good.java @@ -0,0 +1,102 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import com.alterdekim.game.component.game.RoleFlags; +import com.alterdekim.game.mapper.Integer2BooleanConverter; +import com.alterdekim.game.mapper.Integer2RoleFlagsConverter; +import com.alterdekim.game.mapper.String2DateConverter; +import com.alterdekim.game.xml.RoleFlagsDeserializer; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.Date; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="goods") +@ToString +@FlashClass(name = "g") +public class Good { + + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashField(name = "MRId") + @Column(nullable = false) + private Integer mRId = -1; + + @FlashField(name = "TRId") + @Column(nullable = false) + private Integer tRId = -1; + + @FlashField(name = "IconMRId") + @Column(nullable = false) + private Integer iconMRId = -1; + + @FlashField(name = "DescTRId") + @Column(nullable = false) + private Integer descTRId = -1; + + @FlashField(name = "GoodTypeId") + @Column(nullable = false) + private Integer goodTypeId; + + @FlashField(name = "RoleFlags", useCustomConverter = true, converter = Integer2RoleFlagsConverter.class) + @Enumerated(EnumType.STRING) + private RoleFlags roleFlags = RoleFlags.NONE; + + @FlashField(name = "UsualTickets") + @Column(nullable = false) + private Integer usualTickets = 0; + + @FlashField(name = "MagicTickets") + @Column(nullable = false) + private Integer magicTickets = 0; + + @FlashField(name = "DefCurrency") + @Column(nullable = false) + private Integer defCurrency; + + @FlashField(name = "LevelThreshold") + @Column(nullable = false) + private Integer levelThreshold; + + @FlashField(name = "UnlockCost") + @Column(nullable = false) + private Integer unlockCost; + + @FlashField(name = "SortOrder") + @Column(nullable = false) + private Integer sortOrder; + + @FlashField(name = "PublishDate", useCustomConverter = true, converter = String2DateConverter.class) + @Column(nullable = false) + private LocalDateTime publishDate; + + @FlashField(name = "Tags") + @Column(nullable = false) + private String tags; + + @FlashField(name = "LayerId") + @Column(nullable = false) + private Integer layerId = 0; + + @FlashField(name = "IsActive", useCustomConverter = true, converter = Integer2BooleanConverter.class) + @Column(nullable = false) + private Boolean isActive; + + @FlashField(name = "ItemCount") + @Column(nullable = false) + private Integer itemCount = -1; +} diff --git a/src/main/java/com/alterdekim/game/entity/GoodClubDancefloor.java b/src/main/java/com/alterdekim/game/entity/GoodClubDancefloor.java new file mode 100644 index 0000000..3a11aea --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GoodClubDancefloor.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="gcds") +@FlashClass(name = "gcd") +public class GoodClubDancefloor { + + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "Days") + private Integer days; +} diff --git a/src/main/java/com/alterdekim/game/entity/GoodMagic.java b/src/main/java/com/alterdekim/game/entity/GoodMagic.java new file mode 100644 index 0000000..e1a88f2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GoodMagic.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="gms") +@FlashClass(name = "gm") +public class GoodMagic { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "MAId") + private Integer magicAbilityId; + + @Column(nullable = true) + @FlashField(name = "Days") + private Integer days; +} diff --git a/src/main/java/com/alterdekim/game/entity/GoodSmile.java b/src/main/java/com/alterdekim/game/entity/GoodSmile.java new file mode 100644 index 0000000..8abd224 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GoodSmile.java @@ -0,0 +1,37 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="good_smiles") +@ToString +@FlashClass(name = "gs") +public class GoodSmile { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "SId") + private Integer smileId; + + @Column(nullable = false) + @FlashField(name = "Smiles") + private String smiles; + + @Column(nullable = true) + @FlashField(name = "Days") + private Integer days; +} diff --git a/src/main/java/com/alterdekim/game/entity/GoodTag.java b/src/main/java/com/alterdekim/game/entity/GoodTag.java new file mode 100644 index 0000000..43ed6fd --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/GoodTag.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="good_tag") +@FlashClass(name = "t") +public class GoodTag { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashValue(type = FlashValueType.Value) + private String tag; +} diff --git a/src/main/java/com/alterdekim/game/entity/Incompatible.java b/src/main/java/com/alterdekim/game/entity/Incompatible.java new file mode 100644 index 0000000..a5b1bb5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Incompatible.java @@ -0,0 +1,50 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@FlashClass(name = "ic") +@Entity +@Table(name = "incompatibles") +public class Incompatible { + @Id + @FlashValue(type = FlashValueType.Id) + @JacksonXmlProperty(isAttribute = true, localName = "ID") + private Long id; + + @FlashField(name = "IncGID0") + @Column(nullable = true) + @JacksonXmlProperty(isAttribute = true, localName = "IncGID0") + private Integer incGID0; + + @FlashField(name = "IncGID1") + @Column(nullable = true) + @JacksonXmlProperty(isAttribute = true, localName = "IncGID1") + private Integer incGID1; + + @FlashField(name = "IncBPID0") + @Column(nullable = true) + @JacksonXmlProperty(isAttribute = true, localName = "IncBPID0") + private Integer incBPID0; + + @FlashField(name = "IncBPID1") + @Column(nullable = true) + @JacksonXmlProperty(isAttribute = true, localName = "IncBPID1") + private Integer incBPID1; +} diff --git a/src/main/java/com/alterdekim/game/entity/LevelExperience.java b/src/main/java/com/alterdekim/game/entity/LevelExperience.java new file mode 100644 index 0000000..c8c609b --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/LevelExperience.java @@ -0,0 +1,31 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@FlashClass(name = "le") +@Entity +@Table(name = "level_experience") +public class LevelExperience { + + @Id + @FlashValue(type = FlashValueType.Id) + private Long level; + + @Column(nullable = false) + @FlashValue(type = FlashValueType.Value) + private Integer experience; +} diff --git a/src/main/java/com/alterdekim/game/entity/Library.java b/src/main/java/com/alterdekim/game/entity/Library.java new file mode 100644 index 0000000..cf09407 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Library.java @@ -0,0 +1,37 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="library") +@FlashClass(name = "lb") +public class Library { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "TRId") + private Integer textResourceId; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mediaResourceId; + + @Column(nullable = false) + @FlashField(name = "Status") + private Integer status; +} diff --git a/src/main/java/com/alterdekim/game/entity/Location.java b/src/main/java/com/alterdekim/game/entity/Location.java new file mode 100644 index 0000000..5141425 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Location.java @@ -0,0 +1,49 @@ +package com.alterdekim.game.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Entity +@Table(name = "locations") +public class Location { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long locationId; + + @Column(nullable = false) + private Long mediaResourceId; + + @Column(nullable = false) + private Double x; + + @Column(nullable = false) + private Double y; + + @Column(nullable = false, length = 65555) + private String name; + + @Column(nullable = false) + private Boolean isDefault; + + @Column(nullable = false) + private Boolean isEnabled; + + public Location(Long locationId, Long mediaResourceId, Double x, Double y, String name, Boolean isDefault, Boolean isEnabled) { + this.locationId = locationId; + this.mediaResourceId = mediaResourceId; + this.x = x; + this.y = y; + this.name = name; + this.isDefault = isDefault; + this.isEnabled = isEnabled; + } +} diff --git a/src/main/java/com/alterdekim/game/entity/MagicAbility.java b/src/main/java/com/alterdekim/game/entity/MagicAbility.java new file mode 100644 index 0000000..69fa703 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/MagicAbility.java @@ -0,0 +1,57 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="magic_ability") +@FlashClass(name = "ma") +public class MagicAbility { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "TypeId") + private Integer typeId; + + @Column(nullable = true) + @FlashField(name = "duration") + private Integer duration; + + @Column(nullable = true) + @FlashField(name = "TweenerId") + private Integer tweenerId; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mediaResourceId; + + @Column(nullable = false) + @FlashField(name = "TRId") + private Integer textResourceId; + + @Column(nullable = false) + @FlashField(name = "IconMRId") + private Integer iconMRId; + + @Column(nullable = false) + @FlashField(name = "IsVisibleInInfoPanel") + private Boolean isVisibleInInfoPanel; + + @Column(nullable = true) + @FlashField(name = "IsMovable") + private Boolean isMovable; +} diff --git a/src/main/java/com/alterdekim/game/entity/MediaResource.java b/src/main/java/com/alterdekim/game/entity/MediaResource.java new file mode 100644 index 0000000..9f008c4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/MediaResource.java @@ -0,0 +1,37 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="mrids") +@FlashClass(name = "mr") +public class MediaResource { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashField(name = "TId") + @Column(nullable = false) + private Integer tId; + + @FlashField(name = "Url") + @Column(nullable = false) + private String url; + + @FlashField(name = "V") + @Column(nullable = true) + private String v; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/entity/MediaResourcePath.java b/src/main/java/com/alterdekim/game/entity/MediaResourcePath.java new file mode 100644 index 0000000..bf061fb --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/MediaResourcePath.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="mrts") +@FlashClass(name = "mrt") +public class MediaResourcePath { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashValue(type = FlashValueType.Value) + private String val; +} diff --git a/src/main/java/com/alterdekim/game/entity/Miniquest.java b/src/main/java/com/alterdekim/game/entity/Miniquest.java new file mode 100644 index 0000000..36dca48 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Miniquest.java @@ -0,0 +1,62 @@ +package com.alterdekim.game.entity; + + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="miniquests") +@JsonRootName(value = "i") +public class Miniquest { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonProperty + private Long Id; + + @JsonIgnore + @Column(nullable = false) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean isTask; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Long MiniquestId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean IsPostponed; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean IsFinished; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String StartDate; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer Counter; +} diff --git a/src/main/java/com/alterdekim/game/entity/Mission.java b/src/main/java/com/alterdekim/game/entity/Mission.java new file mode 100644 index 0000000..be3e908 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Mission.java @@ -0,0 +1,41 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="missions") +@FlashClass(name = "mi") +public class Mission { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mediaResourceId; + + @Column(nullable = false) + @FlashField(name = "Type") + private Integer type; + + @Column(nullable = false) + @FlashField(name = "IsActive") + private Boolean isActive; + + @Column(nullable = false) + @FlashField(name = "CheckRole") + private Boolean checkRole; +} diff --git a/src/main/java/com/alterdekim/game/entity/PhoneIcon.java b/src/main/java/com/alterdekim/game/entity/PhoneIcon.java new file mode 100644 index 0000000..bc5a099 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/PhoneIcon.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "phone_icons") +@FlashClass(name = "ph") +public class PhoneIcon { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashValue(type = FlashValueType.Value) + @Column(nullable = false) + private Integer iconMRId; +} diff --git a/src/main/java/com/alterdekim/game/entity/PhoneMessage.java b/src/main/java/com/alterdekim/game/entity/PhoneMessage.java new file mode 100644 index 0000000..fcdb64a --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/PhoneMessage.java @@ -0,0 +1,53 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="phone_messages") +@JsonRootName(value = "item") +public class PhoneMessage { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonProperty + private Long Id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String DateSent; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Long SenderId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String SenderName; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String Text; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean IsNew; +} diff --git a/src/main/java/com/alterdekim/game/entity/Postcard.java b/src/main/java/com/alterdekim/game/entity/Postcard.java new file mode 100644 index 0000000..4bd4096 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Postcard.java @@ -0,0 +1,52 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="postcards") +@JsonRootName(value = "item") +public class Postcard { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonProperty + private Long Id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String DateSent; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Long SenderId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String SenderName; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer CardId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + @JsonSerialize(using= NumericBooleanSerializer.class) + private Boolean IsNew; +} diff --git a/src/main/java/com/alterdekim/game/entity/Preloader.java b/src/main/java/com/alterdekim/game/entity/Preloader.java new file mode 100644 index 0000000..79b4a53 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Preloader.java @@ -0,0 +1,48 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="preloaders") +@JsonRootName(value = "i") +public class Preloader { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonIgnore + private Long id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer MRId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer ShowTime; + + @JsonIgnore + @Column(nullable = false) + private Boolean isSelected; + + public Preloader(Integer MRId, Integer showTime, Boolean isSelected) { + this.MRId = MRId; + ShowTime = showTime; + this.isSelected = isSelected; + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/entity/PriceExchange.java b/src/main/java/com/alterdekim/game/entity/PriceExchange.java new file mode 100644 index 0000000..7b1873f --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/PriceExchange.java @@ -0,0 +1,34 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="price_exchange") +@FlashClass(name = "pe") +public class PriceExchange { + + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "UT") + private Integer ut; + + @Column(nullable = false) + @FlashField(name = "MT") + private Integer mt; +} diff --git a/src/main/java/com/alterdekim/game/entity/PriceLicense.java b/src/main/java/com/alterdekim/game/entity/PriceLicense.java new file mode 100644 index 0000000..0bd4fc5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/PriceLicense.java @@ -0,0 +1,35 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "price_licence") +@FlashClass(name = "pl") +public class PriceLicense { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "LicenceId") + private Integer licenceId; + + @Column(nullable = false) + @FlashField(name = "MT") + private Integer mt; +} diff --git a/src/main/java/com/alterdekim/game/entity/Promotion.java b/src/main/java/com/alterdekim/game/entity/Promotion.java new file mode 100644 index 0000000..043e308 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Promotion.java @@ -0,0 +1,48 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="promotions") +@JsonRootName(value = "i") +public class Promotion { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonIgnore + private Long id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer MRId; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private String State; + + @JsonIgnore + @Column(nullable = false) + private Boolean isSelected; + + public Promotion(Integer MRId, String state, Boolean isSelected) { + this.MRId = MRId; + State = state; + this.isSelected = isSelected; + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/entity/PromotionBanner.java b/src/main/java/com/alterdekim/game/entity/PromotionBanner.java new file mode 100644 index 0000000..7d64083 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/PromotionBanner.java @@ -0,0 +1,42 @@ +package com.alterdekim.game.entity; + + +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="promotion_banners") +@JsonRootName(value = "i") +public class PromotionBanner { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JsonIgnore + private Long id; + + @JsonProperty + @JacksonXmlProperty(isAttribute = true) + @Column(nullable = false) + private Integer MRId; + + @JsonIgnore + @Column(nullable = false) + private Boolean isSelected; + + public PromotionBanner(Integer MRId, Boolean isSelected) { + this.MRId = MRId; + this.isSelected = isSelected; + } +} diff --git a/src/main/java/com/alterdekim/game/entity/ResourceObject.java b/src/main/java/com/alterdekim/game/entity/ResourceObject.java new file mode 100644 index 0000000..1896a94 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/ResourceObject.java @@ -0,0 +1,61 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="roids") +@FlashClass(name = "ro") +public class ResourceObject { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashField(name = "X") + @Column(nullable = true) + private Double x; + + @FlashField(name = "Y") + @Column(nullable = true) + private Double y; + + @FlashField(name = "Width") + @Column(nullable = true) + private Double width; + + @FlashField(name = "Height") + @Column(nullable = true) + private Double height; + + @FlashField(name = "IsCloseHide") + @Column(nullable = true) + private Boolean isCloseHide; + + @FlashField(name = "IsBackHide") + @Column(nullable = true) + private Boolean isBackHide; + + @FlashField(name = "Tongue") + @Column(nullable = true) + private String tongue; + + @FlashField(name = "Side") + @Column(nullable = true) + private String side; + + @FlashField(name = "Offset") + @Column(nullable = true) + private Integer offset; +} diff --git a/src/main/java/com/alterdekim/game/entity/Role.java b/src/main/java/com/alterdekim/game/entity/Role.java new file mode 100644 index 0000000..c999115 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Role.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="roles") +public class Role +{ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable=false, unique=true) + private String name; + + @ManyToMany(mappedBy="roles") + private List users; +} diff --git a/src/main/java/com/alterdekim/game/entity/Smile.java b/src/main/java/com/alterdekim/game/entity/Smile.java new file mode 100644 index 0000000..a081b51 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Smile.java @@ -0,0 +1,39 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@FlashClass(name = "sm") +@Entity +@Table(name = "smiles") +public class Smile { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashField(name = "MRId") + @Column(nullable = false) + private Integer mRId; + + @FlashField(name = "BorderColor") + @Column(nullable = true) + private Integer borderColor; + + @FlashField(name = "BackgroundColor") + @Column(nullable = true) + private Integer backgroundColor; +} diff --git a/src/main/java/com/alterdekim/game/entity/SportInventory.java b/src/main/java/com/alterdekim/game/entity/SportInventory.java new file mode 100644 index 0000000..2660ac4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/SportInventory.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="sport_inventory") +@FlashClass(name = "si") +public class SportInventory { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashValue(type = FlashValueType.Value) + private Integer mediaResourceId; +} diff --git a/src/main/java/com/alterdekim/game/entity/TextResource.java b/src/main/java/com/alterdekim/game/entity/TextResource.java new file mode 100644 index 0000000..a998302 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/TextResource.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="trids") +@FlashClass(name = "tr") +public class TextResource { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @FlashField(name = "OId") + @Column(nullable = true) + private Integer oId; + + @FlashField(name = "H") + @Column(nullable = false, length = 65555) + private String h; +} diff --git a/src/main/java/com/alterdekim/game/entity/User.java b/src/main/java/com/alterdekim/game/entity/User.java new file mode 100644 index 0000000..8e28077 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/User.java @@ -0,0 +1,46 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.component.game.ClubAccessType; +import com.alterdekim.game.component.game.RoleFlags; +import com.alterdekim.game.xml.NumericBooleanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String username; + + @Column(nullable = false) + @JsonIgnore + private String password; + + @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.MERGE) + @JoinTable( + name="users_roles", + joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")}, + inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")}) + private List roles = new ArrayList<>(); +} diff --git a/src/main/java/com/alterdekim/game/entity/UserProperty.java b/src/main/java/com/alterdekim/game/entity/UserProperty.java new file mode 100644 index 0000000..0c9e299 --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/UserProperty.java @@ -0,0 +1,46 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.game.component.game.PlayerProperties; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name="user_props") +public class UserProperty { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long userId; + + @Column(nullable = true) + private String stringVal; + + @Column(nullable = true) + private Boolean booleanVal; + + @Column(nullable = true) + private Integer integerVal; + + @Column(nullable = true) + private LocalDateTime dateVal; + + @Enumerated(EnumType.STRING) + private PlayerProperties type; + + public UserProperty(Long userId, String stringVal, Boolean booleanVal, Integer integerVal, LocalDateTime dateVal, PlayerProperties type) { + this.userId = userId; + this.stringVal = stringVal; + this.booleanVal = booleanVal; + this.integerVal = integerVal; + this.dateVal = dateVal; + this.type = type; + } +} diff --git a/src/main/java/com/alterdekim/game/entity/Visa.java b/src/main/java/com/alterdekim/game/entity/Visa.java new file mode 100644 index 0000000..601c4af --- /dev/null +++ b/src/main/java/com/alterdekim/game/entity/Visa.java @@ -0,0 +1,41 @@ +package com.alterdekim.game.entity; + +import com.alterdekim.flash.decompiler.mapper.FlashClass; +import com.alterdekim.flash.decompiler.mapper.FlashField; +import com.alterdekim.flash.decompiler.mapper.FlashValue; +import com.alterdekim.flash.decompiler.mapper.FlashValueType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name="visas") +@FlashClass(name = "vi") +public class Visa { + @Id + @FlashValue(type = FlashValueType.Id) + private Long id; + + @Column(nullable = false) + @FlashField(name = "LocationId") + private Integer locationId; + + @Column(nullable = false) + @FlashField(name = "MaxUseCount") + private Integer maxUseCount; + + @Column(nullable = false) + @FlashField(name = "TRId") + private Integer textResourceId; + + @Column(nullable = false) + @FlashField(name = "MRId") + private Integer mediaResourceId; +} diff --git a/src/main/java/com/alterdekim/game/mapper/Integer2BooleanConverter.java b/src/main/java/com/alterdekim/game/mapper/Integer2BooleanConverter.java new file mode 100644 index 0000000..e22a927 --- /dev/null +++ b/src/main/java/com/alterdekim/game/mapper/Integer2BooleanConverter.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.mapper; + +import com.alterdekim.flash.decompiler.mapper.FlashFieldConverter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class Integer2BooleanConverter extends FlashFieldConverter { + @Override + public Object convert(Object o) { + return ((Integer) o) == 1; + } +} diff --git a/src/main/java/com/alterdekim/game/mapper/Integer2RoleFlagsConverter.java b/src/main/java/com/alterdekim/game/mapper/Integer2RoleFlagsConverter.java new file mode 100644 index 0000000..c3f114f --- /dev/null +++ b/src/main/java/com/alterdekim/game/mapper/Integer2RoleFlagsConverter.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.mapper; + +import com.alterdekim.flash.decompiler.mapper.FlashFieldConverter; +import com.alterdekim.game.component.game.RoleFlags; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class Integer2RoleFlagsConverter extends FlashFieldConverter { + @Override + public Object convert(Object o) { + return RoleFlags.fromId((Integer) o); + } +} diff --git a/src/main/java/com/alterdekim/game/mapper/String2CatalogTypeConverter.java b/src/main/java/com/alterdekim/game/mapper/String2CatalogTypeConverter.java new file mode 100644 index 0000000..471188a --- /dev/null +++ b/src/main/java/com/alterdekim/game/mapper/String2CatalogTypeConverter.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.mapper; + +import com.alterdekim.flash.decompiler.mapper.FlashFieldConverter; +import com.alterdekim.game.component.game.CatalogType; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class String2CatalogTypeConverter extends FlashFieldConverter { + @Override + public Object convert(Object o) { + return CatalogType.fromType((String) o); + } +} diff --git a/src/main/java/com/alterdekim/game/mapper/String2DateConverter.java b/src/main/java/com/alterdekim/game/mapper/String2DateConverter.java new file mode 100644 index 0000000..5aefab8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/mapper/String2DateConverter.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.mapper; + +import com.alterdekim.flash.decompiler.mapper.FlashFieldConverter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@NoArgsConstructor +public class String2DateConverter extends FlashFieldConverter { + + @Override + public Object convert(Object o) { + return LocalDateTime.parse((String) o); + } +} diff --git a/src/main/java/com/alterdekim/game/message/ByteMessage.java b/src/main/java/com/alterdekim/game/message/ByteMessage.java new file mode 100644 index 0000000..f6955e5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/ByteMessage.java @@ -0,0 +1,8 @@ +package com.alterdekim.game.message; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class ByteMessage { + private Byte value; +} diff --git a/src/main/java/com/alterdekim/game/message/CallMessage.java b/src/main/java/com/alterdekim/game/message/CallMessage.java new file mode 100644 index 0000000..1910f84 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/CallMessage.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.message; + +import com.alterdekim.game.message.amf.AMFSerializeAsStream; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class CallMessage { + private String functionName; + private Double num; + private Object none; + + @AMFSerializeAsStream + private Object val; +} diff --git a/src/main/java/com/alterdekim/game/message/ConnectMessage.java b/src/main/java/com/alterdekim/game/message/ConnectMessage.java new file mode 100644 index 0000000..2aa5f54 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/ConnectMessage.java @@ -0,0 +1,22 @@ +package com.alterdekim.game.message; + +import com.alterdekim.game.message.amf.AMFObject; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.Map; + +@ToString +@NoArgsConstructor +@Getter +@Setter +public class ConnectMessage { + private String command; + private Double transactionId; + private Map object; + private String ver; + private Double clientId; + private String guid; +} diff --git a/src/main/java/com/alterdekim/game/message/ConnectMessageBody.java b/src/main/java/com/alterdekim/game/message/ConnectMessageBody.java new file mode 100644 index 0000000..2f192e8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/ConnectMessageBody.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.message; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@Getter +@Setter +public class ConnectMessageBody { + private String app; + private String tcUrl; + private Double audioCodecs; + private Double capabilities; + private String swfUrl; + private Double videoFunction; + private String flashVer; + private Double videoCodecs; + private String pageUrl; + private Boolean fpad; +} diff --git a/src/main/java/com/alterdekim/game/message/SerializerUtils.java b/src/main/java/com/alterdekim/game/message/SerializerUtils.java new file mode 100644 index 0000000..52800f3 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/SerializerUtils.java @@ -0,0 +1,45 @@ +package com.alterdekim.game.message; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class SerializerUtils { + + public static List primitiveToBytes(byte[] bytes) { + List result = new ArrayList<>(); + for( byte b : bytes ) { + result.add(b); + } + return result; + } + + public static byte[] bytesToPrimitive(List bytes) { + byte[] b = new byte[bytes.size()]; + for( int i = 0; i < bytes.size(); i++ ) { + b[i] = bytes.get(i); + } + return b; + } + + public static String convertToString(T obj) { + return switch (obj.getClass().getSimpleName()) { + case "String" -> (String) obj; + case "Integer", "Double", "Short", "Float", "Long", "Boolean", "Byte" -> String.valueOf(obj); + default -> obj.toString(); + }; + } + + public static List getPrivateFields(Class theClass) { + List privateFields = new ArrayList(); + Field[] fields = theClass.getDeclaredFields(); + for(Field field : fields){ + if(Modifier.isPrivate(field.getModifiers())) { + field.setAccessible(true); + privateFields.add(field); + } + } + return privateFields; + } +} diff --git a/src/main/java/com/alterdekim/game/message/SetLocationMessage.java b/src/main/java/com/alterdekim/game/message/SetLocationMessage.java new file mode 100644 index 0000000..e4f9247 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/SetLocationMessage.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.message; + +import com.alterdekim.game.message.amf.AMFObject; +import lombok.*; + +import java.util.Map; + +@ToString +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class SetLocationMessage { + private Double transactionId; + private String location; + private Map coordinates; + private Double startState; +} diff --git a/src/main/java/com/alterdekim/game/message/SetPlayerAvatarPosition.java b/src/main/java/com/alterdekim/game/message/SetPlayerAvatarPosition.java new file mode 100644 index 0000000..d7ef054 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/SetPlayerAvatarPosition.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.message; + +import com.alterdekim.game.message.amf.AMFObject; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.Map; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class SetPlayerAvatarPosition { + private Double transactionId; + private Map coordinates; + private Double tweenerId; + private Boolean emitEvent; +} diff --git a/src/main/java/com/alterdekim/game/message/SetPlayerAvatarState.java b/src/main/java/com/alterdekim/game/message/SetPlayerAvatarState.java new file mode 100644 index 0000000..c92c49c --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/SetPlayerAvatarState.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.message; + +import lombok.*; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class SetPlayerAvatarState { + private Double transactionId; + private Double state; +} diff --git a/src/main/java/com/alterdekim/game/message/UserMessage.java b/src/main/java/com/alterdekim/game/message/UserMessage.java new file mode 100644 index 0000000..ad6b352 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/UserMessage.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.message; + +import com.alterdekim.game.message.amf.AMFObject; +import lombok.*; + +import java.util.List; +import java.util.Map; + +@ToString +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class UserMessage { + private Double transactionId; + private String commandType; + private String srv; + private List arguments; + private String uuid; +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFDeserializer.java b/src/main/java/com/alterdekim/game/message/amf/AMFDeserializer.java new file mode 100644 index 0000000..20568ae --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFDeserializer.java @@ -0,0 +1,264 @@ +package com.alterdekim.game.message.amf; + +import com.alterdekim.game.message.SerializerUtils; +import com.alterdekim.game.message.serializer.BinaryMessageSerializer; +import com.alterdekim.game.utils.ByteUtils; +import com.alterdekim.game.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.*; + +@Slf4j +public class AMFDeserializer { + public static List deserialize(List bytes) { + List objects = new ArrayList<>(); + DeserializerSession session = new DeserializerSession(); + for( int i = 0; i < bytes.size(); i++ ) { + switch (session.getState()) { + case VALUE_TYPE: + session.getCurrent().setType(AMFValueType.toValueType(bytes.get(i))); + session.setState(DeserializerState.VALUE_DATA); + i = parseLength(bytes, i, session); + break; + case VALUE_DATA: + if(session.getCurrent().getType() == AMFValueType.NULL) { + session.getCurrent().setValue(null); + session.setState(DeserializerState.VALUE_TYPE); + session.getData().clear(); + objects.add(session.getCurrent()); + session.setCurrent(new AMFObject()); + } + session.getData().add(bytes.get(i)); + if(session.getData().size() >= session.getValueLength()) { + session.getCurrent().setValue(deserializeSingleValue(session.getCurrent().getType(), session.getData())); + session.getData().clear(); + session.setState(DeserializerState.VALUE_TYPE); + objects.add(session.getCurrent()); + session.setCurrent(new AMFObject()); + } + break; + } + } + return objects; + } + + private static DeserializeSignleResult deserializeSingle( List bytes ) { + DeserializerSession session = new DeserializerSession(); + for( int i = 0; i < bytes.size(); i++ ) { + switch (session.getState()) { + case VALUE_TYPE: + session.getCurrent().setType(AMFValueType.toValueType(bytes.get(i))); + session.setState(DeserializerState.VALUE_DATA); + i = parseLength(bytes, i, session); + break; + case VALUE_DATA: + if(session.getCurrent().getType() == AMFValueType.NULL) { + session.getCurrent().setValue(null); + session.setState(DeserializerState.VALUE_TYPE); + session.getData().clear(); + return new DeserializeSignleResult(session.getCurrent(), i); + } + session.getData().add(bytes.get(i)); + if(session.getData().size() >= session.getValueLength()) { + session.getCurrent().setValue(deserializeSingleValue(session.getCurrent().getType(), session.getData())); + session.getData().clear(); + session.setState(DeserializerState.VALUE_TYPE); + return new DeserializeSignleResult(session.getCurrent(), i); + } + break; + } + } + return new DeserializeSignleResult(new AMFObject(), 0); + } + + private static int bytes2int(List bytes) { + int value = 0; + for (byte b : bytes) { + value = (value << 8) + (b & 0xFF); + } + return value; + } + + private static int parseArrayLength(List bytes, int i) { + for( int u = i; u < bytes.size(); u++ ) { + if( bytes.get(u) == 0x00 && + bytes.get(u+1) == 0x00 && + bytes.get(u+2) == 0x09 ) { + return u+2; + } + } + return 0; + } + + private static int parseObjectLength(List bytes, int i) { + List pos = new ArrayList<>(); + int ignore = 0; + for( int u = i; u < bytes.size()-2; u++ ) { + if( bytes.get(u) == 0x00 && + bytes.get(u+1) == 0x00 && + bytes.get(u+2) == 0x09 ) { + if( ignore <= 0 ) { + pos.add(u + 2); + u += 2; + continue; + } + ignore--; + } else if( u != i && + bytes.size() > u+3 && + bytes.get(u) == 0x03 && + bytes.get(u+3) >= 0x30 && + bytes.get(u+3) <= 0x7A ) { + if( BinaryMessageSerializer.deserializeLongUnsafe(bytes.subList(u+1, u+3)).intValue() <= (bytes.size() - u) ) { + ignore++; + } + } + } + return pos.get(0); + } + + private static int parseLength(List bytes, int i, DeserializerSession session) { + switch (session.getCurrent().getType()) { + case NUMBER: + session.setValueLength(8); + break; + case BOOLEAN: + session.setValueLength(1); + break; + case STRING: + List len = bytes.subList(i+1, i+3); + session.setValueLength(BinaryMessageSerializer.deserializeLongUnsafe(len).intValue()); + return i + 2; + case OBJECT: + session.setValueLength(parseObjectLength(bytes, i) - i); + break; + case NULL: + session.getCurrent().setValue(null); + session.getData().clear(); + session.setState(DeserializerState.VALUE_TYPE); + break; + case MIXED_ARRAY: + session.setValueLength(parseArrayLength(bytes, i) - i); + break; + } + return i; + } + + private static Object deserializeSingleValue(AMFValueType type, List bytes) { + return switch (type) { + case NUMBER -> Double.longBitsToDouble(BinaryMessageSerializer.deserializeLong(bytes)); + case BOOLEAN -> bytes.get(0) == 0x01; + case STRING -> new String(SerializerUtils.bytesToPrimitive(bytes)); + case OBJECT -> deserializeObject(bytes); + case MIXED_ARRAY -> deserializeArray(bytes); + default -> null; + }; + } + + private static List deserializeArray(List bytes) { + List arr = new ArrayList<>(); + int cnt = 1; + for( int i = 4; i < bytes.size(); i++ ) { + if(bytes.get(i) == 0x00 && bytes.get(i+1) == 0x00 && bytes.get(i+2) == 0x09) cnt--; + if( cnt <= 0 ) break; + i += 2; + DeserializeSignleResult r = deserializeSingle(bytes.subList(i, bytes.size())); + arr.add(r.getObject()); + i += r.getI(); + } + return arr; + } + + private static Map deserializeObject(List bytes) { + Map map = new HashMap<>(); + ObjectDeserializerState state = ObjectDeserializerState.KEY; + String key = ""; + for( int i = 0; i < bytes.size(); i++ ) { + if(bytes.get(i) == 0x00 && bytes.get(i+1) == 0x00 && bytes.get(i+2) == 0x09) break; + switch (state) { + case KEY: + if(i+2 > bytes.size()) return map; + List key_len_bytes = bytes.subList(i, i+2); + int keyLen = BinaryMessageSerializer.deserializeLongUnsafe(key_len_bytes).intValue(); + if( i+2+keyLen >= bytes.size() ) return map; + key = new String(SerializerUtils.bytesToPrimitive(bytes.subList(i+2, i+2+keyLen))); + state = ObjectDeserializerState.VALUE; + i += 1+keyLen; + break; + case VALUE: + DeserializeSignleResult r = deserializeSingle(bytes.subList(i, bytes.size())); + map.put(key, r.getObject()); + i += r.getI(); + state = ObjectDeserializerState.KEY; + break; + } + } + return map; + } + + public static T deserialize(List objects, Class valueType) throws Exception { + List fields = SerializerUtils.getPrivateFields(valueType); + T obj = valueType.getDeclaredConstructor().newInstance(); + for( Field field : fields ) { + AMFObject object = objects.stream() + .filter(o -> checkTypeEquality(o.getType(), field.getType().getCanonicalName())) + .findFirst() + .orElse(null); + if( object == null ) continue; + field.set(obj, object.getValue()); + objects.remove(object); + } + return obj; + } + + private static boolean checkTypeEquality(AMFValueType type, String typeName) { + return switch(type) { + case NUMBER -> typeName.equals("java.lang.Double"); + case BOOLEAN -> typeName.equals("java.lang.Boolean"); + case STRING -> typeName.equals("java.lang.String"); + case OBJECT -> typeName.equals("java.util.Map"); + case MIXED_ARRAY -> typeName.equals("java.util.List"); + default -> false; + }; + } + + private enum ObjectDeserializerState { + KEY, + VALUE + } + + @Getter + @Setter + private static class DeserializerSession { + private DeserializerState state; + private AMFObject current; + private List data; + private int valueLength; + + private DeserializerSession() { + state = DeserializerState.VALUE_TYPE; + current = new AMFObject(); + data = new ArrayList<>(); + valueLength = 0; + } + } + + private enum DeserializerState { + VALUE_TYPE, + VALUE_DATA + } + + @AllArgsConstructor + @NoArgsConstructor + @Getter + @Setter + private static class DeserializeSignleResult { + private AMFObject object; + private int i; + } +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFKey.java b/src/main/java/com/alterdekim/game/message/amf/AMFKey.java new file mode 100644 index 0000000..3606131 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFKey.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.message.amf; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface AMFKey { + String name() default ""; +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFObject.java b/src/main/java/com/alterdekim/game/message/amf/AMFObject.java new file mode 100644 index 0000000..2666d5d --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFObject.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.message.amf; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class AMFObject { + private AMFValueType type; + private Object value; +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFSerializeAsStream.java b/src/main/java/com/alterdekim/game/message/amf/AMFSerializeAsStream.java new file mode 100644 index 0000000..d491ee7 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFSerializeAsStream.java @@ -0,0 +1,11 @@ +package com.alterdekim.game.message.amf; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface AMFSerializeAsStream { +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFSerializer.java b/src/main/java/com/alterdekim/game/message/amf/AMFSerializer.java new file mode 100644 index 0000000..1b3f391 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFSerializer.java @@ -0,0 +1,142 @@ +package com.alterdekim.game.message.amf; + +import com.alterdekim.game.message.SerializerUtils; +import com.alterdekim.game.utils.ByteUtils; +import lombok.extern.slf4j.Slf4j; +import org.javatuples.Pair; + +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.alterdekim.game.message.SerializerUtils.convertToString; + +@Slf4j +public class AMFSerializer { + + public static List serialize( T obj ) { + return serialize(obj ,true); + } + + public static List serialize( T obj, boolean isStream ) { + if( obj == null ) return new ArrayList<>(Collections.singletonList((byte) 0x05)); + return switch (obj.getClass().getSimpleName()) { + case "String" -> serializeString((String) obj); + case "Integer", "Double", "Float", "Long", "Short", "Byte" -> serializeNumber(((java.lang.Number) obj).doubleValue()); + case "Boolean" -> serializeBoolean((Boolean) obj); + case "Map", "HashMap" -> serializeMap((Map) obj); + case "List", "ArrayList" -> serializeArray((List) obj); + default -> isStream ? serializeJavaObjectAsStream(obj) : serializeJavaObjectAsKeyVal(obj); + }; + } + + // Serializes java object to sequence of serialized fields. + private static List serializeJavaObjectAsStream( T obj ) { + return SerializerUtils.getPrivateFields(obj.getClass()) + .stream() + .map(f -> { + try { + return serialize(f.get(obj), f.isAnnotationPresent(AMFSerializeAsStream.class)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + // Serialize java object to ActionScript Object (HashMap) + private static List serializeJavaObjectAsKeyVal( T obj ) { + List d = new ArrayList<>(Collections.singletonList((byte) 0x03)); + d.addAll( + SerializerUtils.getPrivateFields(obj.getClass()) + .stream() + .filter(f -> { + try { + return f.get(obj) != null; + } catch (IllegalAccessException e) { + return false; + } + }) + .map(f -> { + String name = f.isAnnotationPresent(AMFKey.class) ? f.getAnnotation(AMFKey.class).name() : f.getName(); + var l = serializeString(name).subList(1, name.length()+3); + try { + l.addAll(serialize(f.get(obj), f.isAnnotationPresent(AMFSerializeAsStream.class))); + return l; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ); + d.addAll(List.of((byte) 0x00, (byte) 0x00, (byte) 0x09)); + return d; + } + + private static List int2bytes(int val) { + int len = 4; + List r = new ArrayList<>(); + for( int i = 0; i < len; i++ ) { + r.add( (byte) ( (val >> 8*(len-i-1)) & 0xFF ) ); + } + return r; + } + + private static List serializeArray(List list) { + List d = new ArrayList<>(Collections.singletonList((byte) 0x08)); + d.addAll(int2bytes(list.size())); + d.addAll( + IntStream.range(0, list.size()) + .boxed() + .map( i -> Pair.with(String.valueOf(i), list.get(i))) + .map( o -> { + var l = serializeString(o.getValue0()).subList(1, o.getValue0().length()+3); + l.addAll(serialize(o.getValue1(), false)); + return l; + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ); + d.addAll(List.of((byte) 0x00, (byte) 0x00, (byte) 0x09)); + return d; + } + + private static List serializeMap(Map map) { + List d = new ArrayList<>(Collections.singletonList((byte) 0x03)); + d.addAll( + map.keySet() + .stream() + .map(k -> { + String key = convertToString(k); + var l = serializeString(key).subList(1, key.length()+3); + l.addAll(serialize(map.get(k), false)); + return l; + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ); + d.addAll(List.of((byte) 0x00, (byte) 0x00, (byte) 0x09)); + return d; + } + + private static List serializeString(String s) { + List d = new ArrayList<>(Collections.singletonList((byte) 0x02)); + d.addAll(SerializerUtils.primitiveToBytes(ByteBuffer.allocate(2).putShort((short) s.getBytes().length).array())); + d.addAll(SerializerUtils.primitiveToBytes(s.getBytes())); + return d; + } + + private static List serializeBoolean(Boolean b) { + return new ArrayList<>(List.of((byte) 0x01, b ? (byte) 0x01 : (byte) 0x00)); + } + + private static List serializeNumber(Double d) { + List l = new ArrayList<>(Collections.singletonList((byte)0x00)); + l.addAll(SerializerUtils.primitiveToBytes(ByteUtils.longToBytes(Double.doubleToLongBits(d)))); + return l; + } +} diff --git a/src/main/java/com/alterdekim/game/message/amf/AMFValueType.java b/src/main/java/com/alterdekim/game/message/amf/AMFValueType.java new file mode 100644 index 0000000..2e9d470 --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/amf/AMFValueType.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.message.amf; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public enum AMFValueType { + NUMBER, + BOOLEAN, + STRING, + OBJECT, + NULL, + MIXED_ARRAY, + DATE; + + + public static AMFValueType toValueType(byte b) { + return switch (b) { + case 0x00 -> NUMBER; + case 0x01 -> BOOLEAN; + case 0x02 -> STRING; + case 0x03 -> OBJECT; + case 0x08 -> MIXED_ARRAY; + case 0x0b -> DATE; + default -> NULL; + }; + } +} diff --git a/src/main/java/com/alterdekim/game/message/serializer/BinaryMessageSerializer.java b/src/main/java/com/alterdekim/game/message/serializer/BinaryMessageSerializer.java new file mode 100644 index 0000000..ad2aa6c --- /dev/null +++ b/src/main/java/com/alterdekim/game/message/serializer/BinaryMessageSerializer.java @@ -0,0 +1,133 @@ +package com.alterdekim.game.message.serializer; + +import com.alterdekim.game.message.SerializerUtils; +import com.alterdekim.game.utils.ByteUtils; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class BinaryMessageSerializer { + public static List serialize(Object message) { + List fields = SerializerUtils.getPrivateFields(message.getClass()); + List result = new ArrayList<>(); + fields.forEach(f -> { + String type_name = ((Class) f.getType()).getName(); + try { + switch (type_name) { + case "java.lang.String": + result.addAll(SerializerUtils.primitiveToBytes(((String) f.get(message)).getBytes())); + break; + case "java.lang.Integer": + result.addAll(serializeInteger((Integer) f.get(message))); + break; + case "java.lang.Boolean": + if( ((Boolean) f.get(message)).booleanValue() ) { result.add((byte) 1); } else { result.add((byte) 0); } + break; + case "java.lang.Long": + result.addAll(serializeLong((Long) f.get(message))); + break; + case "java.lang.Double": + result.addAll(serializeDouble((Double) f.get(message))); + break; + case "java.lang.Float": + result.addAll(serializeFloat((Float) f.get(message))); + break; + case "java.lang.Byte": + result.add((Byte) f.get(message)); + break; + case "int": + result.addAll(serializeInteger(f.getInt(message))); + break; + case "boolean": + if( f.getBoolean(message) ) { result.add((byte) 1); } else { result.add((byte) 0); } + break; + case "long": + result.addAll(serializeLong(f.getLong(message))); + break; + case "double": + result.addAll(serializeDouble(f.getDouble(message))); + break; + case "float": + result.addAll(serializeFloat(f.getFloat(message))); + break; + case "byte": + result.add(f.getByte(message)); + break; + default: + result.addAll(BinaryMessageSerializer.serialize(f.get(message))); + break; + } + } catch (IllegalAccessException e) { + log.error(e.getMessage()); + } + }); + return result; + } + + public static Integer deserializeInteger(List bytes) { + assert bytes.size() == 4; + int value = 0; + for (int i = 0; i < bytes.size(); i++) { + value = (value << 8) + (bytes.get(i) & 0xFF); + } + return value; + } + + public static Integer deserializeInteger(byte[] bytes) { + assert bytes.length == 4; + int value = 0; + for (int i = 0; i < bytes.length; i++) { + value = (value << 8) + (bytes[i] & 0xFF); + } + return value; + } + + public static Long deserializeLong(List bytes) { + assert bytes.size() == 8; + long value = 0; + for (int i = 0; i < bytes.size(); i++) { + value = (value << 8) + (bytes.get(i) & 0xFF); + } + return value; + } + + public static Long deserializeLongUnsafe(List bytes) { + long value = 0; + for (int i = 0; i < bytes.size(); i++) { + value = (value << 8) + (bytes.get(i) & 0xFF); + } + return value; + } + + public static Long deserializeLong(byte[] bytes) { + assert bytes.length == 8; + long value = 0; + for (int i = 0; i < bytes.length; i++) { + value = (value << 8) + (bytes[i] & 0xFF); + } + return value; + } + + private static List serializeInteger(Integer i) { + return serializeNumber(i.doubleValue()); + } + + private static List serializeLong(Long l) { + return serializeNumber(l.doubleValue()); + } + + private static List serializeDouble(Double d) { + return serializeNumber(d); + } + + private static List serializeFloat(Float f) { + return serializeNumber(f.doubleValue()); + } + + private static List serializeNumber(Double d) { + return SerializerUtils.primitiveToBytes(ByteUtils.longToBytes(Double.doubleToLongBits(d))); + } +} diff --git a/src/main/java/com/alterdekim/game/repository/AchievementRepository.java b/src/main/java/com/alterdekim/game/repository/AchievementRepository.java new file mode 100644 index 0000000..cf81054 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/AchievementRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Achievement; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface AchievementRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/AvatarInventoryRepository.java b/src/main/java/com/alterdekim/game/repository/AvatarInventoryRepository.java new file mode 100644 index 0000000..6a42814 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/AvatarInventoryRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.component.game.AvatarInventoryType; +import com.alterdekim.game.entity.AvatarInventory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AvatarInventoryRepository extends JpaRepository { + + @Query(value = "SELECT a FROM AvatarInventory a WHERE a.userId = :uid AND a.type = :type") + List findItemsByUserIdAndType(@Param("uid") Long userId, @Param("type") AvatarInventoryType type); + + @Query(value = "SELECT a FROM AvatarInventory a WHERE a.userId = :uid AND a.type = :type AND a.isUsed = true") + List findUsedItemsByUserIdAndType(@Param("uid") Long userId, @Param("type") AvatarInventoryType type); +} diff --git a/src/main/java/com/alterdekim/game/repository/BackgroundRepository.java b/src/main/java/com/alterdekim/game/repository/BackgroundRepository.java new file mode 100644 index 0000000..e6fd0f2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/BackgroundRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Background; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BackgroundRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/BodyPartRepository.java b/src/main/java/com/alterdekim/game/repository/BodyPartRepository.java new file mode 100644 index 0000000..11bf449 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/BodyPartRepository.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.component.game.BodyPartType; +import com.alterdekim.game.entity.BodyPart; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface BodyPartRepository extends JpaRepository { + Optional findById(Long id); + + @Query(value = "SELECT b FROM BodyPart b WHERE b.type = :type") + List findByBodyPartType(@Param("type") BodyPartType type); +} diff --git a/src/main/java/com/alterdekim/game/repository/CatalogObjectRepository.java b/src/main/java/com/alterdekim/game/repository/CatalogObjectRepository.java new file mode 100644 index 0000000..92d5dd9 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/CatalogObjectRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.CatalogObject; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CatalogObjectRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/CatalogRepository.java b/src/main/java/com/alterdekim/game/repository/CatalogRepository.java new file mode 100644 index 0000000..e38965e --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/CatalogRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Catalog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface CatalogRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/GAAccountRepository.java b/src/main/java/com/alterdekim/game/repository/GAAccountRepository.java new file mode 100644 index 0000000..ddeff45 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GAAccountRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GAAccount; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface GAAccountRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/GameIconRepository.java b/src/main/java/com/alterdekim/game/repository/GameIconRepository.java new file mode 100644 index 0000000..ffeceec --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GameIconRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GameIcon; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface GameIconRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/GoodClubDancefloorRepository.java b/src/main/java/com/alterdekim/game/repository/GoodClubDancefloorRepository.java new file mode 100644 index 0000000..d4228bc --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GoodClubDancefloorRepository.java @@ -0,0 +1,17 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GoodClubDancefloor; +import com.alterdekim.game.entity.GoodSmile; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GoodClubDancefloorRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} + diff --git a/src/main/java/com/alterdekim/game/repository/GoodMagicRepository.java b/src/main/java/com/alterdekim/game/repository/GoodMagicRepository.java new file mode 100644 index 0000000..c28d9c8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GoodMagicRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GoodMagic; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GoodMagicRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/GoodRepository.java b/src/main/java/com/alterdekim/game/repository/GoodRepository.java new file mode 100644 index 0000000..5736a4f --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GoodRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Good; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GoodRepository extends JpaRepository { + Optional findById(Long id); + + @Query(value = "SELECT g FROM Good g WHERE g.goodTypeId = :goodTypeId") + List findByGoodTypeId(@Param("goodTypeId") Integer goodTypeId); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/GoodSmileRepository.java b/src/main/java/com/alterdekim/game/repository/GoodSmileRepository.java new file mode 100644 index 0000000..a15bc77 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GoodSmileRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GoodSmile; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GoodSmileRepository extends JpaRepository { + Optional findById(Integer id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/GoodTagRepository.java b/src/main/java/com/alterdekim/game/repository/GoodTagRepository.java new file mode 100644 index 0000000..6c2a91b --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/GoodTagRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.GoodTag; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GoodTagRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/IncompatibleRepository.java b/src/main/java/com/alterdekim/game/repository/IncompatibleRepository.java new file mode 100644 index 0000000..1c04d18 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/IncompatibleRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Incompatible; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface IncompatibleRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/LevelExperienceRepository.java b/src/main/java/com/alterdekim/game/repository/LevelExperienceRepository.java new file mode 100644 index 0000000..f2e5398 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/LevelExperienceRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.LevelExperience; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface LevelExperienceRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/LibraryRepository.java b/src/main/java/com/alterdekim/game/repository/LibraryRepository.java new file mode 100644 index 0000000..2f12cdb --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/LibraryRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Library; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface LibraryRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/LocationRepository.java b/src/main/java/com/alterdekim/game/repository/LocationRepository.java new file mode 100644 index 0000000..98d6c3d --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/LocationRepository.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Location; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface LocationRepository extends JpaRepository { + + @Query(value = "SELECT l FROM Location l WHERE l.locationId = :uid AND l.isEnabled = true") + Optional findLocationById(@Param("uid") Long locationId); + + @Query(value = "SELECT l FROM Location l WHERE l.isEnabled = true AND l.isDefault = true") + Optional findDefaultLocation(); +} diff --git a/src/main/java/com/alterdekim/game/repository/MRTRepository.java b/src/main/java/com/alterdekim/game/repository/MRTRepository.java new file mode 100644 index 0000000..dd6a8f2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MRTRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.MediaResourcePath; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface MRTRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/MagicAbilityRepository.java b/src/main/java/com/alterdekim/game/repository/MagicAbilityRepository.java new file mode 100644 index 0000000..b72b935 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MagicAbilityRepository.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.MagicAbility; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface MagicAbilityRepository extends JpaRepository { + + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/MediaResourceRepository.java b/src/main/java/com/alterdekim/game/repository/MediaResourceRepository.java new file mode 100644 index 0000000..c82eaf4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MediaResourceRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.MediaResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface MediaResourceRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/MiniquestRepository.java b/src/main/java/com/alterdekim/game/repository/MiniquestRepository.java new file mode 100644 index 0000000..85a9737 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MiniquestRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Miniquest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface MiniquestRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/MissionRepository.java b/src/main/java/com/alterdekim/game/repository/MissionRepository.java new file mode 100644 index 0000000..972871a --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/MissionRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Mission; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MissionRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/PhoneIconRepository.java b/src/main/java/com/alterdekim/game/repository/PhoneIconRepository.java new file mode 100644 index 0000000..03c3be3 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PhoneIconRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.PhoneIcon; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PhoneIconRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/PhoneMessageRepository.java b/src/main/java/com/alterdekim/game/repository/PhoneMessageRepository.java new file mode 100644 index 0000000..718c3e7 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PhoneMessageRepository.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Miniquest; +import com.alterdekim.game.entity.PhoneMessage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PhoneMessageRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/PostcardRepository.java b/src/main/java/com/alterdekim/game/repository/PostcardRepository.java new file mode 100644 index 0000000..1856868 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PostcardRepository.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.PhoneMessage; +import com.alterdekim.game.entity.Postcard; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PostcardRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/PreloaderRepository.java b/src/main/java/com/alterdekim/game/repository/PreloaderRepository.java new file mode 100644 index 0000000..455700a --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PreloaderRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Postcard; +import com.alterdekim.game.entity.Preloader; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PreloaderRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); + + @Query("SELECT p FROM Preloader p WHERE p.isSelected = true") + List findSelected(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/PriceExchangeRepository.java b/src/main/java/com/alterdekim/game/repository/PriceExchangeRepository.java new file mode 100644 index 0000000..51c629e --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PriceExchangeRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.PriceExchange; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PriceExchangeRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/PriceLicenseRepository.java b/src/main/java/com/alterdekim/game/repository/PriceLicenseRepository.java new file mode 100644 index 0000000..5eed080 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PriceLicenseRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.PriceLicense; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PriceLicenseRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/PromotionBannerRepository.java b/src/main/java/com/alterdekim/game/repository/PromotionBannerRepository.java new file mode 100644 index 0000000..ae93ff6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PromotionBannerRepository.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.PromotionBanner; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PromotionBannerRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); + + @Query(value = "SELECT p FROM PromotionBanner p WHERE p.isSelected = true") + List findSelected(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/PromotionRepository.java b/src/main/java/com/alterdekim/game/repository/PromotionRepository.java new file mode 100644 index 0000000..c82dcca --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/PromotionRepository.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Preloader; +import com.alterdekim.game.entity.Promotion; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PromotionRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); + + @Query("SELECT p FROM Promotion p WHERE p.isSelected = true") + List findSelected(); +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/repository/ResourceObjectRepository.java b/src/main/java/com/alterdekim/game/repository/ResourceObjectRepository.java new file mode 100644 index 0000000..3119a65 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/ResourceObjectRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.ResourceObject; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface ResourceObjectRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/RoleRepository.java b/src/main/java/com/alterdekim/game/repository/RoleRepository.java new file mode 100644 index 0000000..eacb715 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/RoleRepository.java @@ -0,0 +1,13 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface RoleRepository extends JpaRepository { + Role findByName(String name); + + List findAll(); +} + diff --git a/src/main/java/com/alterdekim/game/repository/SmileRepository.java b/src/main/java/com/alterdekim/game/repository/SmileRepository.java new file mode 100644 index 0000000..4bd5523 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/SmileRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Smile; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SmileRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/SportInventoryRepository.java b/src/main/java/com/alterdekim/game/repository/SportInventoryRepository.java new file mode 100644 index 0000000..0bd1a3b --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/SportInventoryRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.SportInventory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SportInventoryRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/repository/TextResourceRepository.java b/src/main/java/com/alterdekim/game/repository/TextResourceRepository.java new file mode 100644 index 0000000..3366ef6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/TextResourceRepository.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.TextResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface TextResourceRepository extends JpaRepository { + Optional findById(Long id); + + List findAll(); +} diff --git a/src/main/java/com/alterdekim/game/repository/UserPropertyRepository.java b/src/main/java/com/alterdekim/game/repository/UserPropertyRepository.java new file mode 100644 index 0000000..7e26f98 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/UserPropertyRepository.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.component.game.PlayerProperties; +import com.alterdekim.game.entity.UserProperty; +import jakarta.transaction.Transactional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserPropertyRepository extends JpaRepository { + Optional findById(Long id); + + @Query(value = "SELECT u FROM UserProperty u WHERE u.userId = :uid AND u.type = :type") + Optional findByUserIdAndTag(@Param("uid") Long userId, @Param("type") PlayerProperties property); + + List findAll(); + + @Transactional + @Modifying + @Query(value = "DELETE FROM UserProperty u WHERE u.userId = :uid AND u.type = :type") + void deleteByUserIdAndType(@Param("uid") Long userId, @Param("type") PlayerProperties properties); +} diff --git a/src/main/java/com/alterdekim/game/repository/UserRepository.java b/src/main/java/com/alterdekim/game/repository/UserRepository.java new file mode 100644 index 0000000..725e8b6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/UserRepository.java @@ -0,0 +1,11 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + User findByUsername(String username); +} + diff --git a/src/main/java/com/alterdekim/game/repository/VisaRepository.java b/src/main/java/com/alterdekim/game/repository/VisaRepository.java new file mode 100644 index 0000000..14c93ad --- /dev/null +++ b/src/main/java/com/alterdekim/game/repository/VisaRepository.java @@ -0,0 +1,9 @@ +package com.alterdekim.game.repository; + +import com.alterdekim.game.entity.Visa; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface VisaRepository extends JpaRepository { +} diff --git a/src/main/java/com/alterdekim/game/security/AuthenticationUtil.java b/src/main/java/com/alterdekim/game/security/AuthenticationUtil.java new file mode 100644 index 0000000..2023735 --- /dev/null +++ b/src/main/java/com/alterdekim/game/security/AuthenticationUtil.java @@ -0,0 +1,41 @@ +package com.alterdekim.game.security; + +import com.alterdekim.game.entity.User; +import com.alterdekim.game.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCrypt; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.util.Optional; + +@Slf4j +public class AuthenticationUtil { + public static Optional authProfile(UserService userService) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if( !authentication.isAuthenticated() ) return Optional.empty(); + Object o = authentication.getPrincipal(); + if( o.getClass().getSimpleName().equals("String") ) { + return Optional.empty(); + } + return Optional.of(userService.findByUsername(((org.springframework.security.core.userdetails.User) o).getUsername())); + } + + public static Boolean hwIdAuth(User user, String token) { + return new BCryptPasswordEncoder().matches(user.getId()+user.getPassword()+user.getUsername(), token); + } + + public static String getToken(User user) { + return new BCryptPasswordEncoder().encode(user.getId()+user.getPassword()+user.getUsername()); + } + + public static boolean checkCredentials(String username, String password) { + return username.length() >= 3 && + username.length() <= 25 && + !username.matches("[^a-zA-Z0-9а-яА-Я-_]") && + password.length() >= 5 && + password.length() <= 128 && + password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@#$%^&+=]).{4,128}$"); + } +} diff --git a/src/main/java/com/alterdekim/game/security/CustomAuthenticationFailureHandler.java b/src/main/java/com/alterdekim/game/security/CustomAuthenticationFailureHandler.java new file mode 100644 index 0000000..f9b6d4c --- /dev/null +++ b/src/main/java/com/alterdekim/game/security/CustomAuthenticationFailureHandler.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.security; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import java.io.IOException; + +public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + response.sendRedirect("/"); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/security/SpringSecurity.java b/src/main/java/com/alterdekim/game/security/SpringSecurity.java new file mode 100644 index 0000000..e098da1 --- /dev/null +++ b/src/main/java/com/alterdekim/game/security/SpringSecurity.java @@ -0,0 +1,98 @@ +package com.alterdekim.game.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.firewall.StrictHttpFirewall; +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +public class SpringSecurity { + + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + HttpSessionRequestCache requestCache = new HttpSessionRequestCache(); + requestCache.setMatchingRequestParameterName(null); + http.csrf().disable() + .authorizeHttpRequests((authorize) -> + authorize + .requestMatchers("/").permitAll() + .requestMatchers("/login").permitAll() + .requestMatchers("/main").hasAnyAuthority("ROLE_USER") + .requestMatchers("/_js/**").permitAll() + .requestMatchers("/img/**").permitAll() + .requestMatchers("/png/**").hasAnyAuthority("ROLE_USER") + .requestMatchers("/swf/**").hasAnyAuthority("ROLE_USER") + .requestMatchers("/avatar.swf").hasAnyAuthority("ROLE_USER") + .requestMatchers("/pub/**").permitAll() + .requestMatchers("/css/**").permitAll() + .requestMatchers("/ConstructorACHandler.ashx").permitAll() + .requestMatchers("/crossdomain.xml").permitAll() + .requestMatchers("/async/**").hasAnyAuthority("ROLE_USER") + .requestMatchers("/signup").permitAll() + .requestMatchers("/cache/**").hasAnyAuthority("ROLE_USER") + .requestMatchers("/signup/**").permitAll() + ) + .formLogin( + form -> form + .loginPage("/login") + .loginProcessingUrl("/login") + .failureForwardUrl("/") + .defaultSuccessUrl("/login") + .permitAll() + ) + .logout( + logout -> logout + .logoutUrl("/logout") + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .permitAll() + ) + .requestCache((cache) -> cache + .requestCache(requestCache) + ); + return http.build(); + } + + /*@Bean + public AccessDeniedHandler accessDeniedHandler() { + return new CustomAccessDeniedHandler(); + }*/ + + @Bean + public AuthenticationFailureHandler authenticationFailureHandler() { + return new CustomAuthenticationFailureHandler(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } + + + @Bean + public static PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + firewall.setUnsafeAllowAnyHttpMethod(true); + return (web) -> web.httpFirewall(firewall); + } +} diff --git a/src/main/java/com/alterdekim/game/service/AchievementService.java b/src/main/java/com/alterdekim/game/service/AchievementService.java new file mode 100644 index 0000000..de41acb --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/AchievementService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Achievement; +import com.alterdekim.game.repository.AchievementRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AchievementService implements OutputObjectCallback { + + @Autowired + private AchievementRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Achievement) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java b/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java new file mode 100644 index 0000000..c42171e --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/AvatarInventoryService.java @@ -0,0 +1,177 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.component.game.*; +import com.alterdekim.game.controller.result.signup.AvatarBodyPart; +import com.alterdekim.game.controller.result.signup.AvatarBodyPartGroup; +import com.alterdekim.game.entity.AvatarInventory; +import com.alterdekim.game.entity.BodyPart; +import com.alterdekim.game.entity.Good; +import com.alterdekim.game.repository.AvatarInventoryRepository; +import com.alterdekim.game.repository.BodyPartRepository; +import com.alterdekim.game.repository.GoodMagicRepository; +import com.alterdekim.game.repository.GoodRepository; +import com.alterdekim.game.utils.GameUtils; +import org.javatuples.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +public class AvatarInventoryService { + + @Autowired + private AvatarInventoryRepository inventoryRepository; + + @Autowired + private GoodRepository goodRepository; + + @Autowired + private BodyPartRepository bodyPartRepository; + + @Autowired + private GoodMagicRepository goodMagicRepository; + + @Autowired + private ResourceService resourceService; + + public List getAvatarInventoryItemsByUserId(Long userId) { + return this.inventoryRepository.findItemsByUserIdAndType(userId, AvatarInventoryType.Clothes) + .stream() + .map(AvatarInventory::getGoodId) + .map(id -> goodRepository.findById(id)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(g -> new AvatarInventoryItem(g.getId(), g.getTRId(), null, g.getMRId(), false, false, 0, false, g.getPublishDate(), 0, false)) + .collect(Collectors.toList()); + } + + public List getAvatarItemsByUserId(Long userId) { + List items = new ArrayList<>(); + items.addAll( + this.inventoryRepository.findUsedItemsByUserIdAndType(userId, AvatarInventoryType.BodyParts) + .stream() + .map(i -> Pair.with(i.getColor(), bodyPartRepository.findById(i.getGoodId()))) + .filter(i -> i.getValue1().isPresent()) + .map(i -> Pair.with(i.getValue0(), i.getValue1().get())) + .map(i -> new BodyAvatarItem(i.getValue1().getType().getValue(), i.getValue1().getId(), i.getValue0(), true, i.getValue1().getId().intValue(), 0l, i.getValue1().getMediaResourceId(), GameUtils.getLayerIdByBodyPartType(i.getValue1().getType()), null)) + .collect(Collectors.toList()) + ); + items.addAll( + this.inventoryRepository.findUsedItemsByUserIdAndType(userId, AvatarInventoryType.Clothes) + .stream() + .map(AvatarInventory::getGoodId) + .map(id -> goodRepository.findById(id)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(g -> new BodyAvatarItem(null, g.getId(), null, false, 0, g.getId(), g.getMRId(), g.getLayerId(), g.getGoodTypeId())) + .collect(Collectors.toList()) + ); + return items; + } + + public List getMagicAbilitiesItemsByUserId(Long userId) { + return this.inventoryRepository.findItemsByUserIdAndType(userId, AvatarInventoryType.Magic) + .stream() + .map(AvatarInventory::getGoodId) + .map(id -> goodMagicRepository.findById(id)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(g -> new InitMagicAbility(g.getMagicAbilityId(), "", false)) // todo: implement expiration ability + .collect(Collectors.toList()); + } + + public void saveBodyPart(BodyPart bodyPart) { + this.bodyPartRepository.save(bodyPart); + } + + private List getAvatarBodyPartsByType(BodyPartType type) { + return this.bodyPartRepository.findByBodyPartType(type) + .stream() + .filter(b -> resourceService.getUrlByMediaResourceId(b.getMediaResourceId().longValue()).isPresent()) + .map(b -> new AvatarBodyPart( + b.getId().intValue(), + 0, + false, + resourceService.getUrlByMediaResourceId(b.getMediaResourceId().longValue()).get() + ) + ) + .collect(Collectors.toList()); + } + + private List getAvatarBodyPartGroups(boolean isShape) { + List groups = new ArrayList<>(); + for( BodyPartType type : BodyPartType.values() ) { + if( type.isShape() != isShape ) continue; + groups.add( + new AvatarBodyPartGroup( + type.getValue(), + type.name(), + resourceService.getUrlByMediaResourceId(type.getMediaResourceId()).get(), + type.getLayerId(), + isShape ? this.getAvatarBodyPartsByType(type) : Stream.of( + this.getAvatarBodyPartsByType(type), + Collections.singletonList( + new AvatarBodyPart(98, 0, true, this.resourceService.getUrlByMediaResourceId(98L).get()) + ) + ) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ) + ); + } + return groups; + } + + public List getShapeGroups() { + return this.getAvatarBodyPartGroups(true); + } + + public List getBodyPartGroups() { + return this.getAvatarBodyPartGroups(false); + } + + private List getRandomStuffByGoodType(GoodClothType type) { + List g = this.goodRepository.findByGoodTypeId(type.getVal()); + int l = (int) Math.round(Math.random() * (g.size() - 4)); + return g.subList(l, l+3) + .stream() + .map(i -> new AvatarBodyPart( + i.getId().intValue(), + 0, + false, + resourceService.getUrlByMediaResourceId(i.getMRId().longValue()).get() + ) + ) + .collect(Collectors.toList()); + } + + public List getAvatarInventoryGroups() { + List groups = new ArrayList<>(); + for( GoodClothType type : GoodClothType.values() ) { + groups.add( + new AvatarBodyPartGroup( + type.getVal(), + type.name(), + resourceService.getUrlByMediaResourceId(type.getTabMediaResourceId()).get(), + type.getLayerId(), + Stream.of( + this.getRandomStuffByGoodType(type), + Collections.singletonList( + new AvatarBodyPart(98, 0, true, this.resourceService.getUrlByMediaResourceId(98L).get()) + ) + ) + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ) + ); + } + return groups; + } + + public void addGoodToInventory(AvatarInventory avatarInventory) { + this.inventoryRepository.save(avatarInventory); + } +} diff --git a/src/main/java/com/alterdekim/game/service/BackgroundService.java b/src/main/java/com/alterdekim/game/service/BackgroundService.java new file mode 100644 index 0000000..5649651 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/BackgroundService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Background; +import com.alterdekim.game.repository.BackgroundRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BackgroundService implements OutputObjectCallback { + + @Autowired + private BackgroundRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Background) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/CatalogService.java b/src/main/java/com/alterdekim/game/service/CatalogService.java new file mode 100644 index 0000000..1b0f314 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/CatalogService.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Catalog; +import com.alterdekim.game.entity.CatalogObject; +import com.alterdekim.game.repository.CatalogObjectRepository; +import com.alterdekim.game.repository.CatalogRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@NoArgsConstructor +@Service +public class CatalogService implements OutputObjectCallback { + + @Autowired + private CatalogRepository repository; + + @Autowired + private CatalogObjectRepository objectRepository; + + @Override + public void onOutputObjectReady(Object o) { + if( o.getClass().equals(Catalog.class) ) { + this.repository.save((Catalog) o); + return; + } + this.objectRepository.save((CatalogObject) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/CustomUserDetailsService.java b/src/main/java/com/alterdekim/game/service/CustomUserDetailsService.java new file mode 100644 index 0000000..4e232d2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/CustomUserDetailsService.java @@ -0,0 +1,43 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entity.Role; +import com.alterdekim.game.entity.User; +import com.alterdekim.game.repository.UserRepository; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.stream.Collectors; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + public CustomUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + User user = userRepository.findByUsername(email); + + if (user != null) { + return new org.springframework.security.core.userdetails.User(user.getUsername(), + user.getPassword(), + mapRolesToAuthorities(user.getRoles())); + }else{ + throw new UsernameNotFoundException("Invalid username or password."); + } + } + + private Collection< ? extends GrantedAuthority> mapRolesToAuthorities(Collection roles) { + return roles.stream() + .map(role -> new SimpleGrantedAuthority(role.getName())) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/service/GAAccountService.java b/src/main/java/com/alterdekim/game/service/GAAccountService.java new file mode 100644 index 0000000..839bbb5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GAAccountService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GAAccount; +import com.alterdekim.game.repository.GAAccountRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GAAccountService implements OutputObjectCallback { + + @Autowired + private GAAccountRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GAAccount) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GCDService.java b/src/main/java/com/alterdekim/game/service/GCDService.java new file mode 100644 index 0000000..21fe7b2 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GCDService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GoodClubDancefloor; +import com.alterdekim.game.repository.GoodClubDancefloorRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GCDService implements OutputObjectCallback { + + @Autowired + private GoodClubDancefloorRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GoodClubDancefloor) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GameIconService.java b/src/main/java/com/alterdekim/game/service/GameIconService.java new file mode 100644 index 0000000..3ccdde1 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GameIconService.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GameIcon; +import com.alterdekim.game.repository.GameIconRepository; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GameIconService implements OutputObjectCallback { + + @Autowired + private GameIconRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GameIcon) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GoodMagicService.java b/src/main/java/com/alterdekim/game/service/GoodMagicService.java new file mode 100644 index 0000000..13cfd08 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GoodMagicService.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GoodMagic; +import com.alterdekim.game.repository.GoodMagicRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@NoArgsConstructor +public class GoodMagicService implements OutputObjectCallback { + + @Autowired + private GoodMagicRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GoodMagic) o); + } + + public Optional findById(Long id) { + return this.repository.findById(id); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GoodSmileService.java b/src/main/java/com/alterdekim/game/service/GoodSmileService.java new file mode 100644 index 0000000..a334b50 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GoodSmileService.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GoodSmile; +import com.alterdekim.game.repository.GoodSmileRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@NoArgsConstructor +@Service +public class GoodSmileService implements OutputObjectCallback { + + @Autowired + private GoodSmileRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GoodSmile) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GoodTagService.java b/src/main/java/com/alterdekim/game/service/GoodTagService.java new file mode 100644 index 0000000..c1d78b9 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GoodTagService.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.GoodTag; +import com.alterdekim.game.repository.GoodTagRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@NoArgsConstructor +@Service +public class GoodTagService implements OutputObjectCallback { + + @Autowired + private GoodTagRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((GoodTag) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/GoodsService.java b/src/main/java/com/alterdekim/game/service/GoodsService.java new file mode 100644 index 0000000..deaa948 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/GoodsService.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Good; +import com.alterdekim.game.repository.GoodRepository; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +@Service +public class GoodsService implements OutputObjectCallback { + + @Autowired + private GoodRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Good) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/IncompatibleService.java b/src/main/java/com/alterdekim/game/service/IncompatibleService.java new file mode 100644 index 0000000..8f0dc17 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/IncompatibleService.java @@ -0,0 +1,26 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Incompatible; +import com.alterdekim.game.repository.IncompatibleRepository; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class IncompatibleService implements OutputObjectCallback { + + @Autowired + private IncompatibleRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Incompatible) o); + } + + public List getAll() { + return this.repository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/LevelExperienceService.java b/src/main/java/com/alterdekim/game/service/LevelExperienceService.java new file mode 100644 index 0000000..f9406ef --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/LevelExperienceService.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.LevelExperience; +import com.alterdekim.game.repository.LevelExperienceRepository; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class LevelExperienceService implements OutputObjectCallback { + + @Autowired + private LevelExperienceRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((LevelExperience) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/LibraryService.java b/src/main/java/com/alterdekim/game/service/LibraryService.java new file mode 100644 index 0000000..cc61c01 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/LibraryService.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Library; +import com.alterdekim.game.repository.LibraryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class LibraryService implements OutputObjectCallback { + + @Autowired + private LibraryRepository repository; + + public void save(Library library) { + this.repository.save(library); + } + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Library) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/LocationService.java b/src/main/java/com/alterdekim/game/service/LocationService.java new file mode 100644 index 0000000..9067bbd --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/LocationService.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.component.game.LocationObject; +import com.alterdekim.game.entity.Location; +import com.alterdekim.game.repository.LocationRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Optional; + +@Service +public class LocationService { + + @Autowired + private LocationRepository repository; + + public LocationObject getDefaultLocation() { + Optional l = this.repository.findDefaultLocation(); + if( l.isPresent() ) { + Location r = l.get(); + return new LocationObject(r.getLocationId(), false, false, false, true, r.getMediaResourceId(), r.getX(), r.getY(), r.getName(), Collections.emptyList()); + } + return new LocationObject(); + } + + public void addLocation(Location location) { + this.repository.save(location); + } +} diff --git a/src/main/java/com/alterdekim/game/service/MAService.java b/src/main/java/com/alterdekim/game/service/MAService.java new file mode 100644 index 0000000..056785d --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/MAService.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.MagicAbility; +import com.alterdekim.game.repository.MagicAbilityRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@NoArgsConstructor +public class MAService implements OutputObjectCallback { + + @Autowired + private MagicAbilityRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((MagicAbility) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/MRService.java b/src/main/java/com/alterdekim/game/service/MRService.java new file mode 100644 index 0000000..c5f08c1 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/MRService.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.MediaResource; +import com.alterdekim.game.repository.MediaResourceRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@NoArgsConstructor +public class MRService implements OutputObjectCallback { + + @Autowired + private MediaResourceRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((MediaResource) o); + } + + public List getAll() { + return this.repository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/MRTService.java b/src/main/java/com/alterdekim/game/service/MRTService.java new file mode 100644 index 0000000..ae40ca1 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/MRTService.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.MediaResourcePath; +import com.alterdekim.game.repository.MRTRepository; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@NoArgsConstructor +@Service +public class MRTService implements OutputObjectCallback { + + @Autowired + private MRTRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((MediaResourcePath) o); + } + + public List getAll() { + return this.repository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/MiniquestService.java b/src/main/java/com/alterdekim/game/service/MiniquestService.java new file mode 100644 index 0000000..4b09370 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/MiniquestService.java @@ -0,0 +1,30 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entity.Miniquest; +import com.alterdekim.game.repository.MiniquestRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class MiniquestService { + private final MiniquestRepository repository; + + public List getAllMiniquests() { + return repository.findAll(); + } + + public Miniquest getMiniquestById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void saveMiniquest(Miniquest miniquest) { + repository.save(miniquest); + } +} diff --git a/src/main/java/com/alterdekim/game/service/MissionService.java b/src/main/java/com/alterdekim/game/service/MissionService.java new file mode 100644 index 0000000..026a50d --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/MissionService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Mission; +import com.alterdekim.game.repository.MissionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MissionService implements OutputObjectCallback { + + @Autowired + private MissionRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Mission) o); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/service/PhoneIconService.java b/src/main/java/com/alterdekim/game/service/PhoneIconService.java new file mode 100644 index 0000000..84665ea --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PhoneIconService.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.PhoneIcon; +import com.alterdekim.game.repository.PhoneIconRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PhoneIconService implements OutputObjectCallback { + @Autowired + private PhoneIconRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((PhoneIcon) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PhoneMessageService.java b/src/main/java/com/alterdekim/game/service/PhoneMessageService.java new file mode 100644 index 0000000..6ea4ed8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PhoneMessageService.java @@ -0,0 +1,32 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entity.Miniquest; +import com.alterdekim.game.entity.PhoneMessage; +import com.alterdekim.game.repository.MiniquestRepository; +import com.alterdekim.game.repository.PhoneMessageRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class PhoneMessageService { + private final PhoneMessageRepository repository; + + public List getAllPhoneMessages() { + return repository.findAll(); + } + + public PhoneMessage getPhoneMessageById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void savePhoneMessage(PhoneMessage phoneMessage) { + repository.save(phoneMessage); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PostcardService.java b/src/main/java/com/alterdekim/game/service/PostcardService.java new file mode 100644 index 0000000..a87bb2a --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PostcardService.java @@ -0,0 +1,32 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entity.PhoneMessage; +import com.alterdekim.game.entity.Postcard; +import com.alterdekim.game.repository.PhoneMessageRepository; +import com.alterdekim.game.repository.PostcardRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class PostcardService { + private final PostcardRepository repository; + + public List getAllPostcards() { + return repository.findAll(); + } + + public Postcard getPostcardById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void savePostcard(Postcard postcard) { + repository.save(postcard); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PreloaderService.java b/src/main/java/com/alterdekim/game/service/PreloaderService.java new file mode 100644 index 0000000..ab3bc53 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PreloaderService.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.service; + + +import com.alterdekim.game.entity.Postcard; +import com.alterdekim.game.entity.Preloader; +import com.alterdekim.game.repository.PostcardRepository; +import com.alterdekim.game.repository.PreloaderRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class PreloaderService { + private final PreloaderRepository repository; + + public List getAllPreloaders() { + return repository.findSelected(); + } + + public Preloader getPreloaderById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void savePreloader(Preloader preloader) { + repository.save(preloader); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PriceExchangeService.java b/src/main/java/com/alterdekim/game/service/PriceExchangeService.java new file mode 100644 index 0000000..46d3b3f --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PriceExchangeService.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.PriceExchange; +import com.alterdekim.game.repository.PriceExchangeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PriceExchangeService implements OutputObjectCallback { + @Autowired + private PriceExchangeRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((PriceExchange) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PriceLicenseService.java b/src/main/java/com/alterdekim/game/service/PriceLicenseService.java new file mode 100644 index 0000000..8814635 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PriceLicenseService.java @@ -0,0 +1,18 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.PriceLicense; +import com.alterdekim.game.repository.PriceLicenseRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class PriceLicenseService implements OutputObjectCallback { + @Autowired + private PriceLicenseRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((PriceLicense) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PromotionBannerService.java b/src/main/java/com/alterdekim/game/service/PromotionBannerService.java new file mode 100644 index 0000000..4ba476c --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PromotionBannerService.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.service; + + +import com.alterdekim.game.entity.Preloader; +import com.alterdekim.game.entity.PromotionBanner; +import com.alterdekim.game.repository.PreloaderRepository; +import com.alterdekim.game.repository.PromotionBannerRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class PromotionBannerService { + private final PromotionBannerRepository repository; + + public List getAllPromotionBanners() { + return repository.findSelected(); + } + + public PromotionBanner getPromotionBannerById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void savePromotionBanner(PromotionBanner promotionBanner) { + repository.save(promotionBanner); + } +} diff --git a/src/main/java/com/alterdekim/game/service/PromotionService.java b/src/main/java/com/alterdekim/game/service/PromotionService.java new file mode 100644 index 0000000..e0026d0 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/PromotionService.java @@ -0,0 +1,33 @@ +package com.alterdekim.game.service; + + +import com.alterdekim.game.entity.Promotion; +import com.alterdekim.game.entity.PromotionBanner; +import com.alterdekim.game.repository.PromotionBannerRepository; +import com.alterdekim.game.repository.PromotionRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@AllArgsConstructor +@Service +public class PromotionService { + private final PromotionRepository repository; + + public List getAllPromotions() { + return repository.findSelected(); + } + + public Promotion getPromotionById(long id) { + return repository.findById(id).orElse(null); + } + + public void removeById(long id) { + repository.deleteById(id); + } + + public void savePromotion(Promotion promotion) { + repository.save(promotion); + } +} diff --git a/src/main/java/com/alterdekim/game/service/ROService.java b/src/main/java/com/alterdekim/game/service/ROService.java new file mode 100644 index 0000000..91f64f4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/ROService.java @@ -0,0 +1,26 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.ResourceObject; +import com.alterdekim.game.repository.ResourceObjectRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@NoArgsConstructor +@Service +public class ROService implements OutputObjectCallback { + @Autowired + private ResourceObjectRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((ResourceObject) o); + } + + public List getAll() { + return this.repository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/ResourceService.java b/src/main/java/com/alterdekim/game/service/ResourceService.java new file mode 100644 index 0000000..bac9897 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/ResourceService.java @@ -0,0 +1,28 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.entity.MediaResource; +import com.alterdekim.game.entity.MediaResourcePath; +import com.alterdekim.game.repository.MRTRepository; +import com.alterdekim.game.repository.MediaResourceRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public class ResourceService { + + @Autowired + private MediaResourceRepository mediaResourceRepository; + + @Autowired + private MRTRepository mrtRepository; + + // todo: abolish hardcoded destination address + public Optional getUrlByMediaResourceId(Long mediaResourceId) { + Optional mr = this.mediaResourceRepository.findById(mediaResourceId); + if( mr.isEmpty() ) return Optional.empty(); + Optional path = mrtRepository.findById(mr.get().getTId().longValue()); + return path.map(mediaResourcePath -> "http://localhost:8082/" + mediaResourcePath.getVal() + mr.get().getUrl()); + } +} diff --git a/src/main/java/com/alterdekim/game/service/SmileService.java b/src/main/java/com/alterdekim/game/service/SmileService.java new file mode 100644 index 0000000..3517873 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/SmileService.java @@ -0,0 +1,21 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Smile; +import com.alterdekim.game.repository.SmileRepository; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SmileService implements OutputObjectCallback { + + @Autowired + private SmileRepository repository; + + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Smile) o); + } +} diff --git a/src/main/java/com/alterdekim/game/service/SportInventoryService.java b/src/main/java/com/alterdekim/game/service/SportInventoryService.java new file mode 100644 index 0000000..88c2578 --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/SportInventoryService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.SportInventory; +import com.alterdekim.game.repository.SportInventoryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SportInventoryService implements OutputObjectCallback { + + @Autowired + private SportInventoryRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((SportInventory) o); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/service/TRService.java b/src/main/java/com/alterdekim/game/service/TRService.java new file mode 100644 index 0000000..052520e --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/TRService.java @@ -0,0 +1,27 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.TextResource; +import com.alterdekim.game.repository.TextResourceRepository; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@NoArgsConstructor +@Service +public class TRService implements OutputObjectCallback { + + @Autowired + private TextResourceRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((TextResource) o); + } + + public List getAll() { + return this.repository.findAll(); + } +} diff --git a/src/main/java/com/alterdekim/game/service/UserService.java b/src/main/java/com/alterdekim/game/service/UserService.java new file mode 100644 index 0000000..08ff99b --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/UserService.java @@ -0,0 +1,386 @@ +package com.alterdekim.game.service; + +import com.alterdekim.game.component.game.*; +import com.alterdekim.game.entity.Role; +import com.alterdekim.game.entity.User; +import com.alterdekim.game.entity.UserProperty; +import com.alterdekim.game.repository.RoleRepository; +import com.alterdekim.game.repository.UserPropertyRepository; +import com.alterdekim.game.repository.UserRepository; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +public class UserService { + + @Autowired + private UserRepository userRepository; + @Autowired + private RoleRepository roleRepository; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private UserPropertyRepository propertyRepository; + + @Autowired + private AvatarInventoryService inventoryService; + + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } + + public long saveUser(String username, String password) { + User user = new User(); + user.setUsername(username); + user.setPassword(passwordEncoder.encode(password)); + Role role = roleRepository.findByName("ROLE_USER"); + if(role == null){ + role = checkRoleExist(); + } + user.setRoles(Collections.singletonList(role)); + return userRepository.save(user).getId(); + } + + private Role checkRoleExist() { + Role role = new Role(); + role.setName("ROLE_USER"); + return roleRepository.save(role); + } + + public Optional findUserProperty(Long userId, PlayerProperties type) { + Optional res = this.propertyRepository.findByUserIdAndTag(userId, type); + if( res.isEmpty() ) return Optional.empty(); + UserProperty property = res.get(); + return Optional.of(switch (property.getType().getValueType()) { + case String -> property.getStringVal(); + case Integer -> property.getIntegerVal(); + case Boolean -> property.getBooleanVal(); + case Date -> property.getDateVal(); + case Enum -> property.getType() == PlayerProperties.RoleFlags ? RoleFlags.valueOf(property.getStringVal()) : ClubAccessType.valueOf(property.getStringVal()); + }); + } + + public void pushUserProperty(Long userId, PlayerProperties type, Object val) { + this.propertyRepository.deleteByUserIdAndType(userId, type); + this.propertyRepository.save(switch (type.getValueType()) { + case String -> new UserProperty(userId, (String) val, null, null, null, type); + case Integer -> new UserProperty(userId, null, null, (Integer) val, null, type); + case Boolean -> new UserProperty(userId, null, (Boolean) val, null, null, type); + case Date -> new UserProperty(userId, null, null, null, (LocalDateTime) val, type); + case Enum -> new UserProperty(userId, String.valueOf(val), null, null, null, type); + }); + } + + // todo: optimize all getters, group them by type of result value. + public RoleFlags getRoleFlags(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.RoleFlags); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.RoleFlags, RoleFlags.SA); + return RoleFlags.SA; + } + return (RoleFlags) res.get(); + } + + public Boolean getIsLimited(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.IsLimited); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.IsLimited, false); + return false; + } + return (Boolean) res.get(); + } + + public Boolean getIsMinorSecretAgent(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.IsMinorSecretAgent); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.IsMinorSecretAgent, true); + return false; + } + return (Boolean) res.get(); + } + + public Boolean getIsHomeLocked(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.IsHomeLocked); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.IsHomeLocked, true); + return true; + } + return (Boolean) res.get(); + } + + public Boolean getIsClubLocked(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.IsClubLocked); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.IsClubLocked, true); + return true; + } + return (Boolean) res.get(); + } + + public Boolean getIsClubPresent(Long userId) { + // todo: Implement clubs; + return false; + } + + public ClubAccessType getClubAccessType(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.ClubAccessType); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.ClubAccessType, ClubAccessType.CLOSED); + return ClubAccessType.CLOSED; + } + return (ClubAccessType) res.get(); + } + + public Boolean getIsFriend(Long userId) { + // todo: Implement friends; + return false; + } + + public Integer getExperience(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.Experience); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.Experience, 0); + return 0; + } + return (Integer) res.get(); + } + + public Integer getLevel(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.Level); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.Level, 1); + return 1; + } + return (Integer) res.get(); + } + + public Integer getMagicLevel(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.MagicLevel); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.MagicLevel, 1); + return 1; + } + return (Integer) res.get(); + } + + public Integer getRaceLevel(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.RaceLevel); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.RaceLevel, 0); + return 1; + } + return (Integer) res.get(); + } + + public Integer getCampId(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.CampId); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.CampId, 0); + return 1; + } + return (Integer) res.get(); + } + + public Integer getNiftTotalCount(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.NiftTotalCount); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.NiftTotalCount, 0); + return 1; + } + return (Integer) res.get(); + } + + public Integer getInstructorLevel(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.InstructorLevel); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.InstructorLevel, 10); + return 1; + } + return (Integer) res.get(); + } + + public Integer getUsualTickets(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.UsualTickets); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.UsualTickets, 1000); + return 1000; + } + return (Integer) res.get(); + } + + public Integer getMagicTickets(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.MagicTickets); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.MagicTickets, 500); + return 500; + } + return (Integer) res.get(); + } + + public Integer getVisaId(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.VisaId); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.VisaId, 10); + return 0; + } + return (Integer) res.get(); + } + + public Integer getCurrentUseCount(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.CurrentUseCount); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.CurrentUseCount, 10); + return 0; + } + return (Integer) res.get(); + } + + public Integer getLastNewspaperId(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.LastNewspaperId); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.LastNewspaperId, 10); + return 0; + } + return (Integer) res.get(); + } + + public Boolean getHasSnFriends(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.HasSnFriends); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.HasSnFriends, true); + return false; + } + return (Boolean) res.get(); + } + + public Integer getMinsLeft(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.MinsLeft); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.MinsLeft, 10); + return 0; + } + return (Integer) res.get(); + } + + public Integer getTimesUsed(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.TimesUsed); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.TimesUsed, 10); + return 0; + } + return (Integer) res.get(); + } + + public Integer getPhoneCardBalance(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.PhoneCardBalance); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.PhoneCardBalance, 10); + return 10; + } + return (Integer) res.get(); + } + + public Integer getWeaponsCount(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.WeaponsCount); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.WeaponsCount, 15); + return 15; + } + return (Integer) res.get(); + } + + public Integer getOxygenUnits(Long userId) { + Optional res = this.findUserProperty(userId, PlayerProperties.OxygenUnits); + if( res.isEmpty() ) { + this.pushUserProperty(userId, PlayerProperties.OxygenUnits, 1); + return 1; + } + return (Integer) res.get(); + } + + public Optional getUsernameById(Long userId) { + return this.userRepository.findById(userId).map(User::getUsername); + } + + public UserAccount getUserAccountByUserId(Long userId) { + return new UserAccount( + getPhoneCardBalance(userId), + getWeaponsCount(userId), + getOxygenUnits(userId) + ); + } + + public UserPhone getUserPhoneByUserId(Long userId) { + return new UserPhone( + 0, + 0L, + 0, + getPhoneCardBalance(userId) + ); + } + + public UserInfo getUserInfoByUserId(Long userId) { + // todo: implement hardcoded stuff + return new UserInfo( + userId, + getRoleFlags(userId), + getIsLimited(userId), + true, + getIsMinorSecretAgent(userId), + getIsHomeLocked(userId), + getIsClubLocked(userId), + getIsClubPresent(userId), + getClubAccessType(userId), + getIsFriend(userId), + getExperience(userId), + getLevel(userId), + getMagicLevel(userId), + getRaceLevel(userId), + 5, + 10, + getCampId(userId), + getNiftTotalCount(userId), + "Server", + LocalDateTime.now(), + getInstructorLevel(userId), + getUsualTickets(userId), + getMagicTickets(userId), + getVisaId(userId), + getCurrentUseCount(userId), + getLastNewspaperId(userId), + getHasSnFriends(userId), + getMinsLeft(userId), + getTimesUsed(userId), + this.getUsernameById(userId).get() + ); + } + + public UserInitInfo getUserInitInfoByUserId(Long userId) { + return new UserInitInfo( + getUserAccountByUserId(userId), + getUserInfoByUserId(userId), + getUserPhoneByUserId(userId), + inventoryService.getMagicAbilitiesItemsByUserId(userId), + null // inventoryService.getAvatarInventoryItemsByUserId(userId) + ); + } + + public PlayerAvatar getAvatarById(long userId, Player p) { + Map body = new HashMap<>(); + List i = inventoryService.getAvatarItemsByUserId(userId); + IntStream.range(0, i.size()).boxed().forEach(g -> body.put(String.valueOf(g+1), i.get(g))); + UserAvatar a = new UserAvatar(getLevel(userId), getUsernameById(userId).get(), p.getX(), p.getY(), p.getState(), body); + return new PlayerAvatar(String.valueOf(getRoleFlags(userId).getValue()), a); + } + + public void removeUser(long userId) { + this.userRepository.deleteById(userId); + } +} diff --git a/src/main/java/com/alterdekim/game/service/VisaService.java b/src/main/java/com/alterdekim/game/service/VisaService.java new file mode 100644 index 0000000..ccf2ddc --- /dev/null +++ b/src/main/java/com/alterdekim/game/service/VisaService.java @@ -0,0 +1,19 @@ +package com.alterdekim.game.service; + +import com.alterdekim.flash.decompiler.mapper.OutputObjectCallback; +import com.alterdekim.game.entity.Visa; +import com.alterdekim.game.repository.VisaRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class VisaService implements OutputObjectCallback { + + @Autowired + private VisaRepository repository; + + @Override + public void onOutputObjectReady(Object o) { + this.repository.save((Visa) o); + } +} diff --git a/src/main/java/com/alterdekim/game/storage/FileSystemStorageService.java b/src/main/java/com/alterdekim/game/storage/FileSystemStorageService.java new file mode 100644 index 0000000..70dd1c5 --- /dev/null +++ b/src/main/java/com/alterdekim/game/storage/FileSystemStorageService.java @@ -0,0 +1,97 @@ +package com.alterdekim.game.storage; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.stream.Stream; + +@Service +public class FileSystemStorageService implements StorageService { + + private final Path rootLocation; + + @Autowired + public FileSystemStorageService(StorageProperties properties) { + if(properties.getLocation().trim().isEmpty()) { + throw new StorageException("File upload location can not be Empty."); + } + this.rootLocation = Paths.get(properties.getLocation()); + } + + @Override + public void store(MultipartFile file) { + try { + if (file.isEmpty()) { + throw new StorageException("Failed to store empty file."); + } + Path destinationFile = this.rootLocation.resolve( + Paths.get(file.getOriginalFilename())) + .normalize().toAbsolutePath(); + if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) { + throw new StorageException( + "Cannot store file outside current directory."); + } + try (InputStream inputStream = file.getInputStream()) { + Files.copy(inputStream, destinationFile, + StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + throw new StorageException("Failed to store file.", e); + } + } + + @Override + public Stream loadAll() { + try { + return Files.walk(this.rootLocation, 2) + .filter(path -> !path.equals(this.rootLocation)) + .map(this.rootLocation::relativize); + } catch (IOException e) { + throw new StorageException("Failed to read stored files", e); + } + } + + @Override + public Path load(String filename) { + return rootLocation.resolve(filename); + } + + @Override + public Resource loadAsResource(String filename) { + try { + Resource resource = new ClassPathResource(filename); + if (resource.exists() || resource.isReadable()) { + return resource; + } else { + throw new StorageFileNotFoundException( + "Could not read file: " + filename); + } + } catch (Exception e) { + throw new StorageFileNotFoundException("Could not read file: " + filename, e); + } + } + + @Override + public void deleteAll() { + FileSystemUtils.deleteRecursively(rootLocation.toFile()); + } + + @Override + public void init() { + try { + Files.createDirectories(rootLocation); + } catch (IOException e) { + throw new StorageException("Could not initialize storage", e); + } + } +} diff --git a/src/main/java/com/alterdekim/game/storage/StorageException.java b/src/main/java/com/alterdekim/game/storage/StorageException.java new file mode 100644 index 0000000..824209d --- /dev/null +++ b/src/main/java/com/alterdekim/game/storage/StorageException.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.storage; + +public class StorageException extends RuntimeException { + + public StorageException(String message) { + super(message); + } + + public StorageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/alterdekim/game/storage/StorageFileNotFoundException.java b/src/main/java/com/alterdekim/game/storage/StorageFileNotFoundException.java new file mode 100644 index 0000000..6fa838f --- /dev/null +++ b/src/main/java/com/alterdekim/game/storage/StorageFileNotFoundException.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.storage; + +public class StorageFileNotFoundException extends StorageException { + + public StorageFileNotFoundException(String message) { + super(message); + } + + public StorageFileNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/storage/StorageProperties.java b/src/main/java/com/alterdekim/game/storage/StorageProperties.java new file mode 100644 index 0000000..3f6df00 --- /dev/null +++ b/src/main/java/com/alterdekim/game/storage/StorageProperties.java @@ -0,0 +1,12 @@ +package com.alterdekim.game.storage; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Setter +@Getter +@ConfigurationProperties("storage") +public class StorageProperties { + private String location = "static"; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/storage/StorageService.java b/src/main/java/com/alterdekim/game/storage/StorageService.java new file mode 100644 index 0000000..802135c --- /dev/null +++ b/src/main/java/com/alterdekim/game/storage/StorageService.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.storage; + +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; +import java.util.stream.Stream; + +public interface StorageService { + + void init(); + + void store(MultipartFile file); + + Stream loadAll(); + + Path load(String filename); + + Resource loadAsResource(String filename); + + void deleteAll(); + +} diff --git a/src/main/java/com/alterdekim/game/utils/ByteUtils.java b/src/main/java/com/alterdekim/game/utils/ByteUtils.java new file mode 100644 index 0000000..a6cda0e --- /dev/null +++ b/src/main/java/com/alterdekim/game/utils/ByteUtils.java @@ -0,0 +1,23 @@ +package com.alterdekim.game.utils; + +import java.nio.ByteBuffer; + +public class ByteUtils { + public static byte[] longToBytes(long l) { + byte[] result = new byte[Long.BYTES]; + for (int i = Long.BYTES - 1; i >= 0; i--) { + result[i] = (byte)(l & 0xFF); + l >>= Byte.SIZE; + } + return result; + } + + public static long bytesToLong(byte[] b) { + long result = 0; + for (int i = 0; i < Long.BYTES; i++) { + result <<= Byte.SIZE; + result |= (b[i] & 0xFF); + } + return result; + } +} diff --git a/src/main/java/com/alterdekim/game/utils/GameUtils.java b/src/main/java/com/alterdekim/game/utils/GameUtils.java new file mode 100644 index 0000000..8036f97 --- /dev/null +++ b/src/main/java/com/alterdekim/game/utils/GameUtils.java @@ -0,0 +1,25 @@ +package com.alterdekim.game.utils; + +import com.alterdekim.game.component.game.BodyPartType; + +public class GameUtils { + public static int extractLocationId(String s) { + String[] r = s.split("\\$"); + if( r.length != 3) return 0; + return Integer.parseInt(r[2]); + } + + public static int getLayerIdByBodyPartType(BodyPartType type) { + return switch (type) { + case Body -> 20; + case Legs -> 10; + case Ears -> 50; + case Eyes -> 30; + case Nose -> 40; + case Mouth -> 26; + case Beak -> 41; + case Horn -> 55; + default -> 0; + }; + } +} diff --git a/src/main/java/com/alterdekim/game/utils/StringUtils.java b/src/main/java/com/alterdekim/game/utils/StringUtils.java new file mode 100644 index 0000000..ebd31ce --- /dev/null +++ b/src/main/java/com/alterdekim/game/utils/StringUtils.java @@ -0,0 +1,26 @@ +package com.alterdekim.game.utils; + +public class StringUtils { + + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } +} diff --git a/src/main/java/com/alterdekim/game/xml/ClubAccessTypeSerializer.java b/src/main/java/com/alterdekim/game/xml/ClubAccessTypeSerializer.java new file mode 100644 index 0000000..1b98a44 --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/ClubAccessTypeSerializer.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.xml; + +import com.alterdekim.game.component.game.ClubAccessType; +import com.alterdekim.game.component.game.RoleFlags; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class ClubAccessTypeSerializer extends JsonSerializer { + @Override + public void serialize(ClubAccessType clubAccessType, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeNumber(clubAccessType.getValue()); + } +} diff --git a/src/main/java/com/alterdekim/game/xml/CryptoSerializer.java b/src/main/java/com/alterdekim/game/xml/CryptoSerializer.java new file mode 100644 index 0000000..dd2a41a --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/CryptoSerializer.java @@ -0,0 +1,34 @@ +package com.alterdekim.game.xml; + +import com.alterdekim.game.controller.result.async.ServerActionCData; +import com.alterdekim.game.crypto.RC4; +import com.alterdekim.game.utils.StringUtils; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.List; + +@Slf4j +public class CryptoSerializer extends JsonSerializer> { + private static final String S = "\r\n"; + + @Override + public void serialize(List list, JsonGenerator generator, SerializerProvider provider) throws IOException { + for( ServerActionCData v : list ) { + String s = String.format(S, StringUtils.bytesToHex(new RC4("_level0".getBytes()).encrypt( + new XmlMapper() + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + .writeValueAsBytes(v) + )).toLowerCase() + ); + generator.writeRaw(s); + } + //generator.writeNull(); + } +} diff --git a/src/main/java/com/alterdekim/game/xml/LocalDateTimeSerializer.java b/src/main/java/com/alterdekim/game/xml/LocalDateTimeSerializer.java new file mode 100644 index 0000000..b21dab4 --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/LocalDateTimeSerializer.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.xml; + +import com.alterdekim.game.component.game.ClubAccessType; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalDateTime; + +public class LocalDateTimeSerializer extends JsonSerializer { + @Override + public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString("2008-08-08T12:07:44.927"); // localDateTime.toString() + } +} diff --git a/src/main/java/com/alterdekim/game/xml/NumericBooleanSerializer.java b/src/main/java/com/alterdekim/game/xml/NumericBooleanSerializer.java new file mode 100644 index 0000000..9e533fb --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/NumericBooleanSerializer.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.xml; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class NumericBooleanSerializer extends JsonSerializer { + @Override + public void serialize(Boolean bool, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeString(bool ? "1" : "0"); + } +} diff --git a/src/main/java/com/alterdekim/game/xml/RoleFlagsDeserializer.java b/src/main/java/com/alterdekim/game/xml/RoleFlagsDeserializer.java new file mode 100644 index 0000000..800b985 --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/RoleFlagsDeserializer.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.xml; + +import com.alterdekim.game.component.game.RoleFlags; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; + +public class RoleFlagsDeserializer extends JsonDeserializer { + @Override + public RoleFlags deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + Integer i = jsonParser.readValueAs(Integer.class); + return RoleFlags.fromId(i); + } +} diff --git a/src/main/java/com/alterdekim/game/xml/RoleFlagsSerializer.java b/src/main/java/com/alterdekim/game/xml/RoleFlagsSerializer.java new file mode 100644 index 0000000..ac3fee6 --- /dev/null +++ b/src/main/java/com/alterdekim/game/xml/RoleFlagsSerializer.java @@ -0,0 +1,15 @@ +package com.alterdekim.game.xml; + +import com.alterdekim.game.component.game.RoleFlags; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class RoleFlagsSerializer extends JsonSerializer { + @Override + public void serialize(RoleFlags roleFlags, JsonGenerator generator, SerializerProvider provider) throws IOException { + generator.writeNumber(roleFlags.getValue()); + } +} diff --git a/src/main/resources/templates/db.html b/src/main/resources/templates/db.html new file mode 100644 index 0000000..db9de19 --- /dev/null +++ b/src/main/resources/templates/db.html @@ -0,0 +1,4808 @@ + + + + + Title + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..70015ca --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,256 @@ + + + + + PolyDarker + + + + + + + + + + + + +
+ +
+ + Закрыть + + +
+
+ + + + +
+ Загрузка... +
+
+
+ + Закрыть + + + + + + +
+
+ + +
+ + + + + + + + + +
+
+ +
+
+ +
+ +
    + +
  • + +
    +

    Что нужно знать о безопасности,

    +
    чтобы не попасть в плохую историю
    +
    + +
  • +
  • + +
    +

    Правила игры -

    +
    узнай всё о Шарараме
    +
    + +
  • +
  • + +
    +

    Экскурсия по игре -

    +
    прогуляйся по Шарараму
    +
    + +
  • +
  • + +
    +

    Получить ШарарамКарту,

    +
    чтобы тратить заработанные смешинки
    +
    + +
  • +
+
+ + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..9d9433f --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,16 @@ + + + + + + Polydarker + + + +
+ + +
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/main.html b/src/main/resources/templates/main.html new file mode 100644 index 0000000..3452eed --- /dev/null +++ b/src/main/resources/templates/main.html @@ -0,0 +1,42 @@ + + + + Игра | Unofficial + + + +
+
+ +
+ +
+
+
+ + \ No newline at end of file