commit e691d0504aa66d6cbf5c6617505c8ae20749db8b Author: Mordred Admin Date: Tue Aug 8 13:53:47 2017 +0200 Initial commit of the sapphire server diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..bf07950c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 2.6) +project (Sapphire) + +set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) +set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) + +# boost stuff +if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + set(Boost_COMPILER "-vc140") +endif() +set(SAPPHIRE_BOOST_VER 1.60.0) +set(SAPPHIRE_BOOST_FOLDER_NAME boost_1_60_0) + +include_directories("${PROJECT_INCLUDE_DIR}") +include_directories("${PROJECT_SOURCE_DIR}") + +add_subdirectory("src/servers") +add_subdirectory("src/libraries/sapphire/datReader") diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000..441c9d43 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,37 @@ +{ + // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. + "configurations": [ + { + "name": "x86-Debug", + "generator": "Visual Studio 14 2015", + "configurationType" : "Debug", + "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal" + }, + { + "name": "x86-Release", + "generator": "Visual Studio 14 2015", + "configurationType": "Release", + "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal" + }, + { + "name": "x64-Debug", + "generator": "Visual Studio 14 2015 Win64", + "configurationType" : "Debug", + "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal" + }, + { + "name": "x64-Release", + "generator": "Visual Studio 14 2015 Win64", + "configurationType": "Release", + "buildRoot": "${env.LOCALAPPDATA}\\CMakeBuild\\${workspaceHash}\\build\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-m -v:minimal" + } + ] +} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..a871fcfd --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,662 @@ + 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 00000000..e8ff2cc7 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Sapphire - FINAL FANTASY XIV Server Emulator +[![Discord Server](https://img.shields.io/badge/discord-Sapphire-7289DA.svg)](https://discord.gg/KfrZCkx) + +![FFXIV Sapphire](http://i.imgur.com/I4bj1tR.png) + +Sapphire is a FINAL FANTASY XIV 4.0+ Server Emulator currently in development. + +## Features + +* Fully working base world servers, including character creation, chat, player interaction, etc. +* Basic action/battle system +* Teleports, discovery +* NPCs, enemies +* Support for most Quest types(instanced content planned, to be added later) +* Content scripting via ChaiScript +* Retail GM commands working + +## Dependencies + Compiling +Sapphire requires the following software: + +| *Name* | *Windows* | *Linux* | +| ------ | --------- | ------- | +| CMake 2.6+ and C++14 capable compiler | [Visual Studio 2017](https://www.visualstudio.com/) | Your favorite C++14 capable compiler | +| Boost 1.63.0 | [Win32 precompiled binaries](https://sourceforge.net/projects/boost/files/boost-binaries/1.63.0/boost_1_63_0-msvc-14.0-32.exe/download) | Boost libraries from your distribution's package manager | +| MySQL Server 5.7 | [Official Site](https://dev.mysql.com/downloads/mysql/) | MySQL server from your distribution's package manager | +| C# Compiler(used for various tools) | [Visual Studio 2017](https://www.visualstudio.com/) \| [Mono](http://www.mono-project.com/) | [Mono](http://www.mono-project.com/) from your distribution's package manager | + +**Windows** +Set the environment variables ``BOOST_ROOT_DIR`` and ``BOOST_LIB_DIR`` to ``[boost main folder]`` and ``[boost main folder]/lib32-msvc-14.0`` respectively **or** copy your boost installation into the project's ``src/lib`` folder. +* In *Visual Studio 2017*: Open the project via ``File`` > ``Open`` > ``Folder`` and wait, till CMake automatically finishes cache generation. Right click any CMakeLists.txt and select build to build a application. +To configure debug startup parameters, select ``Debug and Launch settings``, the application you want to set up parameters for, and add a ``args`` json array containing your launch parameters in ``configurations``. +If Visual Studio fails to generate a CMake Cache or does not show options to build, make sure that the newest version of it is installed and all environment variables are set correctly. Keep in mind that generating a cache can take a while on some machines. +* In *Visual Studio 2015*: Generate a Visual Studio project via CMake: ``cmake -G "Visual Studio 14 2015"`` and open it as a normal solution in Visual Studio. + +**Linux** +Generate a cache with CMake and use your favorite C++14 capable compiler to compile those wonderful lines of code into binaries with ``make``. +Sapphire is **not** currently configured to compile in 64bit. Make sure that all libraries have their ``:i386`` versions installed. + +## Links + +* [Sapphire on YouTube](https://www.youtube.com/channel/UCJKYuovoGsq7PxSAfrNJKbw) + +Final Fantasy XIV © 2010-2017 SQUARE ENIX CO., LTD. All Rights Reserved. We are not affiliated with SQUARE ENIX CO., LTD. in any way. diff --git a/bin/config/settings_lobby.xml b/bin/config/settings_lobby.xml new file mode 100644 index 00000000..5cccee53 --- /dev/null +++ b/bin/config/settings_lobby.xml @@ -0,0 +1,27 @@ + + + + 54994 + 54998 + + 127.0.0.1 + + C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv + + 127.0.0.1 + + 54992 + + 127.0.0.1:8080 + + default + + + 127.0.0.1 + 3306 + root + + sapphire + + + \ No newline at end of file diff --git a/bin/config/settings_rest.xml b/bin/config/settings_rest.xml new file mode 100644 index 00000000..d881dda5 --- /dev/null +++ b/bin/config/settings_rest.xml @@ -0,0 +1,26 @@ + + + + 54994 + 54998 + + 127.0.0.1 + + C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv + + 127.0.0.1 + + 127.0.0.1 + + default + + 8080 + + 127.0.0.1 + 3306 + root + + sapphire + + + \ No newline at end of file diff --git a/bin/config/settings_zone.xml b/bin/config/settings_zone.xml new file mode 100644 index 00000000..8259be1d --- /dev/null +++ b/bin/config/settings_zone.xml @@ -0,0 +1,22 @@ + + + + + 100 + + 54992 + + 127.0.0.1 + + C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv + + + 127.0.0.1 + 3306 + root + + sapphire + + + + \ No newline at end of file diff --git a/bin/libmysql.dll b/bin/libmysql.dll new file mode 100644 index 00000000..479cefda Binary files /dev/null and b/bin/libmysql.dll differ diff --git a/bin/scripts/chai/CmnDefCutSceneReplay.chai_outdated b/bin/scripts/chai/CmnDefCutSceneReplay.chai_outdated new file mode 100644 index 00000000..4bddaa63 --- /dev/null +++ b/bin/scripts/chai/CmnDefCutSceneReplay.chai_outdated @@ -0,0 +1,61 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai + + +// Quest Script: SubFst004_00027 +// Quest Name: Preserving the Past +// Quest ID: 65563 +// Start NPC: 1000194 +// End NPC: 1000789 + +class CmnDefCutSceneReplayDef +{ + // Basic quest information + var quest_name + var quest_id + + // These are the quest vars / flags used in this quest + // GetQuestUI8AL + // GetQuestUI8BH + + // Available Scenes in this quest, not necessarly all are used + attr onScene00000; + + // Quest rewards + var RewardExpFactor; + var RewardItem; + var RewardItemCount; + + // Entities found in the script data of the quest + // some of these may be useful + var ACTOR0; + var ACTOR1; + var ACTOR2; + var ITEM0; + + def CmnDefCutSceneReplayDef() + { + + } + + def Scene00000( player, eventId, flags, unk, unk1 ) + { + player.eventPlay(eventId, 0, 0x2000, unk, 1, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + player.eventFinish(eventId, 1); + } ); + } + +}; + + +def CmnDefCutSceneReplay_START(player, actorId, eventId) +{ + var quest = CmnDefCutSceneReplay(); + var actor = mapActor( actorId ); + + player.eventStart( actorId, eventId, 1, 0, 0 ); + CmnDefCutSceneReplay.Scene00000( player, eventId, 0, 0, 0 ); +} diff --git a/bin/scripts/chai/aetheryte/Aethernet.chai b/bin/scripts/chai/aetheryte/Aethernet.chai new file mode 100644 index 00000000..db86ed39 --- /dev/null +++ b/bin/scripts/chai/aetheryte/Aethernet.chai @@ -0,0 +1,57 @@ +//Aethernet - Aetheryte + +class AethernetDef +{ + + def AethernetDef() + { + this.AetheryteBaseId = 0x50000; + this.AETHERYTE_MENU_AETHERNET = 1; + this.AETHERYTE_MENU_HOUSING = 2; + this.AETHERYTE_MENU_HOME_POINT = 3; + this.AETHERYTE_MENU_FAVORITE_POINT = 4; + this.AETHERYTE_MENU_FAVORITE_POINT_SECURITY_TOKEN = 5; + } + + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( player.isAetheryteRegistered( eventId & 0xFFFF ) ) + { + player.eventPlay( eventId, 2, NONE, + fun ( player, eventId, param1, param2, param3 ) + { + var aetherId = eventId & 0xFFFF; + + if ( param1 == 256 ) // aethernet + { + //player.setTeleporting( true ); + player.teleport( param2, 2 ); + } + } ); + } + else + { + var ACTION_ATTUNE = 0x13; + player.eventActionStart( eventId, + ACTION_ATTUNE, + // callback function for finished casting + fun ( player, eventId, additional ) + { + + var aetherId = eventId & 0xFFFF; + player.aetheryteRegister( aetherId ); + player.eventPlay( eventId, 3, 0, 0, 0); + + }, + // callback for interrupted action. + fun ( player, eventId, additional ){}, + 0); + player.unlock(); + + } + + } +} +GLOBAL Aethernet = AethernetDef(); \ No newline at end of file diff --git a/bin/scripts/chai/aetheryte/Aetheryte.chai b/bin/scripts/chai/aetheryte/Aetheryte.chai new file mode 100644 index 00000000..5c48eec7 --- /dev/null +++ b/bin/scripts/chai/aetheryte/Aetheryte.chai @@ -0,0 +1,71 @@ +//Aethernet - Aetheryte + +class AetheryteDef +{ + + def AetheryteDef() + { + this.AetheryteBaseId = 0x50000; + this.AETHERYTE_MENU_AETHERNET = 1; + this.AETHERYTE_MENU_HOUSING = 2; + this.AETHERYTE_MENU_HOME_POINT = 3; + this.AETHERYTE_MENU_FAVORITE_POINT = 4; + this.AETHERYTE_MENU_FAVORITE_POINT_SECURITY_TOKEN = 5; + } + + def onTalk( eventId, player, actorId ) + { + if( player.isAetheryteRegistered( eventId & 0xFFFF ) ) + { + player.eventPlay( eventId, 0, 1, + fun ( player, eventId, param1, param2, param3 ) + { + var aetherId = eventId & 0xFFFF; + + if ( param1 == 256 ) // set homepoint + { + player.setHomepoint( aetherId ); + player.questMessage( eventId, 2, 0xEA, 0, 0); + } + else if ( param1 == 512 && param2 == 4 ) // aethernet + { + player.setTeleporting( true ); + player.teleport( param3, 2 ); + } + + } ); + } + else + { + var ACTION_ATTUNE = 0x13; + player.eventActionStart(eventId, + ACTION_ATTUNE, + // callback function for finished casting + fun ( player, eventId, additional ) + { + var aetherId = eventId & 0xFFFF; + player.aetheryteRegister(aetherId); + + // check if teleport is already unlocked + if( player.isActionLearned( 4 ) ) + { + player.questMessage( eventId, 0, 2, 0, 0); + } + else + { + player.questMessage( eventId, 0, 1, 1, 0); + player.learnAction( 4 ); + } + }, + // callback for interrupted action. + fun ( player, eventId, additional ) + { + }, + 0); + player.unlock(); + } + + } +} + +GLOBAL Aetheryte = AetheryteDef(); \ No newline at end of file diff --git a/bin/scripts/chai/global.inc b/bin/scripts/chai/global.inc new file mode 100644 index 00000000..4ec015ea --- /dev/null +++ b/bin/scripts/chai/global.inc @@ -0,0 +1,226 @@ +///////////////////////////////////////////////////////////////////// +// Quest sequence +///////////////////////////////////////////////////////////////////// +GLOBAL SEQ_COMPLETE = 0xFF + +///////////////////////////////////////////////////////////////////// +// SCENE_FLAGS +///////////////////////////////////////////////////////////////////// +GLOBAL SET_EOBJ_BASE = 0xF8400EF3 +GLOBAL SET_INVIS_BASE = 0xF8400FFF +GLOBAL SET_BASE = 0xF8400EFB +GLOBAL INVIS_COMPANION = 0x80000000 +GLOBAL INVIS_ALLIANCE_BUDDY = 0x40000000 +GLOBAL INVIS_ALLIANCE_PC = 0x20000000 +GLOBAL INVIS_AOE = 0x08000000 +GLOBAL DISABLE_CANCEL_EMOTE = 0x04000000 +GLOBAL LOCK_HOTBAR = 0x02000000 +GLOBAL LOCK_HUD = 0x01000000 +GLOBAL ROLLBACK_HIDE_UI = 0x00800000 +GLOBAL DISABLE_STEALTH = 0x00400000 +GLOBAL HIDE_FESTIVAL = 0x00200000 +GLOBAL DISABLE_SKIP = 0x00080000 +GLOBAL SILENT_ENTER_TERRI_ALL = 0x00038000 +GLOBAL SILENT_ENTER_TERRI_SE = 0x00020000 +GLOBAL SILENT_ENTER_TERRI_BGM = 0x00010000 +GLOBAL SILENT_ENTER_TERRI_ENV = 0x00008000 +GLOBAL INVINCIBLE = 0x00004000 +GLOBAL HIDE_HOTBAR = 0x00002000 +GLOBAL AUTO_LOC_CAMERA = 0x00001000 +GLOBAL INVIS_ALL = 0xF80003FC +GLOBAL HIDE_UI = 0x00000800 +GLOBAL CONDITION_CUTSCENE = 0x00000400 +GLOBAL INVIS_TREASURE = 0x00000200 +GLOBAL INVIS_AETHERYTE = 0x00000100 +GLOBAL INVIS_GATHERING_POINT = 0x00000080 +GLOBAL INVIS_PARTY_BUDDY = 0x10000000 +GLOBAL INVIS_PARTY_PC = 0x00000040 +GLOBAL INVIS_OTHER_PC = 0x00000020 +GLOBAL INVIS_BNPC = 0x00000010 +GLOBAL INVIS_EOBJ = 0x00000008 +GLOBAL INVIS_ENPC = 0x00000004 +GLOBAL FADE_OUT = 0x00000002 +GLOBAL NO_DEFAULT_CAMERA = 0x00000001 +GLOBAL NONE = 0x00000000 + +///////////////////////////////////////////////////////////////////// +// Event Types +///////////////////////////////////////////////////////////////////// +GLOBAL EVENT_TALK = 1 +GLOBAL EVENT_EMOTE = 2 +GLOBAL EVENT_DISTANCE_BELOW = 3 +GLOBAL EVENT_DISTANCE_OVER = 4 +GLOBAL EVENT_BATTLE_REWARD = 5 +GLOBAL EVENT_CRAFT = 6 +GLOBAL EVENT_NEST = 7 +GLOBAL EVENT_EVENT_ITEM = 8 +GLOBAL EVENT_DROP = 9 +GLOBAL EVENT_WITHIN_RANGE = 10 +GLOBAL EVENT_OUTSIDE_RANGE = 11 +GLOBAL EVENT_GAME_START = 12 +GLOBAL EVENT_GAME_PROGRESS = 13 +GLOBAL EVENT_ENTER_TERRITORY = 15 +GLOBAL EVENT_GAME_COME_BACK = 17 +GLOBAL EVENT_ACTION_RESULT = 18 +GLOBAL EVENT_MATERIA_CRAFT = 19 +GLOBAL EVENT_FISHING = 20 +GLOBAL EVENT_UI = 21 +GLOBAL EVENT_HOUSING = 22 +GLOBAL EVENT_SAY = 23 +GLOBAL EVENT_TABLE_GAME = 24 + +///////////////////////////////////////////////////////////////////// +// Event finish states +///////////////////////////////////////////////////////////////////// +GLOBAL UNLOCK = 1 +GLOBAL KEEPLOCK = 0 + +///////////////////////////////////////////////////////////////////// +// Possible Event Types +///////////////////////////////////////////////////////////////////// + +GLOBAL ACTOR_EVENT_ON_CREATE = 1 +GLOBAL ACTOR_EVENT_ON_SPAWN = 2 +GLOBAL ACTOR_EVENT_ON_DESPAWN = 3 +GLOBAL ACTOR_EVENT_ON_AI_TICK = 4 +GLOBAL ACTOR_EVENT_ON_TALK_DEFAULT = 5 + +GLOBAL MOB_EVENT_ON_DEATH = 1 + +GLOBAL REGION_EVENT_ON_CREATE= 1 +GLOBAL REGION_EVENT_ON_ENTER = 2 +GLOBAL REGION_EVENT_ON_LEAVE = 3 + +GLOBAL PLAYER_EVENT_ON_FIRST_ENTER_WORLD = 1 +GLOBAL PLAYER_EVENT_ON_ENTER_WORLD = 2 +GLOBAL PLAYER_EVENT_ON_DEATH = 3 +GLOBAL PLAYER_EVENT_ON_LOGIN = 4 +GLOBAL PLAYER_EVENT_ON_LOGOUT = 5 + +// End Event Types + +GLOBAL SLOT_MAINH = 0 +GLOBAL SLOT_OFFH = 1 +GLOBAL SLOT_HEAD = 2 +GLOBAL SLOT_BODY = 3 +GLOBAL SLOT_GLOVES = 4 +GLOBAL SLOT_WAIST = 5 +GLOBAL SLOT_LEGS = 6 +GLOBAL SLOT_FEET = 7 + +GLOBAL GENDER_MALE = 0 +GLOBAL GENDER_FEMALE = 1 + +GLOBAL ACTION_EVENT_START = 1 +GLOBAL ACTION_EVENT_RETURN = 2 +GLOBAL ACTION_EVENT_TRADE_RETURN = 3 +GLOBAL ACTION_ERANGE_EVENT_START = 4 +GLOBAL ACTION_EVENT_EMOTE = 5 + +GLOBAL ACTION_RETURN = 0x26 +GLOBAL ACTION_TELEPORT = 0x29 + +GLOBAL EVENTACTION_ATTUNE = 0x13 + +GLOBAL CLASS_GLA = 1 +GLOBAL CLASS_PUG = 2 +GLOBAL CLASS_MRD = 3 +GLOBAL CLASS_LNC = 4 +GLOBAL CLASS_ARC = 5 +GLOBAL CLASS_CON = 6 +GLOBAL CLASS_THM = 7 +GLOBAL CLASS_CRP = 8 +GLOBAL CLASS_BSM = 9 +GLOBAL CLASS_ARM = 10 +GLOBAL CLASS_GLD = 11 +GLOBAL CLASS_TAN = 12 +GLOBAL CLASS_WVR = 13 +GLOBAL CLASS_ALC = 14 +GLOBAL CLASS_CUL = 15 +GLOBAL CLASS_MIN = 16 +GLOBAL CLASS_BOT = 17 +GLOBAL CLASS_FSH = 18 +GLOBAL CLASS_PLD = 19 +GLOBAL CLASS_MNK = 20 +GLOBAL CLASS_WAR = 21 +GLOBAL CLASS_DRG = 22 +GLOBAL CLASS_BRD = 23 +GLOBAL CLASS_WHM = 24 +GLOBAL CLASS_BLM = 25 +GLOBAL CLASS_ACN = 26 +GLOBAL CLASS_SMN = 27 +GLOBAL CLASS_SCH = 28 +GLOBAL CLASS_ROG = 29 +GLOBAL CLASS_NIN = 30 +GLOBAL CLASS_MCH = 31 +GLOBAL CLASS_DRK = 32 +GLOBAL CLASS_AST = 33 + +/////////////////////////////////////////////////////////////// +// Inventory Enums +/////////////////////////////////////////////////////////////// +GLOBAL INVENTORY_BAG0 = 0 +GLOBAL INVENTORY_BAG1 = 1 +GLOBAL INVENTORY_BAG2 = 2 +GLOBAL INVENTORY_BAG3 = 3 + +GLOBAL INVENTORY_GEARSET0 = 1000 +GLOBAL INVENTORY_GEARSET1 = 1001 + +GLOBAL INVENTORY_CURRENCY = 2000 +GLOBAL INVENTORY_CRYSTAL = 2001 +//GLOBAL INVENTORY_UNKNOWN_0 = 2003 +GLOBAL INVENTORY_KEYITEM = 2004 +GLOBAL INVENTORY_DAMAGEDGEAR = 2007 +//GLOBAL INVENTORY_UNKNOWN_1 = 2008 + +GLOBAL INVENTORY_ARMORYOFF = 3200 +GLOBAL INVENTORY_ARMORYHEAD = 3201 +GLOBAL INVENTORY_ARMORYBODY = 3202 +GLOBAL INVENTORY_ARMORYHAND = 3203 +GLOBAL INVENTORY_ARMORYWAIST = 3204 +GLOBAL INVENTORY_ARMORYLEGS = 3205 +GLOBAL INVENTORY_ARMORYFEET = 3206 +GLOBAL INVENTORY_ARMOTYNECK = 3207 +GLOBAL INVENTORY_ARMORYEAR = 3208 +GLOBAL INVENTORY_ARMORYWRIST = 3209 +GLOBAL INVENTORY_ARMORYRING = 3300 + +GLOBAL INVENTORY_ARMORYSOULCRYSTAL = 3400 +GLOBAL INVENTORY_ARMORYMAIN = 3500 + +GLOBAL INVENTORY_RETAINERBAG0 = 10000 +GLOBAL INVENTORY_RETAINERBAG1 = 10001 +GLOBAL INVENTORY_RETAINERBAG2 = 10002 +GLOBAL INVENTORY_RETAINERBAG3 = 10003 +GLOBAL INVENTORY_RETAINERBAG4 = 10004 +GLOBAL INVENTORY_RETAINERBAG5 = 10005 +GLOBAL INVENTORY_RETAINERBAG6 = 10006 +GLOBAL INVENTORY_RETAINEREQUIPPEDGEAR = 11000 +GLOBAL INVENTORY_RETAINERGIL = 12000 +GLOBAL INVENTORY_RETAINERCRYSTAL = 12001 +GLOBAL INVENTORY_RETAINERMARKET = 12002 + +GLOBAL INVENTORY_FREECOMPANYBAG0 = 20000 +GLOBAL INVENTORY_FREECOMPANYBAG1 = 20001 +GLOBAL INVENTORY_FREECOMPANYBAG2 = 20002 +GLOBAL INVENTORY_FREECOMPANYGIL = 22000 +GLOBAL INVENTORY_FREECOMPANYCRYSTAL = 22001 + +//////////////////////////////////////////////////////////// +// Currency types +//////////////////////////////////////////////////////////// +GLOBAL CURRENCY_GIL = 0X01 +GLOBAL CURRENCY_STORMSEAL = 0X02 +GLOBAL CURRENCY_SERPENTSEAL = 0X03 +GLOBAL CURRENCY_FLAMESEAL = 0X04 +GLOBAL CURRENCY_TOMESTONEPHILO = 0X05 +GLOBAL CURRENCY_TOMESTONEMYTHO = 0X06 +GLOBAL CURRENCY_WOLFMARK = 0X07 +GLOBAL CURRENCY_TOMESTONESOLD = 0X08 +GLOBAL CURRENCY_ALLIEDSEAL = 0X09 +GLOBAL CURRENCY_TOMESTONEPOET = 0X0A +GLOBAL CURRENCY_MGP = 0X0B +GLOBAL CURRENCY_TOMESTONELAW = 0X0C +GLOBAL CURRENCY_TOMESTONEESO = 0X0D +GLOBAL CURRENCY_TOMESTONELORE = 0X0E diff --git a/bin/scripts/chai/opening/OpeningGridania.chai b/bin/scripts/chai/opening/OpeningGridania.chai new file mode 100644 index 00000000..9dac7aed --- /dev/null +++ b/bin/scripts/chai/opening/OpeningGridania.chai @@ -0,0 +1,97 @@ +// Opening Script: OpeningGridania +// Quest Name: OpeningGridania +// Quest ID: 1245186 + +class OpeningGridaniaDef +{ + + def OpeningGridaniaDef() + { + this.id = 1245186; + this.name = "OpeningGridania"; + + this.ERANGE_HOWTO_ANN_AND_QUEST = 2117539; + this.ERANGE_HOWTO_QUEST_REWARD = 2366417; + this.ERANGE_SEQ_1_CLOSED_1 = 2351918; + this.POS_SEQ_1_CLOSED_RETURN_1 = 2351921; + this.ERANGE_SEQ_1_CLOSED_2 = 2351919; + this.POS_SEQ_1_CLOSED_RETURN_2 = 2351921; + this.ERANGE_ALWAYS_CLOSED_1 = 2280846; + this.POS_ALWAYS_CLOSED_RETURN_1 = 2320804; + this.ENPC_ALWAYS_CLOSED_1 = 2367988; + this.ERANGE_ALWAYS_CLOSED_3 = 2280851; + this.POS_ALWAYS_CLOSED_RETURN_3 = 2320811; + this.ENPC_ALWAYS_CLOSED_3 = 2563491; + this.BGM_MUSIC_ZONE_FST_TWN = 1003; + this.NCUT_FST_1 = 3; + this.NCUT_FST_2 = 53; + this.NCUT_FST_3 = 226; + this.ENPC_QUEST_OFFER = 1985150; + this.NCUT_LIGHT_ALL = 2; + this.NCUT_LIGHT_FST_1 = 147; + this.NCUT_LIGHT_FST_2 = 146; + } + + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2001, 0, 1, + fun( player, eventId, param1, param2, param3 ) + { + player.setFirstLogin( false ); + OpeningGridania.Scene00001( player ); + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x2001, 1, 0x32 ); + } + + def Scene00020( player ) + { + player.eventPlay( this.id, 20, 0x2001, 0, 1 ); + } + + def Scene00030( player ) + { + player.eventPlay( this.id, 30, 0x2001, 0, 0 ); + } + + def Scene00040( player ) + { + player.eventPlay( this.id, 40, 1, 2, 1, + fun( player, eventId, param1, param2, param3 ) + { + if( player.hasQuest( ManFst001.id ) ) + { + // update the instance boundaries + OpeningGridania.Scene00030( player ); + } + } ); + } + + ////////////////////////////// EVENT HANDLERS ////////////////////////////////// + + def onEnterTerritory( eventId, player, param1, param2) + { + if( player.isFirstLogin() == true ) + { + this.Scene00000( player ); + } + else + { + this.Scene00040( player ); + } + } + + def onWithinRange( eventId, player, param1, x, y, z) + { + if( this.ERANGE_ALWAYS_CLOSED_3 == param1 ) + { + this.Scene00020( player ); + } + } + +}; + +GLOBAL OpeningGridania = OpeningGridaniaDef(); diff --git a/bin/scripts/chai/opening/OpeningLimsa.chai b/bin/scripts/chai/opening/OpeningLimsa.chai new file mode 100644 index 00000000..d97977dc --- /dev/null +++ b/bin/scripts/chai/opening/OpeningLimsa.chai @@ -0,0 +1,108 @@ +// Opening Script: OpeningLimsaLominsa +// Quest Name: OpeningLimsaLominsa +// Quest ID: 1245185 + +class OpeningLimsaLominsaDef +{ + + def OpeningLimsaLominsaDef() + { + this.id = 1245185; + this.name = "OpeningLimsaLominsa"; + + this.ERANGE_HOWTO_ANN_AND_QUEST = 4101831 + this.ERANGE_HOWTO_QUEST_REWARD = 4102066 + this.ERANGE_SEQ_1_CLOSED_1 = 4101785 + this.POS_SEQ_1_CLOSED_RETURN_1 = 4101797 + this.ERANGE_ALWAYS_CLOSED_1 = 4101744 + this.POS_ALWAYS_CLOSED_RETURN_1 = 4101761 + this.ENPC_ALWAYS_CLOSED_1 = 4102038 + this.ERANGE_ALWAYS_CLOSED_2 = 4101746 + this.POS_ALWAYS_CLOSED_RETURN_2 = 4101763 + this.ENPC_ALWAYS_CLOSED_2 = 4102036 + this.ERANGE_ALWAYS_CLOSED_3 = 4101967 + this.POS_ALWAYS_CLOSED_RETURN_3 = 4101982 + this.ENPC_ALWAYS_CLOSED_3 = 4102033 + this.ERANGE_ALWAYS_CLOSED_4 = 4101970 + this.POS_ALWAYS_CLOSED_RETURN_4 = 4101984 + this.ENPC_ALWAYS_CLOSED_4 = 4102031 + this.ERANGE_ALWAYS_CLOSED_5 = 4101973 + this.POS_ALWAYS_CLOSED_RETURN_5 = 4101985 + this.ENPC_ALWAYS_CLOSED_5 = 4102007 + this.ERANGE_ALWAYS_CLOSED_6 = 4101979 + this.POS_ALWAYS_CLOSED_RETURN_6 = 4101988 + this.ENPC_ALWAYS_CLOSED_6 = 2367400 + this.BGM_MUSIC_ZONE_SEA_TWN = 1020 + this.NCUT_SEA_1 = 200 + this.NCUT_SEA_2 = 132 + this.NCUT_SEA_3 = 201 + this.ENPC_QUEST_OFFER = 4102039 + this.NCUT_LIGHT_ALL = 2 + this.NCUT_LIGHT_SEA_1 = 147 + this.NCUT_LIGHT_SEA_2 = 138 + } + + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x04AC05, 0, 1, + fun( player, eventId, param1, param2, param3 ) + { + player.setFirstLogin( false ); + OpeningLimsaLominsa.Scene00001( player ); + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x2001, 1, 0x32 ); + } + + def Scene00020( player ) + { + player.eventPlay( this.id, 20, 0x2001, 0, 1 ); + } + + def Scene00030( player ) + { + player.eventPlay( this.id, 30, 0x2001, 0, 0 ); + } + + def Scene00040( player ) + { + player.eventPlay( this.id, 40, 1, 2, 1, + fun( player, eventId, param1, param2, param3 ) + { + player.eventFinish( eventId, UNLOCK ); + if( player.hasQuest( ManSea001.id ) ) + { + // update the instance boundaries + OpeningLimsaLominsa.Scene00030( player ); + } + } ); + } + + ////////////////////////////// EVENT HANDLERS ////////////////////////////////// + + def onEnterTerritory( eventId, player, param1, param2) + { + // if( player.isFirstLogin() == true ) + // { + this.Scene00000( player ); + // } + //else + // { + // this.Scene00040( player ); + // } + } + + def onWithinRange( eventId, player, param1, x, y, z) + { + if( this.ERANGE_SEQ_1_CLOSED_1 == param1 ) + { + this.Scene00020( player ); + } + } + +}; + +GLOBAL OpeningLimsaLominsa = OpeningLimsaLominsaDef(); diff --git a/bin/scripts/chai/opening/OpeningUldah.chai b/bin/scripts/chai/opening/OpeningUldah.chai new file mode 100644 index 00000000..da6b24be --- /dev/null +++ b/bin/scripts/chai/opening/OpeningUldah.chai @@ -0,0 +1,98 @@ +// Opening Script: OpeningUldah +// Quest Name: OpeningUldah +// Quest ID: 1245187 + +class OpeningUldahDef +{ + + def OpeningUldahDef() + { + this.id = 1245187; + this.name = "OpeningUldah"; + + this.ERANGE_HOWTO_ANN_AND_QUEST = 4101650; + this.ERANGE_HOWTO_QUEST_REWARD = 4102883; + this.ERANGE_SEQ_1_CLOSED_1 = 4101587; + this.POS_SEQ_1_CLOSED_RETURN_1 = 4101691; + this.ERANGE_ALWAYS_CLOSED_1 = 4101537; + this.POS_ALWAYS_CLOSED_RETURN_1 = 4101685; + this.ENPC_ALWAYS_CLOSED_1 = 4101796; + this.ERANGE_ALWAYS_CLOSED_2 = 4101525; + this.POS_ALWAYS_CLOSED_RETURN_2 = 4101680; + this.ENPC_ALWAYS_CLOSED_2 = 4101789; + this.ERANGE_ALWAYS_CLOSED_3 = 4101535; + this.POS_ALWAYS_CLOSED_RETURN_3 = 4101681; + this.ENPC_ALWAYS_CLOSED_3 = 4101787; + this.BGM_MUSIC_ZONE_WIL_TWN = 1035; + this.NCUT_WIL_1 = 186; + this.NCUT_WIL_2 = 139; + this.NCUT_WIL_3 = 187; + this.ENPC_QUEST_OFFER = 3969639; + this.NCUT_LIGHT_ALL = 2; + this.NCUT_LIGHT_WIL_1 = 147; + this.NCUT_LIGHT_WIL_2 = 145; + } + + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2001, 0, 1, + fun( player, eventId, param1, param2, param3 ) + { + player.setFirstLogin( false ); + OpeningUldah.Scene00001( player ); + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x2001, 1, 0x32 ); + } + + def Scene00020( player ) + { + player.eventPlay( this.id, 20, 0x2001, 0, 1 ); + } + + def Scene00030( player ) + { + player.eventPlay( this.id, 30, 0x2001, 0, 0 ); + } + + def Scene00040( player ) + { + player.eventPlay( this.id, 40, 1, 2, 1, + fun( player, eventId, param1, param2, param3 ) + { + if( player.hasQuest( ManWil001.id ) ) + { + // update the instance boundaries + OpeningUldah.Scene00030( player ); + } + } ); + } + + ////////////////////////////// EVENT HANDLERS ////////////////////////////////// + + def onEnterTerritory( eventId, player, param1, param2) + { + if( player.isFirstLogin() == true ) + { + this.Scene00000( player ); + } + else + { + this.Scene00040( player ); + } + } + + def onOutsideRange( eventId, player, param1, x, y, z) + { + if( this.ERANGE_SEQ_1_CLOSED_1 == param1 ) + { + this.Scene00020( player ); + } + } + +}; + +GLOBAL OpeningUldah = OpeningUldahDef(); diff --git a/bin/scripts/chai/player_events.chai b/bin/scripts/chai/player_events.chai new file mode 100644 index 00000000..c6877101 --- /dev/null +++ b/bin/scripts/chai/player_events.chai @@ -0,0 +1,11 @@ + +def onFirstEnterWorld(player) +{ + var name = player.getName(); + var race = player.getRace(); + var gender = player.getGender(); + //var class = player.getClass(); + + + return name; +} diff --git a/bin/scripts/chai/quest/ManFst001.chai b/bin/scripts/chai/quest/ManFst001.chai new file mode 100644 index 00000000..d8ea1ebf --- /dev/null +++ b/bin/scripts/chai/quest/ManFst001.chai @@ -0,0 +1,124 @@ +// Quest Script: ManFst001_00039 +// Quest Name: Coming to Gridania +// Quest ID: 65575 +// Start NPC: 1001148 +// End NPC: 1001140 + +class ManFst001Def +{ + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManFst001Def() + { + // Basic quest information + this.name = "Coming to Gridania"; + this.id = 65575; + // These are the quest vars / flags used in this quest + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 103; + + // Entities found in the script data of the quest + // some of these may be useful + this.ACTOR0 = 1001148; + this.ACTOR1 = 1001140; + this.CUT_EVENT = 29; + this.EOBJECT0 = 2001659; + this.EOBJECT1 = 2001660; + this.EOBJECT7 = 2616477; + this.EVENT_ACTION_SEARCH = 1; + this.HOWTO_QUEST_ACCEPT = 12; + this.HOWTO_QUEST_ANNOUNCE = 2; + this.HOWTO_REWARD = 11; + this.HOWTO_TODO = 3; + this.OPENING_EVENT_HANDLER = 1245186; + this.SEQ_2_ACTOR1 = 2; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + ManFst001.Scene00001( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0xF8482EFB/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + ManFst001.Scene00002( player ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( ManFst001.id, ManFst001.SEQ_FINISH );// add quest to player. + + // this is an external event, so we do need to start it here + //player.eventStart( player.getId(), ManFst001.OPENING_EVENT_HANDLER, 7, 0, 0 ); // update the instance boundaries, call to the opening event + player.eventPlay( ManFst001.OPENING_EVENT_HANDLER, 0x1E, 0x2001, 0, 0 ); + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0x2c02, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst001.Scene00005( player ); + }); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, 0x20/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // clicked finish button + { + player.gainExp( 50 ); + player.addCurrency( 1, ManFst001.RewardGil ); + player.questFinish( ManFst001.id ); + } + }); + } + + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + this.Scene00004( player ); + } + + // Script content to be added here.... + } +}; + +GLOBAL ManFst001 = ManFst001Def(); + + diff --git a/bin/scripts/chai/quest/ManFst002.chai b/bin/scripts/chai/quest/ManFst002.chai new file mode 100644 index 00000000..6dda96a1 --- /dev/null +++ b/bin/scripts/chai/quest/ManFst002.chai @@ -0,0 +1,261 @@ +// Quest Script: ManFst002_00124 +// Quest Name: Close to Home +// Quest ID: 65660 +// Start NPC: 1001140 +// End NPC: 1000100 + +class ManFst002Def +{ + + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManFst002Def() + { + // Basic quest information + this.name = "Close to Home"; + this.id = 65621; + + // These are the quest vars / flags used in this quest + // GetQuestBitFlag8 + // GetQuestUI8AL -- attube to Aetheryte + // GetQuestUI8BH -- visit class guild + // GetQuestUI8BL -- Listen to Parsement in the market check mark + // GetQuestUI8CH -- recieve Key item + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + //this.SEQ_OFFER = ?; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardGil = 107; + + // Entities found in the script data of the quest + // some of these may be useful + this.ACTOR0 = 1001140; + this.ACTOR1 = 2; + this.ACTOR2 = 1000251; + this.ACTOR20 = 1000159; + this.ACTOR3 = 1000768; + this.ACTOR4 = 1000100; + this.BIND_ACTOR0 = 6229224; + this.HOW_TO_DESION = 13; + this.HOW_TO_MAP_AND_NAVI = 4; + this.ITEM0 = 2000074; + this.LOC_ACTOR0 = 1003159; + this.LOC_MARKER_01 = 2153091; + this.LOC_MARKER_02 = 2153104; + this.LOC_MARKER_03 = 2153111; + this.LOC_MARKER_04 = 2154539; + this.LOC_MARKER_05 = 2154540; + this.LOC_MARKER_06 = 2154541; + this.LOC_MARKER_07 = 2210446; + this.LOC_MARKER_08 = 2210454; + this.LOC_MARKER_09 = 2210461; + this.LOC_MOTION0 = 799; + this.POPRANGE0 = 2280858; + this.REWARD_DESION = 1; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_0_ACTOR0_LQ = 50; + this.SEQ_1_ACTOR0 = 4; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_WAIT = 51; + this.SEQ_1_ACTOR2 = 2; + this.SEQ_1_ACTOR3 = 3; + this.SEQ_1_ACTOR3_NPCTRADENO = 99; + this.SEQ_1_ACTOR3_NPCTRADEOK = 100; + this.SEQ_2_ACTOR4 = 5; + this.TERRITORYTYPE0 = 132; + this.UNLOCK_DESION = 14; + + } + + def checkQuestCompletion( player, varIdx ) + { + + if (varIdx == 3) + { + player.questMessage(this.id, 1, 0, 0, 0 ); + } + else if (varIdx == 2) + { + player.questMessage(this.id, 2, 0, 0, 0 ); + } + else + { + player.questMessage(this.id, 0, 0, 0, 0 ); + } + + var QUEST_VAR_ATTUNE = player.getQuestUI8AL( this.id ); + var QUEST_VAR_CLASS = player.getQuestUI8BH( this.id ); + var QUEST_VAR_TRADE = player.getQuestUI8BL( this.id ); + + if ( QUEST_VAR_ATTUNE == 1 && QUEST_VAR_CLASS == 1 && QUEST_VAR_TRADE == 1 ) + { + player.questUpdate( this.id, this.SEQ_FINISH ); + } + } + + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + ManFst002.Scene00050( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x0EFB/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8AL( ManFst002.id, 1 ); + ManFst002.checkQuestCompletion( player, 0 ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8BH( ManFst002.id, 1 ); + ManFst002.checkQuestCompletion( player, 3 ); + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if ( param2 == 1 ) + { + ManFst002.Scene00100( player ); + } + else + { + ManFst002.Scene00099( player ); + } + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0, 0, 0 ); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( ManFst002.id, 0 ) ) + { + player.questFinish( ManFst002.id ); + } + } + }); + } + + def Scene00050( player ) + { + player.eventPlay( this.id, 50, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + // accepting quest "close to home" + player.questUpdate( ManFst002.id, 1 ); + player.setQuestUI8CH( ManFst002.id, 1 ); // receive key item + // event is done, need to teleport to real zone. + player.setZone( 132 ); + //player.setZone(183); back to starting griania for debug purpose + }); + } + + def Scene00051( player ) + { + player.eventPlay( this.id, 51, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst002.Scene00001( player ); + }); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst002.Scene00004( player ); + }); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, 0x0EFB, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8CH( ManFst002.id, 0 ); // remove key item, since we have just traded it + player.setQuestUI8BL( ManFst002.id, 1 ); + ManFst002.checkQuestCompletion(player, 2 ); + }); + } + + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + + // starting the eventaction 0x13 ( attuning ) + player.eventActionStart( 0x050002, 0x13, + fun( player, eventId, additional ) + { + player.questMessage( 0x050002, 0, 1, 0, 0 ); + player.aetheryteRegister( 2 ); + player.learnAction( 1 ); + ManFst002.Scene00051( player ); + }, + fun( player, eventId, additional ) + { + player.eventFinish( 0x050002, 0 ); + player.eventFinish( additional, 0 ); + }, + 0x050002 ); + } + else if( actor == this.ACTOR2 ) + { + this.Scene00002( player ); + } + else if( actor == this.ACTOR3 ) + { + this.Scene00003( player ); + } + else if( actor == this.ACTOR4 ) + { + this.Scene00005( player ); + } + + } + +}; + +GLOBAL ManFst002 = ManFst002Def(); + diff --git a/bin/scripts/chai/quest/ManFst003.chai b/bin/scripts/chai/quest/ManFst003.chai new file mode 100644 index 00000000..dfdde4a0 --- /dev/null +++ b/bin/scripts/chai/quest/ManFst003.chai @@ -0,0 +1,259 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: ManFst003_00123 +// Quest Name: Close to Home +// Quest ID: 65659 +// Start NPC: 1001140 +// End NPC: 1000100 + +class ManFst003Def +{ + def ManFst003Def() + { + // Basic quest information + this.name = "Close to Home"; + this.id = 65659; + + // Quest vars / flags used + // GetQuestBitFlag8 + // GetQuestUI8AL + // GetQuestUI8BH + // GetQuestUI8BL + // GetQuestUI8CH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardGil = 107; + + // Entities found in the script data of the quest + this.ACTOR0 = 1001140; + this.ACTOR1 = 2; + this.ACTOR2 = 1000197; + this.ACTOR20 = 1000159; + this.ACTOR3 = 1000768; + this.ACTOR4 = 1000100; + this.BIND_ACTOR0 = 6229224; + this.ITEM0 = 2000119; + this.LOC_ACTOR0 = 1003159; + this.LOC_MARKER_01 = 2153091; + this.LOC_MARKER_02 = 2153104; + this.LOC_MARKER_03 = 2153111; + this.LOC_MARKER_04 = 2154539; + this.LOC_MARKER_05 = 2154540; + this.LOC_MARKER_06 = 2154541; + this.LOC_MARKER_07 = 2210446; + this.LOC_MARKER_08 = 2210454; + this.LOC_MARKER_09 = 2210461; + this.LOC_MOTION0 = 799; + this.POPRANGE0 = 2280858; + this.REWARD_DESION = 1; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_0_ACTOR0_LQ = 50; + this.SEQ_1_ACTOR0 = 4; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_WAIT = 51; + this.SEQ_1_ACTOR2 = 2; + this.SEQ_1_ACTOR3 = 3; + this.SEQ_1_ACTOR3_NPCTRADENO = 99; + this.SEQ_1_ACTOR3_NPCTRADEOK = 100; + this.SEQ_2_ACTOR4 = 5; + this.TERRITORYTYPE0 = 132; + this.UNLOCK_DESION = 14; + + } + + def checkQuestCompletion( player, varIdx ) + { + + print( varIdx ); + if (varIdx == 3) + { + player.questMessage(this.id, 1, 0, 0, 0 ); + } + else if (varIdx == 2) + { + player.questMessage(this.id, 2, 0, 0, 0 ); + } + else + { + player.questMessage(this.id, 0, 0, 0, 0 ); + } + + var QUEST_VAR_ATTUNE = player.getQuestUI8AL( this.id ); + var QUEST_VAR_CLASS = player.getQuestUI8BH( this.id ); + var QUEST_VAR_TRADE = player.getQuestUI8BL( this.id ); + + if ( QUEST_VAR_ATTUNE == 1 && QUEST_VAR_CLASS == 1 && QUEST_VAR_TRADE == 1 ) + { + player.questUpdate( this.id, this.SEQ_FINISH ); + } + } + + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + ManFst003.Scene00050( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x0EFB/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8AL( ManFst004.id, 1 ); + ManFst003.checkQuestCompletion( player, 1 ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8BH( ManFst004.id, 1 ); + ManFst003.checkQuestCompletion( player, 3 ); + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if ( param2 == 1 ) + { + ManFst003.Scene00100( player ); + } + else + { + ManFst003.Scene00099( player ); + } + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0, 0, 0 ); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( ManFst003.id, 0 ) ) + { + player.questFinish( ManFst003.id ); + } + } + + }); + } + + def Scene00050( player ) + { + player.eventPlay( this.id, 50, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + // accepting quest "close to home" + player.questUpdate( ManFst003.id, 1 ); + player.setQuestUI8CH( ManFst003.id, 1 ); // receive key item + // event is done, need to teleport to real zone. + player.setZone( 132 ); + //player.setZone(183); back to starting griania for debug purpose + }); + } + + def Scene00051( player ) + { + player.eventPlay( this.id, 51, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst003.Scene00001( player ); + }); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst003.Scene00004( player ); + }); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, 0x0EFB, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8CH( ManFst004.id, 0 ); // remove key item, since we have just traded it + player.setQuestUI8BL( ManFst004.id, 1 ); + ManFst003.checkQuestCompletion(player, 2 ); + }); + } + + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + // starting the Aetheryte eventaction + // player.eventStart( actorId, 0x050002, 7, 0, 0); + // starting the eventaction 0x13 ( attuning ) + player.eventActionStart( 0x050002, 0x13, + fun( player, eventId, additional ) + { + + player.questMessage( 0x050002, 0, 1, 0, 0 ); + player.aetheryteRegister( 2 ); + player.learnAction( 1 ); + ManFst003.Scene00051( player ); + }, + fun( player, eventId, additional ) {}, + eventId ); + } + else if( actor == this.ACTOR2 ) + { + this.Scene00002( player ); + } + else if( actor == this.ACTOR3 ) + { + this.Scene00003( player ); + } + else if( actor == this.ACTOR4 ) + { + this.Scene00005( player ); + } + + } + +}; + +GLOBAL ManFst003 = ManFst003Def(); + diff --git a/bin/scripts/chai/quest/ManFst003.chai_generated b/bin/scripts/chai/quest/ManFst003.chai_generated new file mode 100644 index 00000000..e893e598 --- /dev/null +++ b/bin/scripts/chai/quest/ManFst003.chai_generated @@ -0,0 +1,246 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai + + +// Quest Script: ManFst003_00123 +// Quest Name: Close to Home +// Quest ID: 65659 +// Start NPC: 1001140 +// End NPC: 1000100 + +class ManFst003 +{ + var name; + var id; + var SEQ_0; + var SEQ_1; + var SEQ_FINISH; + var SEQ_OFFER; + var RewardExpFactor; + var RewardGil; + var ACTOR0; + var SEQ_0_ACTOR0; + var ITEM0; + var ACTOR1; + var SEQ_1_ACTOR1; + var ACTOR2; + var SEQ_1_ACTOR2; + var ACTOR3; + var SEQ_1_ACTOR3; + var SEQ_1_ACTOR3_NPCTRADEOK; + var SEQ_1_ACTOR3_NPCTRADENO; + var SEQ_1_ACTOR0; + var ACTOR4; + var SEQ_2_ACTOR4; + var TERRITORYTYPE0; + var POPRANGE0; + var LOC_MARKER_01; + var LOC_MARKER_02; + var LOC_MARKER_03; + var LOC_MARKER_04; + var LOC_MARKER_05; + var LOC_MARKER_06; + var LOC_MARKER_07; + var LOC_MARKER_08; + var LOC_MARKER_09; + var SEQ_0_ACTOR0_LQ; + var UNLOCK_DESION; + var REWARD_DESION; + var HOW_TO_MAP_AND_NAVI; + var HOW_TO_DESION; + var ACTOR20; + var SEQ_1_ACTOR1_WAIT; + var LOC_ACTOR0; + var BIND_ACTOR0; + var LOC_MOTION0; + + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManFst003() + { + // Basic quest information + this.name = "Close to Home"; + this.id = 65659; + // These are the quest vars / flags used in this quest + // GetQuestBitFlag8 + // GetQuestUI8AL + // GetQuestUI8BH + // GetQuestUI8BL + // GetQuestUI8CH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + //this.SEQ_OFFER = ?; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardGil = 107; + + // Entities found in the script data of the quest + // some of these may be useful + this.ACTOR0 = 1001140; + this.ACTOR1 = 2; + this.ACTOR2 = 1000197; + this.ACTOR20 = 1000159; + this.ACTOR3 = 1000768; + this.ACTOR4 = 1000100; + this.BIND_ACTOR0 = 6229224; + this.HOW_TO_DESION = 13; + this.HOW_TO_MAP_AND_NAVI = 4; + this.ITEM0 = 2000119; + this.LOC_ACTOR0 = 1003159; + this.LOC_MARKER_01 = 2153091; + this.LOC_MARKER_02 = 2153104; + this.LOC_MARKER_03 = 2153111; + this.LOC_MARKER_04 = 2154539; + this.LOC_MARKER_05 = 2154540; + this.LOC_MARKER_06 = 2154541; + this.LOC_MARKER_07 = 2210446; + this.LOC_MARKER_08 = 2210454; + this.LOC_MARKER_09 = 2210461; + this.LOC_MOTION0 = 799; + this.POPRANGE0 = 2280858; + this.REWARD_DESION = 1; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_0_ACTOR0_LQ = 50; + this.SEQ_1_ACTOR0 = 4; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_WAIT = 51; + this.SEQ_1_ACTOR2 = 2; + this.SEQ_1_ACTOR3 = 3; + this.SEQ_1_ACTOR3_NPCTRADENO = 99; + this.SEQ_1_ACTOR3_NPCTRADEOK = 100; + this.SEQ_2_ACTOR4 = 5; + this.TERRITORYTYPE0 = 132; + this.UNLOCK_DESION = 14; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player, eventId ) + { + player.eventPlay( eventId, 0, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00001( player, eventId ) + { + player.eventPlay( eventId, 1, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00002( player, eventId ) + { + player.eventPlay( eventId, 2, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00003( player, eventId ) + { + player.eventPlay( eventId, 3, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00004( player, eventId ) + { + player.eventPlay( eventId, 4, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00005( player, eventId ) + { + player.eventPlay( eventId, 5, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00050( player, eventId ) + { + player.eventPlay( eventId, 50, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00051( player, eventId ) + { + player.eventPlay( eventId, 51, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00099( player, eventId ) + { + player.eventPlay( eventId, 99, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00100( player, eventId ) + { + player.eventPlay( eventId, 100, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest = ManFst003(); + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + +}; + +////////////////////////////////////////////////////////////////////// +// Entry function for this quest +def ManFst003_START(player, actorId, eventId) +{ + var quest = ManFst003(); + var actor = mapActor( actorId ); + + // Script content to be added here.... +} + diff --git a/bin/scripts/chai/quest/ManFst004.chai b/bin/scripts/chai/quest/ManFst004.chai new file mode 100644 index 00000000..a36effde --- /dev/null +++ b/bin/scripts/chai/quest/ManFst004.chai @@ -0,0 +1,258 @@ +// Quest Script: ManFst004_00124 +// Quest Name: Close to Home +// Quest ID: 65660 +// Start NPC: 1001140 +// End NPC: 1000100 + +class ManFst004Def +{ + + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManFst004Def() + { + // Basic quest information + this.name = "Close to Home"; + this.id = 65660; + + // These are the quest vars / flags used in this quest + // GetQuestBitFlag8 + // GetQuestUI8AL -- attube to Aetheryte + // GetQuestUI8BH -- visit class guild + // GetQuestUI8BL -- Listen to Parsement in the market check mark + // GetQuestUI8CH -- recieve Key item + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + //this.SEQ_OFFER = ?; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardGil = 107; + + // Entities found in the script data of the quest + // some of these may be useful + this.ACTOR0 = 1001140; + this.ACTOR1 = 2; + this.ACTOR2 = 1000323; + this.ACTOR20 = 1000159; + this.ACTOR3 = 1000768; + this.ACTOR4 = 1000100; + this.BIND_ACTOR0 = 6229224; + this.ITEM0 = 2000120; + this.LOC_ACTOR0 = 1003159; + this.LOC_MARKER_01 = 2153091; + this.LOC_MARKER_02 = 2153104; + this.LOC_MARKER_03 = 2153111; + this.LOC_MARKER_04 = 2154539; + this.LOC_MARKER_05 = 2154540; + this.LOC_MARKER_06 = 2154541; + this.LOC_MARKER_07 = 2210446; + this.LOC_MARKER_08 = 2210454; + this.LOC_MARKER_09 = 2210461; + this.LOC_MOTION0 = 799; + this.POPRANGE0 = 2280858; + this.REWARD_DESION = 1; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_0_ACTOR0_LQ = 50; + this.SEQ_1_ACTOR0 = 4; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_WAIT = 51; + this.SEQ_1_ACTOR2 = 2; + this.SEQ_1_ACTOR3 = 3; + this.SEQ_1_ACTOR3_NPCTRADENO = 99; + this.SEQ_1_ACTOR3_NPCTRADEOK = 100; + this.SEQ_2_ACTOR4 = 5; + this.TERRITORYTYPE0 = 132; + this.UNLOCK_DESION = 14; + + } + + def checkQuestCompletion( player, varIdx ) + { + + if (varIdx == 3) + { + player.questMessage(this.id, 1, 0, 0, 0 ); + } + else if (varIdx == 2) + { + player.questMessage(this.id, 2, 0, 0, 0 ); + } + else + { + player.questMessage(this.id, 0, 0, 0, 0 ); + } + + var QUEST_VAR_ATTUNE = player.getQuestUI8AL( this.id ); + var QUEST_VAR_CLASS = player.getQuestUI8BH( this.id ); + var QUEST_VAR_TRADE = player.getQuestUI8BL( this.id ); + + if ( QUEST_VAR_ATTUNE == 1 && QUEST_VAR_CLASS == 1 && QUEST_VAR_TRADE == 1 ) + { + player.questUpdate( this.id, this.SEQ_FINISH ); + } + } + + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + ManFst004.Scene00050( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0x0EFB/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8AL( ManFst004.id, 1 ); + ManFst004.checkQuestCompletion( player, 0 ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8BH( ManFst004.id, 1 ); + ManFst004.checkQuestCompletion( player, 3 ); + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if ( param2 == 1 ) + { + ManFst004.Scene00100( player ); + } + else + { + ManFst004.Scene00099( player ); + } + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0, 0, 0 ); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( ManFst004.id, 0 ) ) + { + player.questFinish( ManFst004.id ); + } + } + + }); + } + + def Scene00050( player ) + { + player.eventPlay( this.id, 50, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + // accepting quest "close to home" + player.questUpdate( ManFst004.id, 1 ); + player.setQuestUI8CH( ManFst004.id, 1 ); // receive key item + // event is done, need to teleport to real zone. + player.setZone( 132 ); + //player.setZone(183); back to starting griania for debug purpose + }); + } + + def Scene00051( player ) + { + player.eventPlay( this.id, 51, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst004.Scene00001( player ); + }); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManFst004.Scene00004( player ); + }); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, 0x0EFB, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8CH( ManFst004.id, 0 ); // remove key item, since we have just traded it + player.setQuestUI8BL( ManFst004.id, 1 ); + ManFst004.checkQuestCompletion(player, 2 ); + }); + } + + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + // starting the Aetheryte eventaction + // player.eventStart( actorId, 0x050002, 7, 0, 0); + // starting the eventaction 0x13 ( attuning ) + player.eventActionStart( 0x050002, 0x13, + fun( player, eventId, additional ) + { + + player.questMessage( 0x050002, 0, 1, 0, 0 ); + player.aetheryteRegister( 2 ); + player.learnAction( 1 ); + ManFst004.Scene00051( player ); + }, + fun( player, eventId, additional ) {}, + eventId ); + } + else if( actor == this.ACTOR2 ) + { + this.Scene00002( player ); + } + else if( actor == this.ACTOR3 ) + { + this.Scene00003( player ); + } + else if( actor == this.ACTOR4 ) + { + this.Scene00005( player ); + } + + } + +}; + +GLOBAL ManFst004 = ManFst004Def(); + diff --git a/bin/scripts/chai/quest/ManSea001.chai b/bin/scripts/chai/quest/ManSea001.chai new file mode 100644 index 00000000..75f742d9 --- /dev/null +++ b/bin/scripts/chai/quest/ManSea001.chai @@ -0,0 +1,201 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: ManSea001_00107 +// Quest Name: Coming to Limsa Lominsa +// Quest ID: 65643 +// Start NPC: 1001028 +// End NPC: 1002697 + +class ManSea001Def +{ + def ManSea001Def() + { + // Basic quest information + this.name = "Coming to Limsa Lominsa"; + this.id = 65643; + + // Quest vars / flags used + // GetQuestBitFlag8 + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 103; + + // Entities found in the script data of the quest + this.ACTOR0 = 1001028; + this.ACTOR1 = 1002732; + this.ACTOR2 = 1002697; + this.CUT_EVENT = 202; + this.EOBJECT0 = 2001679; + this.EOBJECT1 = 2001680; + this.EVENT_ACTION_SEARCH = 1; + this.LOC_ACTOR0 = 1002732; + this.LOC_POS_ACTOR0 = 4107186; + this.OPENING_EVENT_HANDLER = 1245185; + this.POPRANGE0 = 4127803; + this.TERRITORYTYPE0 = 181; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + ManSea001.Scene00001( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + ManSea001.Scene00002( player ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + ManSea001.Scene00003( player ); + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( ManSea001.id, 0x01 ); // add quest to player. + + // update the instance boundaries, call to the opening event + //player.eventPlay( ManSea001.OPENING_EVENT_HANDLER, 0x1E, HIDE_HOTBAR, 1, 0); + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, NONE, 0, 0 ); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + ManSea001.Scene00006( player ); + }); + } + + def Scene00006( player ) + { + player.eventPlay( this.id, 6, NONE, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + player.questUpdate( ManSea001.id, ManSea001.SEQ_FINISH ); + player.prepareZoning( player.getZoneId(), true ); + player.changePos( 9, 40, 14, 2 ); + } + }); + } + + def Scene00007( player ) + { + player.eventPlay( this.id, 7, NONE, 0, 0 ); + } + + def Scene00008( player ) + { + player.eventPlay( this.id, 8, NONE, 0, 0 ); + } + + def Scene00009( player ) + { + player.eventPlay( this.id, 9, NONE, 0, 0 ); + } + + def Scene00010( player ) + { + player.eventPlay( this.id, 10, NONE, 0, 0 ); + } + + def Scene00011( player ) + { + player.eventPlay( this.id, 11, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManSea001.Scene00012( player ); + }); + } + + def Scene00012( player ) + { + player.eventPlay( this.id, 12, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + if( player.giveQuestRewards( ManSea001.id, 0 ) ) + { + player.questFinish( ManSea001.id ); + } + } + }); + } + + def Scene00013( player ) + { + player.eventPlay( this.id, 13, NONE, 0, 0 ); + } + + ////////////////////////////////////////////////////////////////////// + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + switch( actor ) + { + case( this.ACTOR0 ) + { + this.Scene00000( player ); + break; + } + case( this.ACTOR1 ) + { + this.Scene00005( player ); + break; + } + case( this.ACTOR2 ) + { + this.Scene00011( player ); + break; + } + default + { + + } + } + } + +}; + +GLOBAL ManSea001 = ManSea001Def(); diff --git a/bin/scripts/chai/quest/ManWil001.chai b/bin/scripts/chai/quest/ManWil001.chai new file mode 100644 index 00000000..1799e45a --- /dev/null +++ b/bin/scripts/chai/quest/ManWil001.chai @@ -0,0 +1,167 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: ManWil001_00594 +// Quest Name: Coming to Ul'dah +// Quest ID: 66130 +// Start NPC: 1003987 +// End NPC: 1003988 + +class ManWil001Def +{ + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManWil001Def() + { + // Basic quest information + this.name = "Coming to Ul'dah"; + this.id = 66130; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 103; + + // Entities found in the script data of the quest + this.ACTOR0 = 1003987; + this.ACTOR1 = 1003988; + this.ACTOR20 = 1001285; + this.CUT_EVENT = 188; + this.EOBJECT0 = 2001681; + this.EOBJECT1 = 2001682; + this.EOBJECT2 = 2001683; + this.EOBJECT3 = 2001706; + this.EVENT_ACTION_SEARCH = 1; + this.OPENING_EVENT_HANDLER = 1245187; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + ManWil001.Scene00001( player ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManWil001.Scene00002( player ); + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( ManWil001.id, ManWil001Obj.SEQ_FINISH );// add quest to player. + player.eventPlay( ManWil001.OPENING_EVENT_HANDLER, 0x1E, 0x2001, 0, 0 ); + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, 0, 0, 0 ); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + ManWil001.Scene00005( player ); + }); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // clicked finish button + { + player.gainExp( 50 ); + player.addCurrency( 1, ManWil001.RewardGil ); + player.questFinish( ManWil001.id ); + } + }); + } + + def Scene00006( player ) + { + player.eventPlay( this.id, 6, 0, 0, 0 ); + } + + def Scene00007( player ) + { + player.eventPlay( this.id, 7, 0, 0, 0 ); + } + + def Scene00008( player ) + { + player.eventPlay( this.id, 8, 0, 0, 0 ); + } + + def Scene00009( player ) + { + player.eventPlay( this.id, 9, 0, 0, 0 ); + } + + def Scene00010( player ) + { + player.eventPlay( this.id, 10, 0, 0, 0 ); + } + + def Scene00011( player ) + { + player.eventPlay( this.id, 11, 0, 0, 0 ); + } + + def Scene00012( player ) + { + player.eventPlay( this.id, 12, 0, 0, 0 ); + } + + def Scene00013( player ) + { + player.eventPlay( this.id, 13, 0, 0, 0 ); + } + + ////////////////////////////////////////////////////////////////////// + // Entry function for this quest + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + this.Scene00004( player ); + } + } + +}; + +GLOBAL ManWil001 = ManWil001Def(); diff --git a/bin/scripts/chai/quest/ManWil001.chai_generated b/bin/scripts/chai/quest/ManWil001.chai_generated new file mode 100644 index 00000000..2c101bd5 --- /dev/null +++ b/bin/scripts/chai/quest/ManWil001.chai_generated @@ -0,0 +1,214 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: ManWil001_00594 +// Quest Name: Coming to Ul'dah +// Quest ID: 66130 +// Start NPC: 1003987 +// End NPC: 1003988 + +class ManWil001 +{ + ////////////////////////////////////////////////////////////////////// + // default ctor + def ManWil001() + { + // Basic quest information + this.name = "Coming to Ul'dah"; + this.id = 66130; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 103; + + // Entities found in the script data of the quest + this.ACTOR0 = 1003987; + this.ACTOR1 = 1003988; + this.ACTOR20 = 1001285; + this.CUT_EVENT = 188; + this.EOBJECT0 = 2001681; + this.EOBJECT1 = 2001682; + this.EOBJECT2 = 2001683; + this.EOBJECT3 = 2001706; + this.EVENT_ACTION_SEARCH = 1; + this.OPENING_EVENT_HANDLER = 1245187; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00006( player ) + { + player.eventPlay( this.id, 6, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00007( player ) + { + player.eventPlay( this.id, 7, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00008( player ) + { + player.eventPlay( this.id, 8, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00009( player ) + { + player.eventPlay( this.id, 9, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00010( player ) + { + player.eventPlay( this.id, 10, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00011( player ) + { + player.eventPlay( this.id, 11, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00012( player ) + { + player.eventPlay( this.id, 12, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + def Scene00013( player ) + { + player.eventPlay( this.id, 13, 0/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, subEvent, param1, param2, param3 ) + { + var quest := ManWil001Obj; + // Callback area for this scene, called when the scene finishes + // Add logic here!!! + }); + } + + ////////////////////////////////////////////////////////////////////// + // Entry function for this quest + def eventHandlerNpc( player, actorId, eventId ) + { + var actor = mapActor( actorId ); + // Script content to be added here.... + } + +}; + +GLOBAL ManWil001Obj = ManWil001(); diff --git a/bin/scripts/chai/quest/SubFst000.chai b/bin/scripts/chai/quest/SubFst000.chai new file mode 100644 index 00000000..07f42a89 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst000.chai @@ -0,0 +1,176 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst000_00020 +// Quest Name: Incense and Sensibility +// Quest ID: 65556 +// Start NPC: 1000786 +// End NPC: 1000705 + +class SubFst000Def +{ + def SubFst000Def() + { + // Basic quest information + this.name = "Incense and Sensibility"; + this.id = 65556; + + // Quest vars / flags used + // GetQuestBitFlag8 + // GetQuestUI8AL + // GetQuestUI8BH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_2 = 2; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardItemOptional = [4551, 4555]; + this.RewardItemOptionalCount = [3, 3]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000786; + this.ACTOR1 = 1000705; + this.EOBJECT0 = 2000070; + this.EOBJECT1 = 2000071; + this.ITEM0 = 2000101; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_2_EOBJECT0 = 2; + this.SEQ_2_EOBJECT0_USEITEMNO = 99; + this.SEQ_2_EOBJECT0_USEITEMOK = 100; + this.SEQ_2_EOBJECT1 = 3; + this.SEQ_2_EOBJECT1_USEITEMNO = 97; + this.SEQ_2_EOBJECT1_USEITEMOK = 98; + this.SEQ_3_ACTOR1 = 4; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + player.questUpdate( SubFst000.id, 1 ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8BH( SubFst000.id, 2 ); + player.questUpdate( SubFst000.id, SubFst000.SEQ_2 );// add quest to player. + }); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, NONE, 0, 0 ); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE, 0, 0 ); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, NONE, 0, 0 ); + } + + def Scene00097( player ) + { + player.eventPlay( this.id, 97, NONE, 0, 0 ); + } + + def Scene00098( player ) + { + player.eventPlay( this.id, 98, NONE, 0, 0 ); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, NONE, 0, 0 ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, 0, 0 ); + } + + ////////////////////////////////////////////////////////////////////// + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + switch( actor ) + { + case( this.ACTOR0 ) + { + this.Scene00000( player ); + break; + } + case( this.ACTOR1 ) + { + this.Scene00001( player ); + break; + } + default + { + } + } + } + + def onEventItem( eventId, player, eventItemId, castTime, targetId ) + { + if( eventItemId == this.ITEM0 ) + { + player.eventItemActionStart( this.id, eventItemId, + // callback function for finished action + fun ( player, eventId, additional ) + { + var actor = mapActor( additional ); + + var censers = player.getQuestUI8AL( eventId ); + + player.questMessage( eventId, 1, 2, censers + 1, 2 ); + + if ( censers == 2 ) + { + player.questUpdate( eventId, SubFst000.SEQ_FINISH ); + } + else if( actor == SubFst000.EOBJECT0 ) + { + player.setQuestUI8AL(eventId, 4 ); + } + else if( actor == SubFst000.EOBJECT1 ) + { + player.setQuestUI8AL(eventId, 8 ); + } + SubFst000.Scene00100( player ); + + player.setQuestUI8AL( eventId, 0, censers + 1 ); + + }, + // callback for interrupted action. + fun ( player, eventId, additional ) {}, + targetId ); + } + } + +}; + +GLOBAL SubFst000 = SubFst000Def(); diff --git a/bin/scripts/chai/quest/SubFst001.chai b/bin/scripts/chai/quest/SubFst001.chai new file mode 100644 index 00000000..b75364d6 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst001.chai @@ -0,0 +1,125 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst001_00024 +// Quest Name: Coarse Correspondence +// Quest ID: 65560 +// Start NPC: 1000206 +// End NPC: 1000233 + +class SubFst001Def +{ + def SubFst001Def() + { + // Basic quest information + this.name = "Coarse Correspondence"; + this.id = 65560; + + // Quest vars / flags used + // GetQuestUI8AL + // GetQuestUI8BH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 103; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000206; + this.ACTOR1 = 1000233; + this.ITEM0 = 2000079; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_NPCTRADENO = 99; + this.SEQ_1_ACTOR1_NPCTRADEOK = 100; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( SubFst001.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + player.setQuestUI8AL( SubFst001.id, 1 ); + player.setQuestUI8BH( SubFst001.id, 1 ); + player.questUpdate( SubFst001.id, SubFst001.SEQ_FINISH ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( SubFst001.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + } ); + } + + def Scene00099( player ) + { + player.eventPlay( SubFst001.id, 99, NONE, + fun( player, eventId, param1, param2, param3 ) + { + + } ); + } + + def Scene00100( player ) + { + player.eventPlay( SubFst001.id, 100, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + if( player.giveQuestRewards( SubFst001.id, 0 ) ) + { + player.questFinish( SubFst001.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == SubFst001.ACTOR0 ) + { + if( !player.hasQuest( SubFst001.id ) ) + { + SubFst001.Scene00000( player ); + } + else + { + SubFst001.Scene00001( player ); + } + } + else if( actor == SubFst001.ACTOR1 ) + { + if( !player.hasQuest( SubFst001.id ) ) + { + // todo: what should this event do? + SubFst001.Scene00099( player ); + } + else + { + SubFst001.Scene00100( player ); + } + } + } + +}; + +GLOBAL SubFst001 = SubFst001Def(); diff --git a/bin/scripts/chai/quest/SubFst002.chai b/bin/scripts/chai/quest/SubFst002.chai new file mode 100644 index 00000000..ec14cda4 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst002.chai @@ -0,0 +1,107 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst002_00025 +// Quest Name: Quarrels with Squirrels +// Quest ID: 65561 +// Start NPC: 1000263 +// End NPC: 1000263 + +class SubFst002Def +{ + def SubFst002Def() + { + // Basic quest information + this.name = "Quarrels with Squirrels"; + this.id = 65561; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardItemOptional = [3762, 3764, 5823]; + this.RewardItemOptionalCount = [1, 1, 3]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000263; + this.ENEMY0 = 37; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_2_ACTOR0 = 1; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst002.id, SubFst002.SEQ_1 ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst002.id, 0 ) ) + { + player.questFinish( SubFst002.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( !player.hasQuest( SubFst002.id ) ) + { + SubFst002.Scene00000( player ); + } + + if( actor == SubFst002.ACTOR0 && player.questGetSeq( SubFst002.id ) == SubFst002.SEQ_FINISH ) + { + SubFst002.Scene00001( player ); + } + } + + def onBnpcKill_37( player ) + { + var currentKC = player.getQuestUI8AL(SubFst002.id); + currentKC = currentKC + 1; + + player.questMessage( SubFst002.id, 0, 2, currentKC, 6 ); + + if( currentKC >= 6 ) + { + player.questUpdate( SubFst002.id, SubFst002.SEQ_FINISH ); + } + else + { + player.setQuestUI8AL( SubFst002.id, currentKC ); + } + } + +}; + +GLOBAL SubFst002 = SubFst002Def(); diff --git a/bin/scripts/chai/quest/SubFst003.chai b/bin/scripts/chai/quest/SubFst003.chai new file mode 100644 index 00000000..88a133c5 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst003.chai @@ -0,0 +1,114 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst003_00026 +// Quest Name: Once Bitten, Twice Shy +// Quest ID: 65562 +// Start NPC: 1000297 +// End NPC: 1000315 + +class SubFst003Def +{ + def SubFst003Def() + { + // Basic quest information + this.name = "Once Bitten, Twice Shy"; + this.id = 65562; + + // Quest vars / flags used + // GetQuestUI8AL + // GetQuestUI8BH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 118; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000297; + this.ACTOR1 = 1000315; + this.ITEM0 = 2000028; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_NPCTRADENO = 99; + this.SEQ_1_ACTOR1_NPCTRADEOK = 100; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst003.id, SubFst003.SEQ_FINISH ); + player.setQuestUI8AL( SubFst003.id, 1 ); + player.setQuestUI8BH( SubFst003.id, 1 ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // gave back items + { + SubFst003.Scene00100( player ); + } + } ); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, NONE, + fun( player, eventId, param1, param2, param3 ) + { + } ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst003.id, 0 ) ) + { + player.questFinish( SubFst003.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( !player.hasQuest( SubFst003.id ) ) + { + SubFst003.Scene00000( player ); + } + + if( actor == SubFst003.ACTOR1 && player.questGetSeq( SubFst003.id ) == SubFst003.SEQ_FINISH ) + { + SubFst003.Scene00001( player ); + } + } + +}; + +GLOBAL SubFst003 = SubFst003Def(); diff --git a/bin/scripts/chai/quest/SubFst004.chai b/bin/scripts/chai/quest/SubFst004.chai new file mode 100644 index 00000000..7dfdacd2 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst004.chai @@ -0,0 +1,150 @@ +// Quest Script: SubFst004_00027 +// Quest Name: Preserving the Past +// Quest ID: 65563 +// Start NPC: 1000194 +// End NPC: 1000789 + +class SubFst004Def +{ + def SubFst004Def() + { + // Basic quest information + this.name = "Preserving the Past"; + this.id = 65563; + + // Quest vars / flags used + // GetQuestUI8AL + // GetQuestUI8BH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardItem = [5594]; + this.RewardItemCount = [10]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000194; + this.ACTOR1 = 1000686; + this.ACTOR2 = 1000789; + this.ITEM0 = 2000024; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_2_ACTOR2 = 2; + this.SEQ_2_ACTOR2_NPCTRADENO = 99; + this.SEQ_2_ACTOR2_NPCTRADEOK = 100; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + + player.questUpdate( SubFst004.id, SubFst004.SEQ_1 ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.setQuestUI8AL( SubFst004.id, 1 ); + player.setQuestUI8BH( SubFst004.id, 1 ); + //player.questMessage( SubFst004.id, 0, 2, 1, 1 ); + player.questMessage( SubFst004.id, 0, 1, 21002, 0 ); + player.questUpdate( SubFst004.id, SubFst004.SEQ_FINISH ); + } ); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + SubFst004.Scene00100( player ); + } + } ); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + if( player.giveQuestRewards( SubFst004.id, 0 ) ) + { + player.questFinish( SubFst004.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == SubFst004.ACTOR0 ) + { + if( !player.hasQuest( SubFst004.id ) ) + { + SubFst004.Scene00000( player ); + } + else + { + SubFst004.Scene00001( player ); + } + } + else if( actor == SubFst004.ACTOR1 ) + { + if( !player.hasQuest( SubFst004.id ) ) + { + // todo: what should this event do? + SubFst004.Scene00099( player ); + } + else + { + SubFst004.Scene00001( player ); + } + } + else if( actor == SubFst004.ACTOR2 ) + { + if( !player.hasQuest( SubFst004.id ) ) + { + // todo: what should this event do? + SubFst004.Scene00099( player ); + } + else + { + SubFst004.Scene00002( player ); + } + } + } + +}; + +GLOBAL SubFst004 = SubFst004Def(); diff --git a/bin/scripts/chai/quest/SubFst007.chai b/bin/scripts/chai/quest/SubFst007.chai new file mode 100644 index 00000000..3c336bcd --- /dev/null +++ b/bin/scripts/chai/quest/SubFst007.chai @@ -0,0 +1,133 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst007_00031 +// Quest Name: Essential Oil +// Quest ID: 65567 +// Start NPC: 1000705 +// End NPC: 1000705 + +class SubFst007Def +{ + def SubFst007Def() + { + // Basic quest information + this.name = "Essential Oil"; + this.id = 65567; + + // Quest vars / flags used + // GetQuestUI8AL + // GetQuestUI8BH + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardItemOptional = [3530, 3531, 5823]; + this.RewardItemOptionalCount = [1, 1, 3]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000705; + this.ENEMY0 = 49; + this.ITEM0 = 2000098; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_2_ACTOR0 = 1; + this.SEQ_2_ACTOR0_NPCTRADENO = 99; + this.SEQ_2_ACTOR0_NPCTRADEOK = 100; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst007.id, SubFst007.SEQ_1 ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // gave back items + { + SubFst007.Scene00100( player ); + } + } ); + } + + def Scene00099( player ) //unused + { + player.eventPlay( this.id, 99, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst007.id, 0 ) ) + { + player.questFinish( SubFst007.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( !player.hasQuest( SubFst007.id ) ) + { + SubFst007.Scene00000( player ); + } + + if( player.questGetSeq( SubFst007.id ) == SubFst007.SEQ_FINISH ) + { + SubFst007.Scene00001( player ); + } + } + + def onBnpcKill_49( player ) + { + var currentKC = player.getQuestUI8AL(SubFst007.id); + currentKC = currentKC + 1; + + player.questMessage( SubFst007.id, 0, 2, currentKC, 6 ); + player.tryAddItem( 2000098, 1 ); + + if( currentKC >= 6 ) + { + player.questUpdate( SubFst007.id, SubFst007.SEQ_FINISH ); + player.setQuestUI8BH( SubFst007.id, currentKC ); + } + else + { + player.setQuestUI8AL( SubFst007.id, currentKC ); + player.setQuestUI8BH( SubFst007.id, currentKC ); + } + } + +}; + +GLOBAL SubFst007 = SubFst007Def(); diff --git a/bin/scripts/chai/quest/SubFst010.chai b/bin/scripts/chai/quest/SubFst010.chai new file mode 100644 index 00000000..fb0f4758 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst010.chai @@ -0,0 +1,82 @@ +// Quest Script: SubFst010_00001 +// Quest Name: A Good Adventurer Is Hard to Find +// Quest ID: 65537 +// Start NPC: 1000146 +// End NPC: 1000195 + +class SubFst010Def +{ + def SubFst010Def() + { + // Basic quest information + this.name = "A Good Adventurer Is Hard to Find"; + this.id = 65537; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardItem = [4551]; + this.RewardItemCount = [2]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000146; + this.ACTOR1 = 1000195; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventid, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst010.id, SubFst010.SEQ_FINISH ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventid, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst010.id, 0 ) ) + { + player.questFinish( SubFst010.id ); + } + } + }); + } + + ////////////////////////////////////////////////////////////////////// + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( actor == this.ACTOR0 ) + { + this.Scene00000( player ); + } + else if( actor == this.ACTOR1 ) + { + this.Scene00001( player ); + } + } + +}; + +GLOBAL SubFst010 = SubFst010Def(); diff --git a/bin/scripts/chai/quest/SubFst011.chai b/bin/scripts/chai/quest/SubFst011.chai new file mode 100644 index 00000000..56f616e4 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst011.chai @@ -0,0 +1,100 @@ +// Quest Script: SubFst011_00037 +// Quest Name: Population Control +// Quest ID: 65573 +// Start NPC: 1000195 +// End NPC: 1000195 + +class SubFst011Def +{ + def SubFst011Def() + { + // Basic quest information + this.name = "Population Control"; + this.id = 65573; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 100; + this.RewardItemOptional = [2653, 2655, 5823]; + this.RewardItemOptionalCount = [1, 1, 3]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000195; + this.ENEMY0 = 47; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_2_ACTOR0 = 1; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventid, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst011.id, SubFst011.SEQ_1 ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventid, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + if( player.giveQuestRewards( SubFst011.id, 0 ) ) + { + player.questFinish( SubFst011.id ); + } + } + }); + } + + ////////////////////////////////////////////////////////////////////// + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( player.hasQuest(SubFst011.id) ) + { + this.Scene00001( player ); + } + else + { + this.Scene00000( player ); + } + } + + def onBnpcKill_47( player ) + { + var currentKC = player.getQuestUI8AL(SubFst011.id); + currentKC = currentKC + 1; + + player.questMessage( SubFst011.id, 0, 2, currentKC, 6 ); + + if( currentKC >= 6 ) + { + player.questUpdate( SubFst011.id, SubFst011.SEQ_FINISH ); + } + else + { + player.setQuestUI8AL( SubFst011.id, currentKC ); + } + } + +}; + +GLOBAL SubFst011 = SubFst011Def(); \ No newline at end of file diff --git a/bin/scripts/chai/quest/SubFst013.chai b/bin/scripts/chai/quest/SubFst013.chai new file mode 100644 index 00000000..8f048451 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst013.chai @@ -0,0 +1,235 @@ +// Quest Script: SubFst013_00040 +// Quest Name: For Friendship +// Quest ID: 65576 +// Start NPC: 1000162 +// End NPC: 1000162 + +class SubFst013Def +{ + def SubFst013Def() + { + // Basic quest information + this.name = "For Friendship"; + this.id = 65576; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_2 = 2; + this.SEQ_3 = 3; + this.SEQ_4 = 4; + this.SEQ_5 = 5; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 111; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000162; + this.ACTOR1 = 1000161; + this.FIRST_QUEST = 65575; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + this.SEQ_1_ACTOR1_EMOTENO = 99; + this.SEQ_1_ACTOR1_EMOTEOK = 100; + this.SEQ_2_ACTOR0 = 2; + this.SEQ_3_ACTOR1 = 3; + this.SEQ_3_ACTOR1_EMOTENO = 97; + this.SEQ_3_ACTOR1_EMOTEOK = 98; + this.SEQ_4_ACTOR0 = 4; + this.SEQ_5_ACTOR1 = 5; + this.SEQ_5_ACTOR1_EMOTENO = 95; + this.SEQ_5_ACTOR1_EMOTEOK = 96; + this.SEQ_6_ACTOR0 = 6; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_1 ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_3 ); + } ); + } + + def Scene00003( player ) //unused + { + player.eventPlay( this.id, 3, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_5 ); + } ); + } + + def Scene00005( player ) //unused + { + player.eventPlay( this.id, 5, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00006( player ) + { + player.eventPlay( this.id, 6, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst013.id, 0 ) ) + { + player.questFinish( SubFst013.id ); + } + } + } ); + } + + def Scene00095( player ) //unused + { + player.eventPlay( this.id, 95, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00096( player ) + { + player.eventPlay( this.id, 96, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_FINISH ); + player.questMessage( SubFst013.id, 4, 2, 0, 0 ); + } ); + } + + def Scene00097( player ) //unused + { + player.eventPlay( this.id, 97, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00098( player ) + { + player.eventPlay( this.id, 98, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_4 ); + player.questMessage( SubFst013.id, 2, 2, 0, 0 ); + } ); + } + + def Scene00099( player ) //unused + { + player.eventPlay( this.id, 99, NONE, + fun( player, eventId, param1, param2, param3 ) + {} ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, + fun( player, eventId, param1, param2, param3 ) + { + player.questUpdate( SubFst013.id, SubFst013.SEQ_2 ); + player.questMessage( SubFst013.id, 0, 2, 0, 0 ); + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( !player.hasQuest( SubFst013.id ) ) + { + SubFst013.Scene00000( player ); + return; + } + + if( actor == SubFst013.ACTOR1 ) //talk to girl without emotes + { + SubFst013.Scene00001( player ); + return; + } + + if( actor == SubFst013.ACTOR0 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_2 ) + { + SubFst013.Scene00002( player ); + return; + } + + if( actor == SubFst013.ACTOR0 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_4 ) + { + SubFst013.Scene00004( player ); + return; + } + + if( actor == SubFst013.ACTOR0 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_FINISH ) + { + SubFst013.Scene00006( player ); + return; + } + + } + + def onEmote( eventId, player, actorId, emoteId ) + { + var actor = mapActor( actorId ); + print( emoteId ); + + if( actor == SubFst013.ACTOR1 && emoteId == 5 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_1 ) + { + SubFst013.Scene00100( player ); + return; + } + + if( actor == SubFst013.ACTOR1 && emoteId == 18 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_3 ) + { + SubFst013.Scene00098( player ); + return; + } + + if( actor == SubFst013.ACTOR1 && emoteId == 11 && player.questGetSeq( SubFst013.id ) == SubFst013.SEQ_5 ) + { + SubFst013.Scene00096( player ); + return; + } + } + +}; + +GLOBAL SubFst013 = SubFst013Def(); diff --git a/bin/scripts/chai/quest/SubFst015.chai b/bin/scripts/chai/quest/SubFst015.chai new file mode 100644 index 00000000..a8f89a2b --- /dev/null +++ b/bin/scripts/chai/quest/SubFst015.chai @@ -0,0 +1,261 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst015_00042 +// Quest Name: Sylphic Gratitude +// Quest ID: 65578 +// Start NPC: 1000286 +// End NPC: 1000286 + +class SubFst015Def +{ + def SubFst015Def() + { + // Basic quest information + this.name = "Sylphic Gratitude"; + this.id = 65578; + + // Quest vars / flags used + // GetQuestBitFlag8 + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_1 = 1; + this.SEQ_FINISH = 255; + //this.SEQ_OFFER = ?; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardGil = 110; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000286; + this.EOBJECT0 = 2000020; + this.EOBJECT1 = 2000021; + this.EOBJECT2 = 2000022; + this.EOBJECT3 = 2000023; + this.EOBJECT4 = 2000024; + this.EOBJECT5 = 2000025; + this.EVENT_ACTION_SEARCH = 1; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + player.questUpdate( SubFst015.id, 1 ); + } + }); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, 0, 0 ); + } + + def Scene00002( player ) + { + player.eventPlay( this.id, 2, NONE, 0, 0 ); + } + + def Scene00003( player ) + { + player.eventPlay( this.id, 3, NONE, 0, 0 ); + } + + def Scene00004( player ) + { + player.eventPlay( this.id, 4, NONE, 0, 0 ); + } + + def Scene00005( player ) + { + player.eventPlay( this.id, 5, NONE, 0, 0 ); + } + + def Scene00006( player ) + { + player.eventPlay( this.id, 6, NONE, 0, 0 ); + } + + def Scene00007( player ) + { + player.eventPlay( this.id, 7, NONE, 0, 0, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) + { + if( player.giveQuestRewards( SubFst015.id, 0 ) ) + { + player.questFinish( SubFst015.id ); + } + } + }); + } + + def Scene00089( player ) + { + player.eventPlay( this.id, 89, NONE, 0, 0 ); + } + + def Scene00090( player ) + { + player.eventPlay( this.id, 90, NONE, 0, 0 ); + } + + def Scene00091( player ) + { + player.eventPlay( this.id, 91, NONE, 0, 0 ); + } + + def Scene00092( player ) + { + player.eventPlay( this.id, 92, NONE, 0, 0 ); + } + + def Scene00093( player ) + { + player.eventPlay( this.id, 93, NONE, 0, 0 ); + } + + def Scene00094( player ) + { + player.eventPlay( this.id, 94, NONE, 0, 0 ); + } + + def Scene00095( player ) + { + player.eventPlay( this.id, 95, NONE, 0, 0 ); + } + + def Scene00096( player ) + { + player.eventPlay( this.id, 96, NONE, 0, 0 ); + } + + def Scene00097( player ) + { + player.eventPlay( this.id, 97, NONE, 0, 0 ); + } + + def Scene00098( player ) + { + player.eventPlay( this.id, 98, NONE, 0, 0 ); + } + + def Scene00099( player ) + { + player.eventPlay( this.id, 99, NONE, 0, 0 ); + } + + def Scene00100( player ) + { + player.eventPlay( this.id, 100, NONE, 0, 0 ); + } + + ////////////////////////////////////////////////////////////////////// + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + switch( actor ) + { + case( this.ACTOR0 ) + { + if( player.hasQuest( this.id ) ) + { + this.Scene00007( player ); + } + else + { + this.Scene00000( player ); + } + break; + } + case ( this.EOBJECT0 ) {} + case ( this.EOBJECT1 ) {} + case ( this.EOBJECT2 ) {} + case ( this.EOBJECT3 ) {} + case ( this.EOBJECT4 ) {} + case ( this.EOBJECT5 ) + { + player.eventActionStart( this.id, 0x0F, + fun( player, eventId, additional ) + { + var actor = mapActor( additional ); + + var currVegetal = player.getQuestUI8AL( eventId ); + + var despawn1 = player.getQuestUI8FL( eventId ); + var despawn2 = player.getQuestUI8FH( eventId ); + + if( SubFst015.EOBJECT3 == actor ) + { + despawn2 = despawn2 | 1; + player.setQuestUI8FH( eventId, despawn2 ); + } + else if( SubFst015.EOBJECT2 == actor ) + { + despawn2 = despawn2 | 2; + player.setQuestUI8FH( eventId, despawn2 ); + } + else if( SubFst015.EOBJECT1 == actor ) + { + despawn2 = despawn2 | 4; + player.setQuestUI8FH( eventId, despawn2 ); + } + else if( SubFst015.EOBJECT0 == actor ) + { + despawn2 = despawn2 | 8; + player.setQuestUI8FH( eventId, despawn2 ); + } + else if( SubFst015.EOBJECT5 == actor ) + { + despawn2 = despawn1 | 4; + player.setQuestUI8FL( eventId, despawn2 ); + } + else if( SubFst015.EOBJECT4 == actor ) + { + despawn2 = despawn1 | 8; + player.setQuestUI8FL( eventId, despawn2 ); + } + + player.questMessage( eventId, 0, 2, currVegetal + 1, 6 ); + + if ( currVegetal + 1 == 6 ) + { + player.questUpdate( eventId, SubFst015.SEQ_FINISH ); + } + else + { + player.setQuestUI8AL( eventId, currVegetal + 1 ); + } + + SubFst015.Scene00096( player ); + }, + fun( player, eventId, additional ) + { + }, + actorId ); + player.unlock(); + break; + } + + default + { + } + } + } + +}; + +GLOBAL SubFst015 = SubFst015Def(); diff --git a/bin/scripts/chai/quest/SubFst019.chai b/bin/scripts/chai/quest/SubFst019.chai new file mode 100644 index 00000000..371a0096 --- /dev/null +++ b/bin/scripts/chai/quest/SubFst019.chai @@ -0,0 +1,89 @@ +// This is an automatically generated chai script template +// Content needs to be added by hand to make it function +// In order for this script to be loaded, change its extension to .chai +// and move it to the correct folder in bin/scripts/chai/quest + +// Quest Script: SubFst019_00049 +// Quest Name: I Am Millicent, Hear Me Roar +// Quest ID: 65585 +// Start NPC: 1000788 +// End NPC: 1000429 + +class SubFst019Def +{ + def SubFst019Def() + { + // Basic quest information + this.name = "I Am Millicent, Hear Me Roar"; + this.id = 65585; + + // Quest vars / flags used + // GetQuestUI8AL + + // Steps in this quest ( 0 is before accepting, + // 1 is first, 255 means ready for turning it in + this.SEQ_0 = 0; + this.SEQ_FINISH = 255; + + // Quest rewards + this.RewardExpFactor = 50; + this.RewardItem = [4551]; + this.RewardItemCount = [5]; + + // Entities found in the script data of the quest + this.ACTOR0 = 1000788; + this.ACTOR1 = 1000429; + this.SEQ_0_ACTOR0 = 0; + this.SEQ_1_ACTOR1 = 1; + + } + + ////////////////////////////////////////////////////////////////////// + // Available Scenes in this quest, not necessarly all are used + def Scene00000( player ) + { + player.eventPlay( this.id, 0, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // accept quest + { + player.questUpdate( SubFst019.id, SubFst019.SEQ_FINISH ); + } + } ); + } + + def Scene00001( player ) + { + player.eventPlay( this.id, 1, NONE, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 == 1 ) // finish quest + { + if( player.giveQuestRewards( SubFst019.id, 0 ) ) + { + player.questFinish( SubFst019.id ); + } + } + } ); + } + + ////////////////////////////////////////////////////////////////////// + // Event Handlers + def onTalk( eventId, player, actorId ) + { + var actor = mapActor( actorId ); + + if( !player.hasQuest( SubFst019.id ) ) + { + SubFst019.Scene00000( player ); + } + + if( actor == SubFst019.ACTOR1 && player.questGetSeq( SubFst019.id ) == SubFst019.SEQ_FINISH ) + { + SubFst019.Scene00001( player ); + } + } + +}; + +GLOBAL SubFst019 = SubFst019Def(); diff --git a/bin/web/createUser.html b/bin/web/createUser.html new file mode 100644 index 00000000..9442299c --- /dev/null +++ b/bin/web/createUser.html @@ -0,0 +1,73 @@ + + + + FFXIV 1.0 Login + + + + + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + +
Username:
Password:
+ +
+

+
+
+
+ + + diff --git a/bin/web/css/global.css b/bin/web/css/global.css new file mode 100644 index 00000000..0af8d574 --- /dev/null +++ b/bin/web/css/global.css @@ -0,0 +1,158 @@ +body +{ + font-family: Verdana; + font-size: 10pt; + line-height: 14pt; + height: 100%; +} + + +div.contentContainer { + width: 50%; + float: left; +} + + +div.info +{ + width: 320px; + height: 100%; + margin-left: auto; + margin-right: auto; + text-align: center; + padding: 20px; + background-color: lightgrey; +} + +div.infoFooter +{ + width: 400px; + margin-left: auto; + margin-right: auto; + text-align: right; + padding: 5px; +} + +div.edit +{ + width: 50%; + min-width: 600px; + margin-top: 20px; + margin-left: auto; + margin-right: auto; + text-align: center; + padding: 20px; + background-color: lightgrey; +} + +div.inner +{ + position: relative; + max-width: 1250px; + margin: 0px auto; +} + +h1 +{ + line-height: 23px; + font-size: 23px; + padding: 5px 0px; +} + +h2 +{ + line-height: 17px; + font-size: 14px; + font-weight: bold; + padding: 5px; +} + +h3 +{ + line-height: 14px; + font-size: 12px; + font-weight: bold; + padding: 5px; +} + +header.top +{ + padding: 20px 0px; + background: none repeat scroll 0% 0% lavender; + position: relative; + z-index: 999; +} + +table.center +{ + margin-left: auto; + margin-right: auto; +} + +table.infoForm +{ + width: 100%; +} + +table.editForm +{ + width: 100%; +} + +table.editForm input +{ + width: 100%; +} + +table.editForm select +{ + width: 100%; +} + +td +{ + padding: 3px; +} + +th +{ + padding: 3px; + text-decoration: underline; +} + +p +{ + padding: 2px; +} + +p.errorMessage +{ + color: darkred; + font-weight: bold; +} + +p.pageTitle +{ + font-weight: bold; + font-size: 28px; + line-height: 20px; + padding: 0px 0px; +} + +p.pageTitle a +{ + text-decoration: none; + color: black; +} + +p.pageSubTitle +{ + font-size: 10px; + line-height: 18px; + padding: 0px 0px; +} + +#recaptcha_area +{ + margin: 0 auto; +} diff --git a/bin/web/css/reset.css b/bin/web/css/reset.css new file mode 100644 index 00000000..e29c0f5f --- /dev/null +++ b/bin/web/css/reset.css @@ -0,0 +1,48 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/bin/web/headlines.xml b/bin/web/headlines.xml new file mode 100644 index 00000000..3b671e4e --- /dev/null +++ b/bin/web/headlines.xml @@ -0,0 +1,1722 @@ +OKBezüglich Einschränkungen bei der Charaktererstellung14854608001485461160ee2c8eb0ec2c20d20e2bd6d9e2c69217dcad20d81Updates bezüglich der Einschränkungen der Charaktererstellung finden Sie hier.<br> +Sollte es zu einer erhöhten Belastung kommen, behalten wir es uns vor, die Erstellung für neue Charaktere zeitweilig einzuschränken.<br> +Diese Information wird in den folgenden Abständen aktualisiert:<br> +2:00 / 7:00 / 12:00 / 16:00 / 21:00 (MEZ)<br> +<br> +○:Charaktererstellung möglich ×:Charaktererstellung eingeschränkt<br> +<br> +Japanisches Datenzentrum<br> + ○ Aegis<br> + ○ Alexander<br> + ○ Anima<br> + ○ Asura<br> + ○ Atomos<br> + ○ Bahamut<br> + ○ Belias<br> + ○ Carbuncle<br> + × Chocobo<br> + ○ Durandal<br> + ○ Fenrir<br> + ○ Garuda<br> + ○ Gungnir<br> + ○ Hades<br> + ○ Ifrit<br> + ○ Ixion<br> + ○ Kujata<br> + ○ Mandragora<br> + ○ Masamune<br> + ○ Pandaemonium<br> + ○ Ramuh<br> + ○ Ridill<br> + × Shinryu<br> + ○ Tiamat<br> + ○ Titan<br> + ○ Tonberry<br> + ○ Typhon<br> + ○ Ultima<br> + ○ Unicorn<br> + ○ Valefor<br> + ○ Yojimbo<br> + ○ Zeromus<br> +<br> +Nordamerikanisches Datenzentrum<br> + ○ Adamantoise<br> + × Balmung<br> + × Behemoth<br> + ○ Brynhildr<br> + ○ Cactuar<br> + ○ Coeurl<br> + × Diabolos<br> + × Excalibur<br> + ○ Exodus<br> + ○ Faerie<br> + ○ Famfrit<br> + × Gilgamesh<br> + ○ Goblin<br> + × Hyperion<br> + ○ Jenova<br> + ○ Lamia<br> + × Leviathan<br> + ○ Malboro<br> + ○ Mateus<br> + ○ Midgardsormr<br> + ○ Sargatanas<br> + ○ Siren<br> + × Ultros<br> + ○ Zalera<br> +<br> +Europäisches Datenzentrum<br> + × Cerberus<br> + ○ Lich<br> + × Moogle<br> + × Odin<br> + ○ Phoenix<br> + × Ragnarok<br> + × Shiva<br> + ○ ZodiarkTechnische Schwierigkeiten des „PSN“ (26. Jan.)14854473281485447484133a2a9464b7a1e1cb77ed6899fc8e813b273d294Gegenwärtig gibt es technische Schwierigkeiten mit dem Sony Interactive Entertainment-Dienst „PSN“.<br> +<br> +Weitere Informationen werden bekanntgegeben, sobald diese verfügbar sind.<br> +<br> +* Bitte beachtet, dass bereits eingeloggte Spieler weiterhin spielen können, solange sie sich nicht ausloggen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 26. Jan. 2017 um 15:22 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggenTechnische Schwierigkeiten des Mana Datenzentrums behoben (26. Jan.)148544489314854449418007f32b6f60752b6efc62332e7eaf608670a8f3Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 26. Jan. 2017 von 13:46 Uhr bis 16:15 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Notwartung des Mana-Datenzentrums (26. Jan.)14854377331485437790021fbfd330ff2fac70a32cf8bd15cfa146d2258fMaintenance2Wir werden eine Notwartung aller Welten des Mana-Datenzentrums durchführen. Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zu deren Abschluss wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 26. Jan. 2017 von 14:45 Uhr bis 17:15 Uhr (MEZ) <br> +<br> +[Betroffene Welten]<br> +Alle Welten des Mana-Datenzentrums:<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Technische Schwierigkeiten des Mana Datenzentrums (26. Jan.)148543638814854364392e83b51376fe2d18ce9dc653b146574508c5829d4Gegenwärtig gibt es technische Schwierigkeiten mit den untenstehenden Welten.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 26. Jan. 2017 um 13:46 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Zeiten in denen Zeremonien des Ewigen Bundes nicht stattfinden können1485423600148542385332c6eee3716cfdfaf24fea79b19b5f5d73b5f8781Wir möchten euch hiermit die Zeiten für zukünftige Wartungen mitteilen.<br> +<br> +Für den Fall, dass sich eine Wartung mit eurer Reservierung für die Zeremonie des Ewigen Bundes überschneidet, findet die Zeremonie nicht statt, und es muss eine neue Reservierung nach der Wartung abgeschlossen werden.<br> +Wir möchten euch bitten, diese Zeiten bei der Planung eurer Zeremonie des Ewigen Bundes zu berücksichtigen.<br> +<br> +* Falls eine Wartung zum gleichen Zeitpunkt stattfindet wie eine Reservierung für die Zeremonie des Ewigen Bundes, wird diese Zeremonie storniert, und muss neu reserviert werden.<br> +* Es besteht die Möglichkeit, dass es Notwartungen gibt, die nicht in der unten stehenden Liste erwähnt sind.<br> +<br> +[Mögliche Wartungen]<br> +* Diese Information ist nicht endgültig. Es besteht die Möglichkeit, dass diese Wartungen verschoben werden oder gar nicht stattfinden.<br> +<br> +・Irgendwann am 31. Jan. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +・Irgendwann am 1. Feb. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +・Irgendwann am 2. Feb. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +・Irgendwann am 7. Feb. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +・Irgendwann am 8. Feb. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +・Irgendwann am 9. Feb. 2017 zwischen 5 Uhr und 12 Uhr (MEZ)<br> +<br> +[Geplante Wartungen]<br> +<br> +・Keine<br> +<br> +* Falls ihr schon eine Zeremonie in diesem Zeitraum gebucht habt, möchten wir euch bitten, diese zu verlegen.<br> +<br> +[Betroffene Welten]<br> +<br> +Unten aufgelistet findet ihr die Welten, auf welchen ihr Zeremonien des Ewigen Bundes reservieren oder nicht reservieren könnt.<br> +Bitte überprüft diese Liste, bevor ihr eine Reservierung macht.<br> +<br> +○:Reservierung möglich ×:Reservierung nicht möglich<br> +<br> +* Auf betroffenen Welten ist eine Reservierung bis zum nächsten Freitag um 10 Uhr (MEZ) nicht möglich.<br> +<br> +■Japanische Welten<br> + × Aegis      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Alexander    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Anima      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Asura      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Atomos      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Bahamut     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Belias      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Carbuncle    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Chocobo     :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Durandal     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Fenrir      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Garuda      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Gungnir     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Hades      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Ifrit      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Ixion      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Kujata      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Mandragora    :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Masamune     :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Pandaemonium   :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Ramuh      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Ridill      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Shinryu     :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Tiamat      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Titan      :vom 21. Jan. 2017, 8:30 Uhr (MEZ)<br> + × Tonberry     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Typhon      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Ultima      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Unicorn     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Valefor     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Yojimbo     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Zeromus     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> +<br> +■Nordamerikanische Welten<br> + × Adamantoise   :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Balmung     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Behemoth     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Brynhildr    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Cactuar     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Coeurl      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Diabolos     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Excalibur    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Exodus      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Faerie      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Famfrit     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Gilgamesh    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Goblin      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Hyperion     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Jenova      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Lamia      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Leviathan    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Malboro     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Mateus      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Midgardsormr   :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Sargatanas    :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Siren      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Ultros      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Zalera      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> +<br> +■Europäische Welten<br> + × Cerberus     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Lich       :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Moogle      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Odin       :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Phoenix     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Ragnarok     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Shiva      :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> + × Zodiark     :vom 26. Jan. 2017, 7:00 Uhr (MEZ)<br> +Wartung aller Welten (26. Jan.): Früher Abgeschlossen14854222401485422365fe767f17bb086bc5428dba96d078dcb8bb9d3479Follow-up2Die Wartung wurde früher als geplant abgeschlossen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 26. Jan. 2017 von 7 Uhr bis 10:15 Uhr (MEZ) <br> +<br> +[Betroffene Welten]<br> +Alle Welten<br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (26. Jan.)148542087314854209860aef862a045b7fe223f6951de54dfd79b8e82dc51RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 19. Jan. 2017 bis zum 25. Jan. 2017<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 188<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 16566<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +FINAL FANTASY XIV Versions-Update (26. Jan.)14854171751485417225f6d8c44ed9c66ab174fab5dd446903963f40a2713Ein FINAL FANTASY XIV Versions-Update wurde durchgeführt. <br> +<br> +* Clients werden nach deren Starten automatisch aktualisiert. <br> +<br> +[Datum & Uhrzeit] <br> +Am 26. Jan. 2017 um 8:12 Uhr (MEZ) <br> +<br> +Bitte folgt dem untenstehenden Link für weitere Informationen:<br> +<a href="http://sqex.to/8vH">http://sqex.to/8vH</a><br> +Wartung aller Welten (26. Jan.)1485238200148542205584aa16f2595b3b7c474a804f510b6605bfda20a0Maintenance2Zur folgenden Zeit werden wir, begleitend zu Patch 3.51, eine Wartung durchführen. Während dieses Zeitraums wird FINAL FANTASY XIV nicht spielbar sein. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit]<br> +Am 26. Jan. 2017 von 7 Uhr bis 11 Uhr (MEZ) <br> +* Änderungen der Wartungszeiten vorbehalten.<br> +<br> +[Betroffene Welten]<br> +Alle Welten<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zu deren Abschluss wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +* Beachtet bitte, dass es Spielern nicht möglich sein wird, Zeremonien des Ewigen Bundes vom Ende der Wartung bis zum 27. Januar 2017 um 10 Uhr (MEZ) zu reservieren.<br> +Technische Schwierigkeiten mit verschiedenen Arealen auf Brynhildr behoben (24. Jan.)148523172414852317790cedb3444ab6e078d1f63660a488cf0886598695Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit der untenstehenden Welt behoben wurden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für eure Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Am 24. Jan. 2017 von 4:32 Uhr bis 4:47 Uhr (MEZ) <br> +<br> +[Details] <br> +・Die unten angegebenen Areale der Welt Brynhildr waren unzugänglich<br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welt] <br> +・Äußeres La Noscea<br> +・Gold Saucer<br> +・Strebewerk<br> +・Wallende Nebel<br> +・Bestimmte Bereiche in Häusern<br> +Technische Schwierigkeiten des Mana Datenzentrums behoben (22. Jan.)14850828291485082967f15a2cacbeb5baa1644195bffea61cbce6af9236Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 22. Jan. 2017 von 9:16 Uhr bis 11:45 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich. <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Notwartung des Mana-Datenzentrums (22. Jan.)14850768731485083118099e9177a41e4c1ab3c986c35c1d62ccfe7e1da6Maintenance2Wir werden eine Notwartung aller Welten des Mana-Datenzentrums durchführen. Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zu deren Abschluss wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 22. Jan. 2017 von 10:30 Uhr bis 12 Uhr (MEZ)<br> +<br> + [Betroffene Welten]<br> +Alle Welten des Mana-Datenzentrums:<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・TitanTechnische Schwierigkeiten des Mana Datenzentrums (22. Jan.)1485074368148507441438dd1111b1a1c959066039a2caa156991eac5eda4Gegenwärtig gibt es technische Schwierigkeiten mit den untenstehenden Welten.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 22. Jan. 2017 um 9:16 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Technische Schwierigkeiten des Mana Datenzentrums behoben (21. Jan.)148499161114849921168aa281daaf04c4ca4b2f43e5eedb0ab503ec537eRecovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 21. Jan. 2017 von 7:35 Uhr bis 10:19 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich. <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・TitanNotwartung des Mana-Datenzentrums (21. Jan.)148498441714849918448dbbd6c4bf84f996bd30138dbc317f87c24d0ae8Maintenance2Wir werden eine Notwartung aller Welten des Mana-Datenzentrums durchführen. Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zu deren Abschluss wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +* Beachtet bitte, dass es Spielern auf den Welten des Mana-Datenzentrums nicht möglich sein wird, Zeremonien des Ewigen Bundes vom Ende der Wartung bis zum 27. Januar 2017 um 10 Uhr (MEZ) zu reservieren.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 21. Jan. 2017 von 8:30 Uhr bis 10 Uhr (MEZ)<br> +<br> + [Betroffene Welten]<br> +Alle Welten des Mana-Datenzentrums:<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・TitanTechnische Schwierigkeiten des Mana Datenzentrums (21. Jan.)14849825811484982635cb789f73d4f023948c46dfea32fab2939e1fb06c4Gegenwärtig gibt es technische Schwierigkeiten mit den untenstehenden Welten.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 21. Jan. 2017 um 7:35 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・TitanTechnische Schwierigkeiten des Aether Datenzentrums behoben (20. Jan.)1484897102148489716437243fb93c19d2eb2da98865bb890a81efab5a1dRecovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 20. Jan. 2017 von 5:13 Uhr bis 7:56 Uhr (MEZ) <br> +<br> +[Details] <br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Aether Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Zalera<br> +Notwartung des Aether-Datenzentrums (20. Jan.)14848896851484895618c96e7ae40e26d496677b421c75138c777691cf05Maintenance2Wir werden eine Notwartung aller Welten des Aether- Datenzentrums durchführen.Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 20. Jan. 2017 von 5:30 bis 7 Uhr (MEZ)<br> +<br> +[Betroffene Welten]<br> +Alle Welten des Aether-Datenzentrums:<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Zalera<br> +Technische Schwierigkeiten des Aether Datenzentrums (20. Jan.)14848887191484888767b3b129122337cd99563110c332723ebeeb3a976c4Gegenwärtig untersuchen wir technischen Schwierigkeiten, welche für Verbindungsprobleme mit bestimmten instanziierten Inhalten auf dem Aether Datenzentrum verantwortlich sind.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 20. Jan. 2017 um 5:13 Uhr (MEZ)<br> +<br> +[Details] <br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Aether Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・ZaleraBekanntes Problem nach Patch 3.5 (19. Jan.)1484833791148483384962d02c99649a715a0c5bff04a3976f5605dfef931Wir konnten den folgenden Fehler feststellen.<br> +Der Fehler wird untersucht und wir bitten euch um Geduld und Verständnis, während wir an einer Lösung arbeiten.<br> +<br> +[Bekanntes Problem]<br> +・Wenn man den NPC nutzt, der einen Spieler von einem mit dem Unterkunftsgebiet verbundenen Gebiet zur privaten Unterkunft/Wohnung/Freien Gesellschaft transportiert, kann man unter Umständen in einem falschen Gebiet ankommen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten.<br> +Bezüglich der Gruppenanmeldung für Dun Scaith (19. Jan.)148482376414848238253c0cd7ed9fca28a503561086551ce7f472076eea1Wir haben zahlreiche Meldungen über lange Wartezeiten für volle Trupps für den Inhalt „Dun Scaith“ erhalten. <br> +<br> +Nach einer Untersuchung konnten wir folgendes feststellen und wollen euch dieses erläutern.<br> +<br> +Im Allgemeinen werden Gruppen die sich für einen Inhalt anmelden vom System bevorzugt. Da der 24 Spieler-Raid jedoch nur 3 Plätze für Verteidiger hat, kann es aufgrund von hoher Belastung zu längeren Wartezeiten für Verteidiger kommen, die sich alleine für den neuen Allianz-Raid anmelden.<br> +<br> +Aus diesem Grund haben wir den Algorithmus für den 24 Spieler Raid mit Patch 3.5 angepasst, damit Verteidiger die sich sowohl alleine, als auch in einer Gruppe anmelden, in dieselbe Warteschlange kommen, welches die Wartezeiten für Verteidiger ausgleicht.<br> +<br> +Da „Dun Scaith“, zuzüglich zum neuen Algorithmus, momentan übermäßig ausgewählt wird, kommt es zu längeren Wartezeiten für Gruppen mit Verteidigern. Wir entschuldigen uns für etwaige Unannehmlichkeiten.<br> +<br> +Um dieses Problem so schnell wie möglich zu beheben, werden wir erneut den Anmeldealgorithmus verwenden, welcher vor Patch 3.5 aktiv war. Wir bitten euch bis dahin um eure Geduld und euer Verständnis.<br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (19. Jan.)14848198751484819939be8b456f0e58f0c7b3e985f4123ce652f9d6b0291RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 12. Jan. 2017 bis zum 18. Jan. 2017<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 247<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 23,247<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +・Konten, die Sanktionen aufgrund von Teilnahme an RMT/unrechtmäßigen Aktivitäten erhielten: 6<br> +・Konsequenz: Temporäre Suspendierung von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.Gaia Datenzentrum Gruppensuche Rekrutierungsfunktion: Notwartung (19.Jan.)148481765614848209630d182e9c810acf09a6d52d9bf515a3a208eec554Maintenance2Zur Behebung des Fehlers beim Rekrutieren über die Gruppensuche, wodurch manche Welten nicht auf dem Gaia Datenzentrum angezeigt wurden, werden wir eine Notwartung an der Gruppensuche durchführen. Während dieser Wartung wird die Gruppensuche nicht verfügbar sein.<br> +<br> +*Alle anderen Funktionen sind weiterhin nutzbar.<br> +<br> +Desweiteren werden beim Start der Notwartung alle Gruppen die über die Gruppensuche zusammengestellt wurden aufgelöst werden. Wir entschuldigen uns für etwaige Unannehmlichkeiten die hierdurch entstehen können.<br> +<br> +*Alle Gruppen die durch die Gruppensuche zusammengestellt wurden und in einem Inhalt beigetreten sind werden beim beenden des Inhalts aufgelöst werden.<br> +<br> +[Datum & Uhrzeit]<br> +Am 19. Jan, 2017 11 bis 11:10 Uhr (MEZ)<br> +<br> +[Betroffene Welten]<br> +Alle Welten des Gaia Datenzentrums<br> +・Alexander<br> +・Bahamut<br> +・Durandal<br> +・Fenrir<br> +・Ifrit<br> +・Ridill<br> +・Tiamat<br> +・Ultima<br> +・Valefor<br> +・Yojimbo<br> +・Zeromus<br> +FINAL FANTASY XIV Versions-Update (19. Jan.)14848160061484821267faa3ac761ad713ac9d8bc4733c216118cade74e93Ein FINAL FANTASY XIV Versions-Update wurde durchgeführt. <br> +* Clients werden nach deren Starten automatisch aktualisiert. <br> +<br> +[Datum & Uhrzeit] <br> +Am 19. Jan. 2017 um 9:30 Uhr (MEZ) <br> +<br> +[Update Details]<br> +◆FINAL FANTASY XIV Hotfixes (19. Jan. 2017)◆<br> +<br> +■Die folgenden Probleme wurde behoben.<br> +<br> +-Wenn bereits alle Voraussetzungen für eine in Patch 3.5 eingeführte Errungenschaft von Sammlern oder Handwerkern vor dem Patch erfüllt worden war, so wurde die Errungenschaft nicht korrekt anerkannt.<br> +*Das bekannte Problem mit der Minenarbeiter-Errungenschaft wird weiterhin untersucht. Wir bedanken uns für eure fortgesetzte Geduld während wir an einer Lösung dieses Problems arbeiten.<br> +<br> +-Für alle Transaktionen am Marktbrett wurde die gleiche Steuer erhoben, unabhängig von dem Stadtstaat, in dem der Gegenstand verkauft beziehungsweise gekauft wurde.<br> +<br> +-Ein Gesuch in der Gruppensuche konnte unter bestimmten Bedingungen nicht mehr vom Gruppenanführer bearbeitet werden.<br> +<br> +-In Wohnbezirken wurde die Orientierung des Zielrings von Trainingspuppen nicht korrekt dargestellt, nachdem die Position der Trainingspuppe geändert worden war.<br> +<br> +・Das Angeln im Äußeren La Noscea konnte den Server unter bestimmtem Bedingungen zum Abstürzen bringen.<br> +*Als vorrübergehende Lösung des Problems, haben wir die Möglichkeit zu angeln am Bronzesee-Nordufer im Äußeren La Noscea deaktiviert. Das Problem wird im Laufe der nächsten Woche behoben. Währenddessen wollen wir euch darauf hinweisen, dass dieselben Fische auch am Bronzesee-Nordufer im Oberen La Noscea geangelt werden können. Wir bitten euch daher stattdessen dort zu angeln, bis das Problem behoben wird.<br> +<br> +-Der Server konnte unter bestimmten Bedingungen abstürzen.<br> +Wartung aller Welten (19. Jan.): Früher Abgeschlossen148481505714848151093c04f5037bebcfb09f2afaeff044ee251dee4dc7Follow-up2Die Wartung wurde früher als geplant abgeschlossen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 19. Jan. 2017 von 7 Uhr bis 9:30 Uhr (MEZ) <br> +<br> +[Betroffene Welten]<br> +Alle Welten<br> +Technische Schwierigkeiten des Aether Datenzentrums behoben (18. Jan.)14847832531484784150e67a53c035d1b439ef9498c1167e38a64c893db5Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit dem Aether Datenzentrum behoben wurden. <br> +<br> +* Der Weltentransferdienst kann nun wieder verwendet werden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Vom 18. Jan. 2017 um 21:27 Uhr bis zum 19. Jan. 2017 um 0:25 Uhr (MEZ) <br> +<br> +[Details] <br> +・Einloggen war nicht möglich.<br> +・Areale konnten nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten war nicht möglich.<br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Aether Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・ZaleraNotwartung des Aether-Datenzentrums (18. Jan.)148477503614847755821a5b75eb42176623b4f5f8c99d16a5ad35e36f34Maintenance2<br> +Wir werden eine Notwartung aller Welten des Aether- Datenzentrums durchführen.Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Vom 18. Jan. 2017 um 22:40 Uhr bis zum 19. Jan. 2017 um 0:10 Uhr (MEZ)<br> +<br> + [Betroffene Welten]<br> +Alle Welten des Aether-Datenzentrums:<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Zalera<br> +Technische Schwierigkeiten des Aether Datenzentrums (18. Jan.)148477495314847749709d10ff3fd068401c528a300c890d0f7b155a178a4Gegenwärtig gibt es technische Schwierigkeiten mit den untenstehenden Welten.<br> +Das Problem wird derzeit untersucht und neue Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 18. Jan. 2017 um 21:27 Uhr (MEZ)<br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen<br> +・Wiedereinloggen ist nicht möglich<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Aether Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Cactuar<br> +・Coeurl<br> +・Faerie<br> +・Gilgamesh<br> +・Goblin<br> +・Jenova<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・ZaleraBekannte Probleme nach Patch 3.5 (18. Jan.)148474508714847451630b4fb28f632dd28fd45d976f1f6974fadfe12e301Wir konnten nach der Veröffentlichung von Patch 3.5 die folgenden Fehler feststellen.<br> +Die Fehler werden untersucht und wir bitten euch um Geduld und Verständnis, während wir an einer Lösung arbeiten.<br> +<br> +[Bekannte Probleme]<br> +<br> +- Wenn das Verhältnis zu einem Wilden Stamm der 3.0 Gebiete über „Verbündet“ liegt, wird die Option „Waren (Verbündet)“ nicht angezeigt und Gegenstände bei den Wilden Stämmen der 2.0 Gebiete können nicht erworben werden.<br> +*Spieler, deren Verhältnis zu den Wilden Stämmen der 2.0 Gebiete unter „Verbündet“ liegt, sind nicht von diesem Problem betroffen.<br> +<br> +- Der Gegenstand „Mörchel“ kann unter Umständen nicht bei einer Scheine-Wechslerin in Frohehalde eingetauscht werden, wenn der Bonus (★-Marke) angezeigt ist.<br> +<br> +- Die Name der folgenden Notenrolle ist falsch:<br> +(Falsch) Notenrolle von „Bathed in Woodsin“<br> +<br> +- Der Abstand zwischen den Kommandoslots des Kommandomenüs ist zu groß, wenn die Anzeige auf [1x12] eingestellt ist.<br> +<br> +- Das Wechseln einer Zone während man Details der Gruppensuche bearbeitet kann unter Umständen zu Fehlanzeigen der Gruppendetails führen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +Wartung aller Welten (19. Jan.)148471772414848154329fc7402cb10ac8c1d7245a20ea2fcda39c33705eMaintenance2Zur folgenden Zeit werden wir, begleitend zum Patch 3.5 Hotfix, eine Wartung aller Welten durchführen. Während dieses Zeitraums wird FINAL FANTASY XIV nicht spielbar sein.<br> +<br> +* 30 Minuten vor Beginn der Wartung wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Zeit]<br> +Am 19. Jan. 2017 von 7 Uhr bis 11 Uhr (MEZ)<br> +* Änderungen der Wartungszeiten vorbehalten. <br> +<br> +[Betroffene Welten] <br> +Alle Welten Bekannte Probleme mit Errungenschaften nach Patch 3.5 (17. Jan.)14846585861484833226c49238e29e77fbee71c52f7aa5522f87aa0a7e7d1Wir konnten nach der Veröffentlichung von Patch 3.5 die folgenden Fehler feststellen.<br> +<br> +- Spieler können Errungenschaften für Handwerker und Sammler die in Patch 3.5 implementiert wurden nicht freischalten falls die Bedingungen hierfür vor dem Patch erfüllt wurden.<br> +<br> +- Spieler können momentan nicht eine Errungenschaft des Minenarbeiters freischalten, da die Vorraussetzungen fehlerhaft sind.<br> +<br> +Die Probleme werden untersucht und wir bitten euch um Geduld und Verständnis, während wir an einer Lösung arbeiten.<br> +<br> +*Update vom 19. Jan. 2017 um 14:10 Uhr (MEZ): <br> +Das folgende Problem wurde in der Wartung am 19. Jan. 2017 von 7 bis 9:30 Uhr (MEZ) behoben: <br> +Errungenschaften für Handwerker und Sammler, die in Patch 3.5 implementiert wurden, wurden nicht korrekt freigeschaltet.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten.<br> +Gruppensuche Rekrutierungsfunktion Notwartung (17.Jan.)14846540631484655546d2502bcf1ebf0401348175ae53930c8394f88484Maintenance2Zur Behebung des Fehlers beim Rekrutieren über die Gruppensuche, wodurch manche Welten nicht angezeigt wurden, werden wir eine Notwartung an der Gruppensuche durchführen. Während dieser Wartung wird die Gruppensuche nicht verfügbar sein.<br> +<br> +*Alle anderen Funktionen sind weiterhin nutzbar.<br> +<br> +Desweiteren werden beim Start der Notwartung alle Gruppen die über die Gruppensuche zusammengestellt wurden aufgelöst werden. Wir entschuldigen uns für etwaige Unannehmlichkeiten die hierdurch entstehen können.<br> +<br> +*Alle Gruppen die durch die Gruppensuche zusammengestellt wurden und in einem Inhalt beigetreten sind werden beim beenden des Inhalts aufgelöst werden.<br> +<br> +[Datum & Uhrzeit]<br> +17. Jan, 2017 13:00 bis 13:15 Uhr (MEZ)<br> +<br> +[Betroffene Funktionen]<br> +FINAL FANTASY XIV Gruppensuche<br> +Bekannte Probleme nach Patch 3.5 (17. Jan.)14846530351484663994443e4d057578f445d28ae686a15102282dc892581Wir konnten nach der Veröffentlichung von Patch 3.5 den folgenden Fehler feststellen.<br> +Der Fehler wird untersucht und wir bitten euch um Geduld und Verständnis, während wir an einer Lösung arbeiten.<br> +<br> +[Bekanntes Problem]<br> +Beim Rekrutieren über die Gruppensuche, wenn andere Welten des Datenzentrums involviert sind werden manche Welten nicht in der Liste angezeigt.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für eure Geduld.<br> +<br> +*Update: 17. Jan. 2017<br> +Dieses Problem wurde während der Notwartung der Gruppensuche am 17. Jan. 2017 zwischen 13:00 und 13:15 Uhr (MEZ) behoben.<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten die durch dieses Problem entstanden sind und danken euch für eure Geduld.<br> +Bezüglich des Libra Eorzea Updates und der Einstellung gewisser Funktionen (19. Jun.): Folgenews 614846473491484647430938701fd2bdd12d1f51708b8be5f9ccce5ee25ebFollow-up1Zur unten angegebenen Zeit wurde eine Aktualisierung der iOS/Android App "FINAL FANTASY XIV: Libra Eorzea" mit den Informationen von Patch 3.5 durchgeführt.<br> +<br> +Bei dieser Aktualisierung handelt es sich um eine temporäre Maßnahme um die andauernde Entwicklungsphase einer neuen App zu überbrücken, welche "FINAL FANTASY XIV: Libra Eorzea" letztendlich ersetzen wird, weshalb die Login-Funktion weiterhin deaktiviert ist.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Bekannte Probleme]<br> +Die Beschreibungen zu den Voraussetzungen um „PvP“ über Zufallsinhalt aus dem Menü beizutreten, werden inkorrekt dargestellt.<br> +- A Realm Reborn > The Fold (zufällige Gruppe)<br> +- Heavensward > The Feast (8 gegen 8, zufällige Gruppe)<br> +- Heavensward > The Feast (4 gegen 4, Solo)<br> +<br> +[Datum & Uhrzeit]<br> +Am 17. Jan. 2016 um 9:45 Uhr (MEZ)<br> +<br> +[Betroffener Dienst]<br> +・Alle iOS/Android "FINAL FANTASY XIV: Libra Eorzea" Nutzer<br> +<br> +* Bitte folgt dem untenstehenden Link für weitere Informationen:<br> +<a href="http://sqex.to/9aL">http://sqex.to/9aL</a>Update des Lodestone (17. Jan.)14846454441484645547dec978253918982a6a58f1f5b7e21c813ff764f33Ein Update des Lodestone wurde durchgeführt.<br> +<br> +[Datum & Uhrzeit]<br> +Am 17. Jan. 2017 um 9:30 Uhr (MEZ) <br> +<br> +Bitte folgt dem untenstehenden Link für weitere Informationen:<br> +<a href="http://sqex.to/Nq-">http://sqex.to/Nq-</a><br> +Wartung des Lodestone (17. Jan.): Abgeschlossen 1484644838148464511066aa51f14b963ae0e854137c9d3ebad519672cfbFollow-up2Die Folgewartung des Lodestones begleitend zu Patch 3.5 wurde um 9:30 Uhr (MEZ) abgeschlossen.<br> +<br> +[Datum & Uhrzeit]<br> +Am 17. Jan. 2017 von 7:00 Uhr bis um 9:30 Uhr (MEZ)<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.Wartung aller Welten (16.-17. Jan.): Früher Abgeschlossen14846409511484641212deb9057ce5469aab8365051945efbfdaad2028e0Follow-up2Die Wartung wurde früher als geplant abgeschlossen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Vom 16. Jan. 2017 um 11 Uhr bis zum 17. Jan. 2017 um 9:15 Uhr (MEZ) <br> +<br> +[Betroffene Welten und Dienste]<br> +Alle Welten <br> +Mogry-StationFINAL FANTASY XIV Versions-Update (17. Jan.)14846313001484631606d86726a7d66ae042f62e233559d1855179d957c43Ein FINAL FANTASY XIV Versions-Update wurde durchgeführt. <br> +<br> +* Clients werden nach deren Starten automatisch aktualisiert. <br> +<br> +[Datum & Uhrzeit] <br> +Am 17. Jan. 2017 um 6 Uhr (MEZ) <br> +<br> +Bitte folgt dem untenstehenden Link für weitere Informationen:<br> +<a href="http://sqex.to/X2S">http://sqex.to/X2S</a><br> +Technische Probleme des Support Centers behoben (Jan. 16)1484566066148456620204ec6f791eecb31f64216ea1999e449bc126ba2aRecovery4Zur untenstehenden Uhrzeit gab es technische Schwierigkeiten wodurch Spieler nicht auf das SQUARE ENIX Support Center zugreifen konnten.<br> +<br> +Wir freuen uns euch mitteilen zu können, dass die untenstehenden technischen Schwierigkeiten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> + <br> +[Datum & Uhrzeit]<br> +Am 16. Jan. 2017 von 9:51 Uhr bis 10:16 Uhr (MEZ)<br> + <br> +[Details]<br> +SQUARE ENIX Support Center war zeitweise nicht verfügbar<br> +<br> +[Ursache] <br> +Probleme mit der Server-Konfiguration<br> +Technische Schwierigkeiten aller Welten des NA Datenzentrums behoben (16. Jan.)1484544724148454620191487c69c7ccd5aaa63d623e370dca3bbfbabd02Recovery4Während der unten angegebenen Zeit kam es zu technischen Schwierigkeiten mit Welten des NA Datenzentrums, aufgrund derer es bei Spielern zu Verbindungsabbrüchen und -Problemen beim Wiedereinloggen in bestimmten instanzierten Inhalten kam. <br> +<br> +Wir freuen uns euch mitteilen zu können, dass dieses Problem behoben wurde und entschuldigen uns für etwaige Unannehmlichkeiten. <br> +<br> +[Datum & Zeit] <br> +Am 16. Jan. 2017 von 4:01 Uhr bis 4:53 Uhr (GMT)<br> +<br> +[Details] <br> +・Verbindungsabbrüche und -Probleme beim Wiedereinloggen in bestimmten instanzierten Inhalten<br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welten] <br> +Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Technische Schwierigkeiten aller Welten des NA Datenzentrums(16. Jan.)148454348614845436329386dcb400181d2bbc9d96f8e2c04385a95f739e4<br> +Gegenwärtig gibt es technische Schwierigkeiten mit allen Welten des NA Datenzentrums, aufgrund derer Spieler Verbindungsabbrüche und -Probleme beim wieder Einloggen in bestimmen instanzierten Inhalten haben.<br> +<br> +Das Problem wird derzeit untersucht und neue Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 16. Jan. 2017 um 4:01 Uhr (GMT)<br> +<br> +[Details] <br> +・Verbindungsabbrüche und Probleme beim wieder Einloggen in bestimmen instanzierten Inhalten<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +FINAL FANTASY XIV Bestimmungen für die Verwendung von Unterlagen überarbeitet 14842974701484297537f8e0d4b8671ead323fc775ca7c5367873f46ade91Die FINAL FANTASY XIV Bestimmungen für die Verwendung von Unterlagen wurden überarbeitet, um das Album "FINAL FANTASY XIV : Duality ~Arrangement Album~" (erschienen am 7. Dez. 2016) als verwendbare Unterlage miteinzuschließen.<br> +<br> +Die FINAL FANTASY XIV Bestimmungen für die Verwendung von Unterlagen können auf dem Square Enix Support-Center gefunden werden, siehe untenstehende Adresse. Stellt bitte sicher, die Regeln für die Nutzung der Unterlagen genau durchzulesen bevor ihr lizensierte Unterlagen nutzt.<br> +<a href="http://sqex.to/V9-">http://sqex.to/V9-</a><br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (12. Jan.)148420896214842090078204bed945ac94f2a19b290fce2b1f2d9a4294bd1RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 5. Jan. 2017 bis zum 11. Jan. 2017<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 433<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 38,472<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +・Konten, die Sanktionen aufgrund von Teilnahme an RMT/unrechtmäßigen Aktivitäten erhielten: 11<br> +・Konsequenz: Temporäre Suspendierung von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +Wartung des Lodestone (16-17. Jan.)14839584001484644142c56051f06010e26d8e56cc4654f34a81286e050bMaintenance2Begleitend zur Einführung von Patch 3.5, werden wir zu untenstehenden Zeiten eine Wartung des Lodestone durchführen.<br> +Dies wird gewisse Auswirkungen auf Dienste von FINAL FANTASY XIV haben, weiteres dazu unten.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Zeit]<br> +<br> +Am 16. Jan. 2017 von 11 Uhr bis 19 Uhr (MEZ)<br> +Einzelheiten: <br> +- Wartung des Lodestone zur Einführung von Patch 3.5<br> +<br> +Auswirkungen: Spielern wird es nicht möglich sein, sich in den Lodestone einzuloggen *Wenn ihr euch vor der Wartung auf der Hauptseite eures Charakters auf dem Lodestone einloggt, werdet ihr während der Wartung eingeloggt bleiben können.<br> +*Informationen zu Charakteren der EU Datenzentren auf dem Lodestone werden während der Wartung nicht verfügbar sein und werden nach der Wartung graduell aktualisiert werden, was unter Umständen etwas dauern wird.<br> +<br> +Vom 16. Jan. 2017 um 19 Uhr bis zum 17. Jan. 2017 um 11 Uhr (MEZ)<br> +Einzelheiten:<br> +- Systemupdates für den Lodestone begleitend zu Patch 3.5 für FINAL FANTASY XIV <br> +<br> +Auswirkungen: Spielern wird es nicht möglich sein auf den Lodestone zuzugreifen *Neuigkeiten und Artikel werden trotzdem zur Verfügung stehen.<br> +<br> +*Änderung der Wartungszeiten vorbehalten.<br> +<br> +[Betroffene Dienste]<br> +・Der Lodestone<br> +Wartung aller Welten (16-17. Jan.)148395812114846418918d4f1e506b9e837b83e42dc2c1aa51d5acdb95f4Maintenance2Zur folgenden Zeit werden wir, begleitend zu Patch 3.5, eine Wartung durchführen. Während dieses Zeitraums wird FINAL FANTASY XIV nicht spielbar sein. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit]<br> +Vom 16. Jan. 2017 um 11 Uhr bis zum 17. Jan. 2017 um 11 Uhr (MEZ)<br> +* Änderungen der Wartungszeiten vorbehalten.<br> +<br> +[Betroffene Welten]<br> +Alle Welten<br> +<br> +* Die folgenden Dienste der Mogry-Station werden 30 Minuten vor dem Beginn der Wartung bis zu deren Abschluss nicht verfügbar sein.<br> +・Weltentransferdienst<br> +・Charakterumbenennung<br> +・Mogry-Kiosk<br> +・Eingabe von Gegenstands- und Geschenkcodes<br> +<br> +* Beachtet bitte, dass es Spielern nicht möglich sein wird, Zeremonien des Ewigen Bundes vom Ende der Wartung bis zum 20. Januar 2017 um 10 Uhr (MEZ) zu reservieren.Technische Schwierigkeiten des Chaos Datenzentrums behoben (8. Jan.)14838912911483891339a6e00e05dfeda8c39d7ed57b2cd2daff6f3c6d4fRecovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 8. Jan. 2017 von 16:22 Uhr bis 16:41 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Chaos Datenzentrums<br> +・Cerberus<br> +・Lich<br> +・Moogle<br> +・Odin<br> +・Phoenix<br> +・Ragnarok<br> +・Shiva<br> +・ZodiarkVorübergehende Aussetzung des europäischen GM-Hilfsdiensts (6. Jan.)148363832114836383733f48955f68a7ee3ce28dc0fc23cce04158a3d2bc1Zur folgenden Zeit wird der GM-Hilfsdienst aufgrund einer internen Netzwerk-Wartung nicht zur Verfügung stehen. Die europäischen GMs werden nicht in der Lage sein, eure Anfragen zu bearbeiten. <br> + <br> +Für dringende Angelegenheiten ist der nordamerikanische GM-Dienst wie gewohnt verfügbar und Spieler werden weiterhin in der Lage sein, Anfragen zu senden. Seid euch bitte dessen bewusst, dass der Dienst nur in Englisch verfügbar sein wird und es zu Verzögerungen kommen kann, bis die GM-Anfragen beantwortet werden.<br> + <br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken für euer Verständnis.<br> + <br> +[Datum & Zeit]<br> +Vom 6. Jan. 2017 um 19 Uhr bis zum 9. Jan. 2017 um 10:30 Uhr (MEZ)<br> + <br> +[Betroffene Dienste]<br> +Die europäischen GM-Hilfsdienste für FINAL FANTASY XI und FINAL FANTASY XIV<br> + <br> +[Ursache]<br> +Interne NetzwerkwartungMaßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (5. Jan.)148360538914836055173ee72d31a08dfb77390d60be3616916b676652351RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 22. Dez. 2016 bis zum 4. Jan. 2016<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 1,232<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 47,989<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +Technische Schwierigkeiten des Gaia Datenzentrums behoben (3. Jan.)14834346321483434718c90d9d434860849c4d836be84df4f0f90ae0cba0Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 3. Jan. 2017 um 8:54 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Gaia Datenzentrums<br> +・Alexander<br> +・Bahamut<br> +・Durandal<br> +・Fenrir<br> +・Ifrit<br> +・Ridill<br> +・Tiamat<br> +・Ultima<br> +・Valefor<br> +・Yojimbo<br> +・Zeromus<br> +Technische Schwierigkeiten des Gaia Datenzentrums behoben (30. Dez.)148308531814830853846ec3699a3b6148e43b95456f5b012bd965b70d0cRecovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 30. Dez. 2016 von 8:20 Uhr bis 8:44 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Gaia Datenzentrums<br> +・Alexander<br> +・Bahamut<br> +・Durandal<br> +・Fenrir<br> +・Ifrit<br> +・Ridill<br> +・Tiamat<br> +・Ultima<br> +・Valefor<br> +・Yojimbo<br> +・Zeromus<br> +Technische Schwierigkeiten auf Midgardsormr behoben (29. Dez.)148305257114830527201a3d4236618d83b45e6e100b175e3fcbf3fe75abRecovery4<br> +Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit der untenstehenden Welt behoben wurden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Am 29. Dez. 2016 von 20:07 Uhr bis 21:02 Uhr (MEZ) <br> +<br> +[Details] <br> +・Spielern war es nicht möglich, Midgardsormr zu betreten oder dorthin transferiert zu werden <br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welt] <br> +Midgardsormr<br> +<br> +Technische Schwierigkeiten auf Midgardsormr (29. Dez.)14830404651483040609fbe0206a5abf9e23536ec01e6af617cffd08eafd4<br> +Gegenwärtig gibt es technische Schwierigkeiten mit der untenstehenden Welt. <br> +Das Problem wird derzeit untersucht und weitere Informationen werden bekanntgegeben, sobald sie verfügbar sind. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für eure Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 29. Dez. 2016 um 20:07 Uhr (MEZ)<br> +<br> +[Details] <br> +・Es ist nicht möglich, Midgardsormr zu betreten oder dorthin transferiert zu werden<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welt] <br> +Midgardsormr<br> +Wartung der Mogry-Station (27. Dez.) 14826282001482802774175a341b8929b1609b272d8194e7326415a69af9Maintenance2Zur folgenden Zeit werden wir eine Wartung durchführen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit]<br> +Am 27. Dez. 2016 von 1:45 Uhr bis 2:45 Uhr (MEZ) <br> +<br> +[Betroffene Dienste der Mogry-Station]<br> +・Charakterumbenennung<br> +・Mogry-Kiosk<br> +・Eingabe von Gegenstands- und Geschenkcodes<br> +Technische Schwierigkeiten mit verschiedenen Arealen auf Leviathan behoben (24. Dez.)14825739751482574049a44c77ce05aa4ef6aaec255b1da7904d9b9c58d62Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit der untenstehenden Welt behoben wurden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für eure Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Am 24. Dez. 2016 von 10:04 Uhr bis 10:29 Uhr (MEZ) <br> +<br> +[Details] <br> +・Die unten angegebenen Areale der Welt Leviathan waren unzugänglich<br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welt] <br> +・Untere Decks<br> +・Zentrales La Noscea<br> +・Unteres La Noscea<br> +・Östliches La Noscea<br> +・Nald-Kreuzgang<br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (22. Dez.)14823954381482395640528714b20f35941075e65153c267cc632daf99de1RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 15. Dez. 2016 bis zum 21. Dez. 2016<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 17594<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 8<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +・Konten, die Sanktionen aufgrund von Teilnahme an RMT/unrechtmäßigen Aktivitäten erhielten: 394<br> +・Konsequenz: Temporäre Suspendierung von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +Technische Schwierigkeiten des NA Datenzentrums behoben (20. Dez.)148222235314822224346d44f28f940a6b2d83bf07fd70bf0f9d51e36786Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 20. Dez. 2016 von 6:10 Uhr bis 7 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu den Welten des NA Datenzentrums unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Betroffene Welten] <br> +Alle Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Wartung des „PSN“ (19. Dez.)14819552111482134825b964ce611f1c80b0d4c2228eb17f2bfa1af3f553Maintenance2Sony Interactive Entertainment wird eine Wartung des PlayStation® Network durchführen. <br> +<br> +* Bitte beachtet, dass bereits eingeloggte Spieler weiterhin spielen können, solange sie sich nicht ausloggen. <br> +<br> +[Datum & Uhrzeit] <br> +Am 19. Dez. 2016 von 7 Uhr bis 8 Uhr (MEZ) <br> +・Spieler können sich nicht in die PlayStation®3 Version von FINAL FANTASY XIV einloggen <br> +<br> +Am 19. Dez. 2016 von 8 Uhr bis 9 Uhr (MEZ) <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggen<br> +SQUARE ENIX Support-Center Feiertags-Öffnungszeiten148189536014818954348388c2857ef5e195f0318bd122fe0cbf90af5f031Aufgrund der bevorstehenden Feiertage wird das europäische SQUARE ENIX Support-Center zu folgenden Zeiten zur Verfügung stehen.<br> +<br> +[Datum]<br> +19. Dezember bis 22. Dezember: Geöffnet von 10 bis 19 Uhr (MEZ)<br> +23. Dezember: Geöffnet von 10 bis 18 Uhr (MEZ)<br> +24. Dezember bis 27. Dezember: Geschlossen<br> +28. Dezember bis 30. Dezember: Geöffnet von 10 bis 19 Uhr (MEZ)<br> +31. Dezember bis 2. Januar: Geschlossen<br> +<br> +Bitte beachtet, dass es während dieser Zeit zu Verzögerungen beim Beantworten von Anfragen kommen kann. Der Dienst wird vom 3. Jan. 2017 ab 10 Uhr (MEZ) normal weitergeführt werden. Vielen Dank für euer Verständnis.<br> +<br> +Der FINAL FANTASY XI und FINAL FANTASY XIV GM Support wird in dieser Zeit wie gewohnt zur Verfügung stehen.<br> +<br> +Das SQUARE ENIX Support-Center wünscht euch frohe Weihnachten und dankt euch für eure Kooperation.<br> +<br> +SQUARE ENIX Support-Center<br> +<a href="http://support.eu.square-enix.com/de">http://support.eu.square-enix.com/de</a>Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (15. Dez.)1481791966148179203702f39c207e7bcfcc90802ad6a2ddaa8f50442a8e1RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 8. Dez. 2016 bis zum 15. Dez. 2016<br> +<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: <br> +・Konten, die eine permanente Account-Suspendierung von erhalten haben: 24,685<br> +・Konten, die eine zeitweisen Account-Suspendierung von erhalten haben: 14<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten:<br> +・Konten, die eine permanente Account-Suspendierung erhalten haben: 1,029<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +Technische Schwierigkeiten des „PSN“ behoben (13. Dez.) 14816412131481641482b4dffdd89746619b69c0dca9b7d2256019528332Recovery4Wir freuen uns euch mitteilen zu können, dass die untenstehenden technischen Schwierigkeiten behoben wurden.<br> +<br> +[Datum & Uhrzeit] <br> +Am 13. Dez. 2016 von 13:21 Uhr bis 14:25 Uhr (MEZ) <br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggen<br> +Technische Schwierigkeiten des „PSN“ (13. Dez.)14816375141481638312660a227038126a0b87273b7fc79fc66ea1c71e434Gegenwärtig gibt es technische Schwierigkeiten mit dem Sony Interactive Entertainment-Dienst „PSN“.<br> +<br> +Weitere Informationen werden bekanntgegeben, sobald diese verfügbar sind.<br> +<br> +* Bitte beachtet, dass bereits eingeloggte Spieler weiterhin spielen können, solange sie sich nicht ausloggen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 13. Dez. 2016 um 13:21 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggen<br> +Technische Schwierigkeiten aller Welten des NA Datenzentrums behoben (12. Dez.)1481622877148162292247a9ab642c10d5f853f652686e9c5ef028f5cdceRecovery4Während der unten angegebenen Zeit kam es zu technischen Schwierigkeiten mit Welten des NA Datenzentrums, aufgrund derer es bei Spielern zu Verbindungsabbrüchen und -Problemen beim Wiedereinloggen in bestimmten instanzierten Inhalten kam. <br> +<br> +Wir freuen uns euch mitteilen zu können, dass dieses Problem behoben wurde und entschuldigen uns für etwaige Unannehmlichkeiten. <br> +<br> +[Datum & Zeit] <br> +Vom 12. Dez. 2016 um 19:06 Uhr bis zum 13. Dez. 2016 um 2:17 Uhr (MEZ)<br> +<br> +[Details] <br> +・Unable to log into or may be disconnected from NA Data Center Worlds<br> +・Unable to log into or may not be able to view your character(s) on The Lodestone<br> +・Unable to view your character(s) in the Mog Station<br> +<br> +[Betroffene Welten] <br> +Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +<br> +[Betroffene Dienste]<br> +・Der Lodestone <br> +・Mogry-StationFINAL FANTASY XIV Versions-Update (13. Dez.)14816183111481618363da0827e127079cd39388b42c19c9dd63331a5d383Ein FINAL FANTASY XIV Versions-Update wurde durchgeführt. <br> +<br> +* Clients werden nach deren Starten automatisch aktualisiert. <br> +<br> +[Datum & Uhrzeit] <br> +Am 13. Dez. 2016 um 8:40 Uhr (MEZ) <br> +<br> +[Update-Details]<br> +<br> +◆FINAL FANTASY XIV Hotfixes (13. Dez.)◆<br> +<br> +■Das folgende Problem wurde behoben.<br> +<br> +・Das Wachstum von Pflanzen in Gartenbeeten wurde unter Umständen angehalten.<br> +<br> +*Alle Pflanzen, die von diesem Problem betroffen waren, wurden angepasst und werden mit der nächsten Wartung zur Ernte bereit sein. Wie gewohnt tritt nach einer gewissen Zeit die Verwelkung der Pflanze ein, weshalb wir euch bitten eure Pflanzen möglichst zeitnah zu ernten.<br> +Netzwerkbedingte Technische Schwierigkeiten aller NA Welten (13. Dez.) Folgenews 31481598317148159836135a897edcb5fc7dc0b39f651ff08e646d30ae71eFollow-up4Bezüglich der technischen Schwierigkeiten mit dem Internetanbieter der für den Upstream für die Welten des nordamerikanischen Datenzentrums zuständig ist und die zu Verbindungsproblemen führten.<br> +<br> +Spieler des nordamerikanischen Datenzentrums können sich weiterhin nicht mehr verbinden oder verlieren die Verbindung zu den Servern.<br> +<br> +Das Problem wird derzeit untersucht und neue Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 12. Dez. 2016 um 19:06 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen<br> +・Wiedereinloggen ist nicht möglich<br> +<br> +[Betroffene Welten] <br> +Alle nordamerikanischen Welten<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Netzwerkbedingte technische Schwierigkeiten aller NA Welten (13. Dez.): Folgenews 2148158928114815895916863b92cb230d5e626323e49cf15929c34e4cc62Follow-up4Bezüglich der technischen Schwierigkeiten mit dem Internetanbieter der für den Upstream für die Welten des nordamerikanischen Datenzentrums zuständig ist und die zu Verbindungsproblemen führten.<br> +<br> +Seit dem 13. Dez. 2016 um 0:25 Uhr (MEZ), können sich Spieler des nordamerikanischen Datenzentrums möglicherweise nicht mehr verbinden oder verlieren die Verbindung zu den Servern.<br> +<br> +Das Problem wird weiterhin überwacht und mehr Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 12. Dez. 2016 um 19:06 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen<br> +・Wiedereinloggen ist nicht möglich<br> +<br> +[Betroffene Welten] <br> +Alle nordamerikanischen Welten<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Netzwerkbedingte technische Schwierigkeiten aller NA Welten (12. Dez.): Folgenews14815820391481590276f0b362f0566bcd065d9d9eac916b79aa61e38b1aFollow-up4Bezüglich der technischen Schwierigkeiten mit dem Internetanbieter der für den Upstream für die Welten des nordamerikanischen Datenzentrums zuständig ist und die zu Verbindungsproblemen führten.<br> +<br> +Das Problem wird derzeit weiterhin überwacht, die Verbindung sollte mittlerweile jedoch hauptsächlich stabil sein.<br> +<br> +Mehr Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 12. Dez. 2016 um 19:06 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen<br> +・Wiedereinloggen ist nicht möglich<br> +<br> +[Betroffene Welten] <br> +Alle nordamerikanischen Welten<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Netzwerkbedingte technische Schwierigkeiten aller NA Welten (12. Dez.)1481572039148157220028a5ea03a48c12d27183198e817f2e439f9eaaa04Gegenwärtig gibt es technische Schwierigkeiten mit dem Internetanbieter der für den Upstream für die Welten des nordamerikanischen Datenzentrums zuständig ist.<br> +<br> +Das Problem wird derzeit untersucht und neue Informationen werden bekanntgegeben, sobald diese zur Verfügung stehen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 12. Dez. 2016 um 19:06 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen<br> +・Wiedereinloggen ist nicht möglich<br> +<br> +[Betroffene Welten] <br> +Alle nordamerikanischen Welten<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Technische Schwierigkeiten des „PSN“ behoben (12. Dez.) 148156529214815657611078ee7a64dd51e29473811def0a3e5966aaac9dRecovery4Wir freuen uns euch mitteilen zu können, dass die untenstehenden technischen Schwierigkeiten behoben wurden.<br> +<br> +[Datum & Uhrzeit] <br> +Am 12. Dez. 2016 von 17:37 Uhr bis 18:06 Uhr (MEZ) <br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggen<br> +Technische Schwierigkeiten des „PSN“ (12. Dez.)148156366514815637141e4b3a530e24d0886253d7c7ce2dbb8ae9da6d934Gegenwärtig gibt es technische Schwierigkeiten mit dem Sony Interactive Entertainment-Dienst „PSN“.<br> +<br> +Weitere Informationen werden bekanntgegeben, sobald diese verfügbar sind.<br> +<br> +* Bitte beachtet, dass bereits eingeloggte Spieler weiterhin spielen können, solange sie sich nicht ausloggen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 12. Dez. 2016 um 17:37 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggen<br> +Technische Schwierigkeiten mit verschiedenen Arealen auf Typhon behoben (11. Dez.)14814638301481464055f7d8bdf8e21834d958183a76b45fe291e419ee88Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit der untenstehenden Welt behoben wurden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für eure Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Am 11. Dez. 2016 von 14:14 Uhr bis 14:21 Uhr (MEZ) <br> +<br> +[Details] <br> +・Die unten angegebenen Areale der Welt Typhon waren unzugänglich<br> +<br> +[Ursache] <br> +Probleme mit der Serverausrüstung<br> +<br> +[Betroffene Welt] <br> +・Östliches Thanalan<br> +・Südliches Thanalan<br> +・Nördliches Thanalan<br> +・Zentrales Hochland von CoerthasWartung aller Welten (13. Dez.)148143272914816230083117acbbc1bc889326da8d21986588e66c9cfb56Maintenance2Zur folgenden Zeit werden wir eine Wartung durchführen um Probleme zu beheben. Während dieses Zeitraums wird FINAL FANTASY XIV nicht spielbar sein. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zum Ende dessen wird der Weltentransferdienst nicht zur Verfügung stehen. <br> +<br> +* Beachtet bitte, dass es Spielern nicht möglich sein wird, Zeremonien des Ewigen Bundes vom Ende der Wartung bis zum 16. Dezember 2016 um 10 Uhr (MEZ) zu reservieren.<br> +<br> +[Datum & Uhrzeit]<br> +Am 13. Dez. 2016 von 6 Uhr bis 11 Uhr (MEZ)<br> +* Änderungen der Wartungszeiten vorbehalten.<br> +<br> +[Betroffene Welten]<br> +Alle Welten<br> +Technische Schwierigkeiten des NA Internetanbieters behoben (9. Dez.)148136951014813695941b5a8c9edbb602a79dbcbb34e86303f8f490ea1bRecovery4Zur folgenden Zeit gab es Verbindungsprobleme mit bestimmten Routen des nordamerikanischen Internetanbieters.<br> +<br> +Wir freuen uns euch mitteilen zu können, dass diese technischen Schwierigkeiten behoben wurden.<br> +<br> +[Datum & Uhrzeit] <br> +Vom 9. Dez. 2016 um 19:40 Uhr bis zum 10. Dez. 2016 um 11:18 Uhr (MEZ) <br> +<br> +[Details] <br> +・Spielern, die bestimmte Internetanbieter nutzen, erfahren Verbindungsabbrüche und -probleme beim Wiedereinloggen <br> +<br> +[Betroffene Welten] <br> +Alle nordamerikanischen Welten<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・ZaleraNetzwerkschwierigkeiten aller Welten des NA Datenzentrums (9. Dez.) Folgenews1481327426148132771476d5a27c1da52f45d762b08b700fdd8fad9ed7c5Follow-up4Dies ist ein Update bezüglich der auftretenden Netzwerkschwierigkeiten aller Welten des NA Datenzentrums, aufgrund welcher Spieler Verbindungsabbrüche und -probleme beim Wiedereinloggen erfahren. Dieses Problem ist nun zum Großteil gelöst und eine stabile Verbindung sollte nun wieder möglich sein. <br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 9. Dez. 2016 um 19:40 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spielern, die bestimmte Internetanbieter nutzen, erfahren Verbindungsabbrüche und -probleme beim Wiedereinloggen<br> +<br> +[Ursache] <br> +Verbindungsprobleme<br> +<br> +[Betroffene Welten] <br> +Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Netzwerkschwierigkeiten aller Welten des NA Datenzentrums (9. Dez.)14813193761481322251fc40d22866a32af110c70f63cc74f928d5bf56854Gegenwärtig treten bei Spielern, die bestimmte Internetanbieter nutzen, technische Schwierigkeiten mit allen Welten des NA Datenzentrums auf, aufgrund derer Spieler Verbindungsabbrüche und -probleme beim Wiedereinloggen erfahren.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 9. Dez. 2016 um 19:40 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spieler, die bestimmte Internetanbieter nutzen, erfahren Verbindungsabbrüche und -probleme beim Wiedereinloggen<br> +<br> +[Ursache] <br> +Verbindungsprobleme<br> +<br> +[Betroffene Welten] <br> +Welten des NA Datenzentrums<br> +・Adamantoise<br> +・Balmung<br> +・Behemoth<br> +・Brynhildr<br> +・Cactuar<br> +・Coeurl<br> +・Diabolos<br> +・Excalibur<br> +・Exodus<br> +・Faerie<br> +・Famfrit<br> +・Gilgamesh<br> +・Goblin<br> +・Hyperion<br> +・Jenova<br> +・Lamia<br> +・Leviathan<br> +・Malboro<br> +・Mateus<br> +・Midgardsormr<br> +・Sargatanas<br> +・Siren<br> +・Ultros<br> +・Zalera<br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (8. Dez.)14811852921481185350576c371d47faf6846c6432e05f968ebe8baafcae1RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 1. Dez. 2016 bis zum 7. Dez. 2016<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 36,046<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +・Konten, die Sanktionen aufgrund von Teilnahme an RMT/unrechtmäßigen Aktivitäten erhielten: 453<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.Technische Schwierigkeiten auf Shinryu behoben (3. Dez.)1480775340148077590195ca3aa1f731de6e12898bd7001f24c66ff6421fRecovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit der untenstehenden Welt behoben wurden.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Am 3. Dez. 2016 von 14:02 Uhr bis 15:05 Uhr (MEZ) <br> +<br> +[Details] <br> +・Spielern war es nicht möglich, Shinryu zu betreten oder dorthin transferiert zu werden <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welt] <br> +Shinryu<br> +Technische Schwierigkeiten auf Shinryu (3. Dez.)1480774131148077562879f5f648b4dfb3ffb7d2656cc33acb48079a85774Gegenwärtig gibt es technische Schwierigkeiten mit der untenstehenden Welt. <br> +Das Problem wird derzeit untersucht und weitere Informationen werden bekanntgegeben, sobald sie verfügbar sind. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 3. Dez. 2016 um 14:42 Uhr (MEZ)<br> +<br> +[Details] <br> +・Es ist nicht möglich, Shinryu zu betreten oder dorthin transferiert zu werden<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welt] <br> +Shinryu<br> +Maßnahmen gegen RMT und andere unerlaubte Aktivitäten im Spiel (1. Dez.)14805805711480581529cff37fccf4a91ec5245b76b675dc3b995fd349e21RMT und andere unerlaubte Aktivitäten zerstören die allgemeine Spielbalance sowie den Spaß am Spiel und werden als unrechtmäßige Aktivitäten angesehen, die gegen die Nutzervereinbarungen verstoßen.<br> +<br> +Die Anzahl der Spieler, denen unerlaubte Spielaktivitäten nachgewiesen werden konnten, ist wie folgt:<br> +<br> +Zeitraum: Vom 24. Nov. 2016 bis zum 30. Nov. 2016<br> +<br> +・Konten, die Sanktionen aufgrund von Werbung für RMT-Webseiten erhielten: 25682<br> +・Konten, die Sanktionen aufgrund von RMT/unrechtmäßiger Aktivitäten erhielten: 293<br> +・Konsequenz: Permanenter Ausschluß von FINAL FANTASY XIV<br> +<br> +Falls ihr andere Spieler beim Cheaten beobachtet, kontaktiert uns bitte direkt aus dem Spiel heraus, indem ihr vom System-Menü aus [Kundendienst / Infos] -> [Kontakt aufnehmen] -> [Unrechtmäßige Aktivitäten melden] auswählt.<br> +<br> +Wir werden weiterhin strikte Maßnahmen gegen RMT und andere unerlaubte Aktivitäten ergreifen. Bitte spielt verantwortungsbewusst, um nicht gegen die Nutzervereinbarung zu verstoßen.<br> +Wartung des Netzwerks des Europäischen Datenzentrums (2. Dez.) 148047933814806552604cd4a6cc9f61f82310786dee67c923a8b9fb6189Maintenance2Internetdienstanbieter des Europäischen Datenzentrums werden zur folgenden Zeit eine Wartung des Netzwerks durchführen. Während dieses Zeitraums könnten Spieler sich nicht zum Europäischen Datenzentrum verbinden, verlieren die Verbindung zu den Welten oder haben eine eingeschränkte Verbindungsqualität zu diesen.<br> +<br> +*Spieler können das Spiel weiterspielen, wenn sie sich neu einloggen.<br> +<br> +[Datum & Uhrzeit]<br> +Am 2. Dez. 2016 von 4 Uhr bis 6 Uhr (MEZ)<br> +<br> +[Details]<br> +・Verbindung zum Europäischen Datenzentrum nicht möglich, Verbindungsabbruch zu den Welten oder eingeschränkte Verbindungsqualität zu diesen.<br> +<br> +[Betroffene Welten]<br> +Alle Welten des Europäischen Datenzentrums <br> + \ No newline at end of file diff --git a/bin/web/icon-01.png b/bin/web/icon-01.png new file mode 100644 index 00000000..05f4243f Binary files /dev/null and b/bin/web/icon-01.png differ diff --git a/bin/web/index.html b/bin/web/index.html new file mode 100644 index 00000000..dd3231e5 --- /dev/null +++ b/bin/web/index.html @@ -0,0 +1,30 @@ + + + + + + Sapphire + + + + +
+

+

+
+ + + \ No newline at end of file diff --git a/bin/web/login.html b/bin/web/login.html new file mode 100644 index 00000000..60ae3750 --- /dev/null +++ b/bin/web/login.html @@ -0,0 +1,75 @@ + + + + FFXIV 1.0 Login + + + + + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + +
Username:
Password:
+ +
+

+
+ Create Account +
+
+
+ + + diff --git a/bin/web/news.xml b/bin/web/news.xml new file mode 100644 index 00000000..0fe6b24c --- /dev/null +++ b/bin/web/news.xml @@ -0,0 +1,162 @@ +OKit works14854473281485447484133a2a9464b7a1e1cb77ed6899fc8e813b273d294Gegenwärtig gibt es technische Schwierigkeiten mit dem Sony Interactive Entertainment-Dienst „PSN“.<br> +<br> +Weitere Informationen werden bekanntgegeben, sobald diese verfügbar sind.<br> +<br> +* Bitte beachtet, dass bereits eingeloggte Spieler weiterhin spielen können, solange sie sich nicht ausloggen.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 26. Jan. 2017 um 15:22 Uhr (MEZ)<br> +<br> +[Details] <br> +・Spieler können sich nicht in die PlayStation®3- und PlayStation®4-Version von FINAL FANTASY XIV einloggenTechnische Schwierigkeiten des Mana Datenzentrums behoben (26. Jan.)148544489314854449418007f32b6f60752b6efc62332e7eaf608670a8f3Recovery4Wir freuen uns euch mitteilen zu können, dass die technischen Schwierigkeiten mit den untenstehenden Welten behoben wurden. <br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit] <br> +Am 26. Jan. 2017 von 13:46 Uhr bis 16:15 Uhr (MEZ) <br> +<br> +[Details] <br> +・Verbindung zu bestimmten instanziierten Inhalten unterbrochen und Wiedereinloggen war nicht möglich <br> +<br> +[Ursache] <br> +Probleme mit der Serversoftware<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Bezüglich Einschränkungen bei der Charaktererstellung14854428001485445569ee2c8eb0ec2c20d20e2bd6d9e2c69217dcad20d81Updates bezüglich der Einschränkungen der Charaktererstellung finden Sie hier.<br> +Sollte es zu einer erhöhten Belastung kommen, behalten wir es uns vor, die Erstellung für neue Charaktere zeitweilig einzuschränken.<br> +Diese Information wird in den folgenden Abständen aktualisiert:<br> +2:00 / 7:00 / 12:00 / 16:00 / 21:00 (MEZ)<br> +<br> +○:Charaktererstellung möglich ×:Charaktererstellung eingeschränkt<br> +<br> +Japanisches Datenzentrum<br> + ○ Aegis<br> + ○ Alexander<br> + ○ Anima<br> + ○ Asura<br> + ○ Atomos<br> + × Bahamut<br> + ○ Belias<br> + × Carbuncle<br> + × Chocobo<br> + ○ Durandal<br> + × Fenrir<br> + × Garuda<br> + × Gungnir<br> + ○ Hades<br> + ○ Ifrit<br> + ○ Ixion<br> + ○ Kujata<br> + × Mandragora<br> + ○ Masamune<br> + ○ Pandaemonium<br> + ○ Ramuh<br> + ○ Ridill<br> + × Shinryu<br> + × Tiamat<br> + ○ Titan<br> + × Tonberry<br> + ○ Typhon<br> + ○ Ultima<br> + ○ Unicorn<br> + ○ Valefor<br> + ○ Yojimbo<br> + ○ Zeromus<br> +<br> +Nordamerikanisches Datenzentrum<br> + ○ Adamantoise<br> + × Balmung<br> + ○ Behemoth<br> + ○ Brynhildr<br> + ○ Cactuar<br> + ○ Coeurl<br> + ○ Diabolos<br> + ○ Excalibur<br> + ○ Exodus<br> + ○ Faerie<br> + ○ Famfrit<br> + × Gilgamesh<br> + ○ Goblin<br> + ○ Hyperion<br> + ○ Jenova<br> + ○ Lamia<br> + ○ Leviathan<br> + ○ Malboro<br> + ○ Mateus<br> + ○ Midgardsormr<br> + ○ Sargatanas<br> + ○ Siren<br> + ○ Ultros<br> + ○ Zalera<br> +<br> +Europäisches Datenzentrum<br> + × Cerberus<br> + ○ Lich<br> + × Moogle<br> + ○ Odin<br> + × Phoenix<br> + × Ragnarok<br> + × Shiva<br> + ○ ZodiarkNotwartung des Mana-Datenzentrums (26. Jan.)14854377331485437790021fbfd330ff2fac70a32cf8bd15cfa146d2258fMaintenance2Wir werden eine Notwartung aller Welten des Mana-Datenzentrums durchführen. Während dieses Zeitraums werden diese Welten nicht verfügbar sein.<br> +<br> +* 30 Minuten vor Beginn der Wartung bis zu deren Abschluss wird der Weltentransferdienst nicht zur Verfügung stehen.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis. <br> +<br> +[Datum & Uhrzeit]<br> +Am 26. Jan. 2017 von 14:45 Uhr bis 17:15 Uhr (MEZ) <br> +<br> +[Betroffene Welten]<br> +Alle Welten des Mana-Datenzentrums:<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +Technische Schwierigkeiten des Mana Datenzentrums (26. Jan.)148543638814854364392e83b51376fe2d18ce9dc653b146574508c5829d4Gegenwärtig gibt es technische Schwierigkeiten mit den untenstehenden Welten.<br> +<br> +Wir entschuldigen uns für etwaige Unannehmlichkeiten und danken euch für euer Verständnis.<br> +<br> +[Datum & Uhrzeit] <br> +Seit dem 26. Jan. 2017 um 13:46 Uhr (MEZ)<br> +<br> +[Details]<br> +・Einloggen ist nicht möglich.<br> +・Areale können nicht gewechselt werden.<br> +・Betreten von instanziierten Inhalten ist nicht möglich.<br> +<br> +[Ursache] <br> +Wird untersucht<br> +<br> +[Betroffene Welten] <br> +Alle Welten des Mana Datenzentrums<br> +・Anima<br> +・Asura<br> +・Belias<br> +・Chocobo<br> +・Hades<br> +・Ixion<br> +・Mandragora<br> +・Masamune<br> +・Pandaemonium<br> +・Shinryu<br> +・Titan<br> +5834234334434534634734835035135240243959354355356357358359360361362363364365367428603683693703714416137237337437537637737837938038138238338438538643044071434◆ Hauptmenü des GM-Diensts1111◆ Untersagte Aktivitäten in FINAL FANTASY XIV1333◆ Account-Strafen1222◇ Werbung für RMT (Echtgeldhandel) melden1001◇ Einmalige Gegenstandswiederherstellung/-umtausch1011◇ Ich habe keinen Zugriff auf den Heavensward Spielinhalt? Was kann ich tun?70628◇ Ich habe meine Heavensward Bonus-Gegenstände nicht erhalten? Was kann ich tun?70629◇ Darf ich Geschenk-Codes im Spiel kaufen oder verkaufen?1027 \ No newline at end of file diff --git a/bin/web/sapphire_logo.png b/bin/web/sapphire_logo.png new file mode 100644 index 00000000..a7970d0c Binary files /dev/null and b/bin/web/sapphire_logo.png differ diff --git a/bin/zlib1.dll b/bin/zlib1.dll new file mode 100644 index 00000000..ea747b42 Binary files /dev/null and b/bin/zlib1.dll differ diff --git a/sql/accounts.sql b/sql/accounts.sql new file mode 100644 index 00000000..2e36b660 --- /dev/null +++ b/sql/accounts.sql @@ -0,0 +1,54 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `dbaccounts` +-- + +DROP TABLE IF EXISTS `accounts`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `accounts` ( + `account_id` int(11) NOT NULL DEFAULT '0', + `account_name` varchar(255) COLLATE latin1_general_ci NOT NULL, + `account_pass` varchar(255) COLLATE latin1_general_ci NOT NULL, + `account_created` int(11) NOT NULL DEFAULT '0', + `account_status` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`account_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `dbaccounts` +-- + +LOCK TABLES `accounts` WRITE; +/*!40000 ALTER TABLE `accounts` DISABLE KEYS */; +INSERT INTO `accounts` VALUES (1,'test','CY9rzUYh03PK3k6DJie09g==',0,2); +/*!40000 ALTER TABLE `accounts` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:06 diff --git a/sql/battlenpc.sql b/sql/battlenpc.sql new file mode 100644 index 00000000..201636ae --- /dev/null +++ b/sql/battlenpc.sql @@ -0,0 +1,1318 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `battlenpc` +-- + +DROP TABLE IF EXISTS `battlenpc`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `battlenpc` ( + `Id` int(11) NOT NULL, + `ZoneId` int(10) DEFAULT NULL, + `Type` int(11) NOT NULL, + `NameId` int(10) DEFAULT NULL, + `SizeId` int(10) DEFAULT NULL, + `ModelId` int(10) DEFAULT NULL, + `ClassJob` int(3) DEFAULT NULL, + `DisplayFlags1` int(3) DEFAULT NULL, + `DisplayFlags2` int(3) DEFAULT NULL, + `Level` int(3) DEFAULT NULL, + `Pos_0_0` float DEFAULT NULL, + `Pos_0_1` float DEFAULT NULL, + `Pos_0_2` float DEFAULT NULL, + `Rotation` int(10) DEFAULT NULL, + `MobType` int(3) DEFAULT NULL, + `Behaviour` int(3) DEFAULT NULL, + `ModelMainWeapon` int(20) DEFAULT NULL, + `ModelSubWeapon` int(20) DEFAULT NULL, + `Look` binary(28) DEFAULT NULL, + `Models` binary(40) DEFAULT NULL, + PRIMARY KEY (`Id`), + KEY `ZoneId` (`ZoneId`), + KEY `NameId` (`NameId`), + KEY `ModelId` (`ModelId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `battlenpc` +-- + +LOCK TABLES `battlenpc` WRITE; +/*!40000 ALTER TABLE `battlenpc` DISABLE KEYS */; +INSERT INTO `battlenpc` VALUES (1073742721,141,2,1277,795,0,0,0,0,35,-117.39,15.66,316.159,58551,0,4,2147483647,2147483647,'2\\\0\0\0\022\0\0\0','\n\0\0\0+\0\0E\0\0:\0\0k\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756675,141,2,632,385,359,0,0,0,1,-97.08,6.25,245.64,8904,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754503,141,2,244,1445,95,0,0,0,12,-104.7,6.44,238.846,56453,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757592,141,2,632,385,359,0,0,0,1,-66.87,11.1,289.524,5487,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757593,141,2,632,385,359,0,0,0,1,-59,11.65,293.869,17983,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757596,141,2,632,385,359,0,0,0,1,-45.53,11.45,305.341,16338,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757597,141,2,262,351,26,0,0,0,1,-36.81,9.13,305.088,15679,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757599,141,2,262,351,26,0,0,0,1,-31.16,7.7,305.49,60879,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757601,141,2,262,351,26,0,0,0,1,-20.32,8.24,319.073,43175,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757602,141,2,262,351,26,0,0,0,1,-29.17,2.99,275.213,58811,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757606,141,2,262,351,26,0,0,0,1,-59.41,8.09,279.897,55975,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757108,141,2,262,351,26,0,0,0,1,-126.31,12.29,280.813,14809,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757750,141,2,262,351,26,0,0,0,1,-81.1,8.31,271.115,41443,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756089,141,2,262,351,26,0,0,0,1,-11.19,0.71,240.091,55050,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757755,141,2,262,351,26,0,0,0,1,-75.03,9.35,274.324,60541,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757757,141,2,262,351,26,0,0,0,1,-78.33,9.58,275.998,59812,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757760,141,2,632,385,359,0,0,0,1,-63.2,7.56,272.461,35940,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757122,141,2,262,351,26,0,0,0,1,-121.03,9.47,268.882,26857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757765,141,2,632,385,359,0,0,0,1,-43.65,2.4,236.811,2589,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757126,141,2,632,385,359,0,0,0,1,-122.93,8.65,264.534,7806,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757135,141,2,262,351,26,0,0,0,1,-124.97,6.61,252.19,47636,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756752,141,2,632,385,359,0,0,0,1,-150.18,7.81,251.131,38777,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757138,141,2,632,385,359,0,0,0,1,-131.43,6.01,247.167,58722,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756498,141,2,262,351,26,0,0,0,1,-51.86,3.58,247.722,54220,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757147,141,2,262,351,26,0,0,0,1,-136.62,11.43,271.618,17901,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757153,141,2,262,351,26,0,0,0,1,-136.25,10.26,266.278,58291,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757422,141,2,632,385,359,0,0,0,1,-114.76,3.91,223.975,4127,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756402,141,2,632,385,359,0,0,0,1,-16.08,5.63,309.024,54560,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756404,141,2,262,351,26,0,0,0,1,-9.24,1.03,248.622,45505,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756794,141,2,632,385,359,0,0,0,1,-157.58,15.29,280.191,34955,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756582,141,2,262,351,26,0,0,0,1,-52.68,-0.04,223.765,40541,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748882,141,2,262,351,26,0,0,0,2,-2.58,12.73,357.169,38257,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755969,141,2,262,351,26,0,0,0,2,-0.01,11.85,351.703,39347,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755973,141,2,262,351,26,0,0,0,2,-0.4,11.2,348.36,12183,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752273,141,2,632,385,359,0,0,0,2,-0.84,13.15,361.077,61032,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755318,141,2,632,385,359,0,0,0,1,-86.44,1.14,214.918,53163,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757260,141,2,632,385,359,0,0,0,1,-26.29,-3.22,221.868,20779,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756665,141,2,632,385,359,0,0,0,1,-48.63,-0.69,223.42,34223,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756668,141,2,632,385,359,0,0,0,1,-51.58,-0.52,221.885,55498,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073749723,141,2,262,351,26,0,0,0,1,-126.72,3.32,212.637,8930,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757418,141,2,262,351,26,0,0,0,1,-105.84,1.08,206.894,62110,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757420,141,2,632,385,359,0,0,0,1,-105.96,1.6,209.566,39586,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756577,141,2,262,351,26,0,0,0,1,-49.49,-3.28,206.022,27425,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757415,141,2,632,385,359,0,0,0,1,-94.47,-1.25,198.204,33034,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757443,141,2,632,385,359,0,0,0,1,5.18,4.78,291.791,65148,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752172,141,2,262,351,26,0,0,0,1,7.38,0.91,257.257,32884,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757380,141,2,632,385,359,0,0,0,3,15.8,-1.83,219.205,30826,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757258,141,2,632,385,359,0,0,0,1,-57.48,-3.58,196.775,12521,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756618,141,2,244,1445,95,0,0,0,12,12.41,4.99,279.438,1389,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757410,141,2,262,351,26,0,0,0,1,-95.99,-2.55,189.574,36475,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745251,141,2,262,351,26,0,0,0,2,17.16,14.65,392.162,59039,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745254,141,2,262,351,26,0,0,0,2,14.68,14.67,388.122,63117,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757417,141,2,262,351,26,0,0,0,1,-89.53,-2.07,194.737,5058,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757627,141,2,318,205,282,0,0,0,3,18.87,-2.03,215.625,42907,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757778,141,2,632,385,359,0,0,0,3,22.67,2.74,264.042,16991,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757802,141,2,318,205,282,0,0,0,3,25.63,2.24,261.824,51652,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745059,141,2,632,385,359,0,0,0,2,25.83,13.05,387.303,4840,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756263,141,2,262,351,26,0,0,0,2,31.85,10.06,354.938,10076,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757203,141,2,632,385,359,0,0,0,3,36.98,5.98,324.476,30891,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757819,141,2,318,205,282,0,0,0,3,35.87,1.85,262.69,47077,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757624,141,2,318,205,282,0,0,0,3,36.65,0.57,206.858,47515,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757385,141,2,318,205,282,0,0,0,3,36.57,-0.02,194.84,22691,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756361,141,2,632,385,359,0,0,0,3,41.61,1.17,218.462,22351,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757413,141,2,262,351,26,0,0,0,1,-100.07,-2.31,181.605,33797,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757431,141,2,318,205,282,0,0,0,3,41.56,5.16,299.072,41212,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757823,141,2,318,205,282,0,0,0,3,33.01,0,236.508,32767,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756713,141,2,318,205,282,0,0,0,3,45.23,1.07,190.859,40498,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757437,141,2,318,205,282,0,0,0,3,59.99,3.77,301.333,49908,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757414,141,2,632,385,359,0,0,0,1,-92.2,-3.69,180.367,29784,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757440,141,2,318,205,282,0,0,0,3,66.02,2.74,291.044,62279,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757442,141,2,318,205,282,0,0,0,3,64.66,2.1,280.423,50907,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756060,141,2,632,385,359,0,0,0,3,75.04,11.06,193.114,48023,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757619,141,2,632,385,359,0,0,0,3,76.55,6.48,406.315,49010,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745060,141,2,632,385,359,0,0,0,2,35.59,13.9,420.153,59749,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756256,141,2,318,205,282,0,0,0,3,99.56,4.34,414.639,44591,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757053,141,2,632,385,359,0,0,0,3,100.53,1.13,323.586,34664,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756285,141,2,318,205,282,0,0,0,3,96.04,2.63,386.164,11182,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756578,141,2,632,385,359,0,0,0,3,94.62,2.33,262.737,54745,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752687,141,2,318,205,282,0,0,0,3,103.7,8.12,217.462,63598,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756292,141,2,318,205,282,0,0,0,3,104.06,3.53,401.065,49554,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756298,141,2,632,385,359,0,0,0,3,103.46,4.02,409.352,14643,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757047,141,2,632,385,359,0,0,0,3,117.52,7.92,249.594,25236,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756288,141,2,632,385,359,0,0,0,3,114.13,3.57,360.24,46546,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756079,141,2,318,205,282,0,0,0,3,118.89,4.86,411.106,5969,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752277,141,2,318,205,282,0,0,0,3,112.66,1.43,311.47,16711,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745062,141,2,632,385,359,0,0,0,1,58.77,12.53,443.738,26614,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756070,141,2,318,205,282,0,0,0,3,103.12,4.23,442.457,53482,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756074,141,2,318,205,282,0,0,0,3,105.72,4.52,439.41,9516,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745071,141,2,244,1445,95,0,0,0,12,51.34,13.15,441.574,34013,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756678,141,2,318,205,282,0,0,0,3,126.58,13.86,276.936,16184,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757050,141,2,632,385,359,0,0,0,3,120.31,8.91,249.872,884,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756305,141,2,632,385,359,0,0,0,3,128.6,7.1,430.953,60620,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756082,141,2,318,205,282,0,0,0,3,121.48,5.12,398.129,63279,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756085,141,2,318,205,282,0,0,0,3,120.85,4.87,402.59,42531,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752279,141,2,318,205,282,0,0,0,3,126.06,2.54,318.801,7297,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073749728,141,2,632,385,359,0,0,0,3,123.58,2.35,317.683,59686,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745072,141,2,244,1445,95,0,0,0,12,59.72,12.86,450.302,24237,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756683,141,2,632,385,359,0,0,0,3,132.3,16.2,272.54,62614,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756686,141,2,318,205,282,0,0,0,3,135.75,16.48,273.366,32903,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745092,141,2,632,385,359,0,0,0,3,133.8,7.37,395.447,18571,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752280,141,2,318,205,282,0,0,0,3,130.26,3.23,325.502,52286,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745662,141,2,318,205,282,0,0,0,4,128.09,8.79,456.686,13254,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756311,141,2,318,205,282,0,0,0,4,138.98,10.05,462.196,34020,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742256,141,2,632,385,359,0,0,0,3,139.19,15.37,285.763,52926,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748894,141,2,632,385,359,0,0,0,4,127.52,8.98,467.801,58543,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756692,141,2,318,205,282,0,0,0,3,146.22,15.38,300.675,63482,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756317,141,2,632,385,359,0,0,0,4,149.3,10.72,462.144,14568,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745103,141,2,632,385,359,0,0,0,3,154.63,9.79,434.4,34725,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742084,141,2,318,205,282,0,0,0,4,156.78,14.9,388.195,11008,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751909,141,2,318,205,282,0,0,0,4,119.07,7.23,489.395,28097,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751910,141,2,318,205,282,0,0,0,4,145.02,11.45,488.705,48449,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754794,141,2,632,385,359,0,0,0,2,79.61,6.57,488.845,14015,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745088,141,2,318,205,282,0,0,0,4,155.67,10.62,408.348,10665,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745107,141,2,244,1445,95,0,0,0,12,159.19,10.65,447.902,16381,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745202,141,2,632,385,359,0,0,0,4,134.05,10.9,495.272,24198,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747327,141,2,318,205,282,0,0,0,4,163.57,12.38,481.198,23147,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747332,141,2,318,205,282,0,0,0,4,169.29,13.13,485.522,49151,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756062,141,2,262,351,26,0,0,0,2,59.64,11.93,493.815,35933,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754791,141,2,632,385,359,0,0,0,2,91.49,4.1,492.133,39753,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757111,141,2,318,205,282,0,0,0,4,167.96,12.33,479.451,53294,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745087,141,2,318,205,282,0,0,0,4,164.29,11.05,411.859,36794,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745091,141,2,632,385,359,0,0,0,3,168.3,12.31,406.265,40304,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745090,141,2,318,205,282,0,0,0,4,179.31,12.06,411.779,28029,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756057,141,2,262,351,26,0,0,0,2,53.2,13.1,506.71,35016,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745382,141,2,632,385,359,0,0,0,4,156.87,12.53,505.148,7184,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747335,141,2,318,205,282,0,0,0,4,184.76,13.66,486.074,29980,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757162,141,2,632,385,359,0,0,0,4,181.25,13.26,483.687,12448,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756055,141,2,262,351,26,0,0,0,2,50.46,13.58,507.356,36512,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754786,141,2,632,385,359,0,0,0,2,55.21,12.83,513.437,30956,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756325,141,2,318,205,282,0,0,0,4,163.63,12.37,514.19,11419,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745078,141,2,318,205,282,0,0,0,4,127.73,11.46,508.397,25260,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745079,141,2,318,205,282,0,0,0,4,131.98,11.63,506.858,61422,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756251,141,2,262,351,26,0,0,0,2,69.12,10.79,519.937,37474,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745068,141,2,632,385,359,0,0,0,4,140.58,11.91,516.586,17494,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754789,141,2,632,385,359,0,0,0,2,44.63,14.68,516.015,9119,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745140,141,2,318,205,282,0,0,0,4,175.62,11.81,520.386,2859,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745223,141,2,632,385,359,0,0,0,4,206.05,16.12,507.605,34354,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742186,141,2,632,385,359,0,0,0,4,200.2,13.03,449.745,57947,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742194,141,2,318,205,282,0,0,0,4,199.33,13.2,454.625,25299,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745225,141,2,632,385,359,0,0,0,4,205.24,17.61,526.61,40217,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757285,141,2,262,351,26,0,0,0,2,106.65,5.56,526.434,25207,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757297,141,2,632,385,359,0,0,0,2,113.12,6.57,524.823,42705,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753579,141,2,318,205,282,0,0,0,4,184.3,12.49,526.289,31486,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756466,141,2,318,205,282,0,0,0,4,155.6,9.8,537.942,22971,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753577,141,2,632,385,359,0,0,0,4,162.7,9.91,535.648,48390,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745224,141,2,632,385,359,0,0,0,4,210.09,17.29,505.249,34071,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752620,141,2,632,385,359,0,0,0,4,196.41,15.8,547.513,18242,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757305,141,2,318,205,282,0,0,0,4,148.09,7.6,548.567,36051,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756388,141,2,632,385,359,0,0,0,4,198.41,16.53,544.044,5939,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757267,141,2,262,351,26,0,0,0,2,84.34,8.55,552.341,13277,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757307,141,2,318,205,282,0,0,0,4,156.04,8.51,557.488,60906,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757315,141,2,318,205,282,0,0,0,4,172.77,9.96,556.493,65419,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757270,141,2,262,351,26,0,0,0,2,87.04,8.36,561.37,38613,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757310,141,2,318,205,282,0,0,0,4,158.16,9.14,564.707,26745,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742125,141,2,318,205,282,0,0,0,4,221.77,14.79,468.725,33580,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752631,141,2,632,385,359,0,0,0,4,219.51,16.9,558.472,65461,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756748,141,2,318,205,282,0,0,0,4,139.76,6.74,567.197,55292,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742720,141,2,1277,797,0,0,0,0,35,32.36,15.91,575.715,48431,0,4,2147483647,2147483647,'2\\\0\0\\\0\0K\0K\0\0\0','\n\0\0\0+\0\0E\0\0:\0\0k\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751753,141,2,318,205,282,0,0,0,4,219.59,20,422.518,61918,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751757,141,2,318,205,282,0,0,0,4,219.09,20.14,419.038,41792,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756444,141,2,632,385,359,0,0,0,4,144.24,7.86,579.901,65458,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756717,141,2,318,205,282,0,0,0,4,148.91,8.36,583.584,56655,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752627,141,2,318,205,282,0,0,0,4,216.2,13.19,581.109,12761,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742124,141,2,318,205,282,0,0,0,4,231.83,15.37,458.995,53738,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754674,141,2,632,385,359,0,0,0,4,166.07,10.19,586.433,32949,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757261,141,2,632,385,359,0,0,0,4,127.85,4.92,591.922,57945,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756754,141,2,318,205,282,0,0,0,4,140.99,7.32,586.131,41621,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754661,141,2,318,205,282,0,0,0,4,150.8,7.61,598.442,61197,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756472,141,2,318,205,282,0,0,0,4,197.98,12.41,599.58,20861,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742169,141,2,632,385,359,0,0,0,4,202.26,12.5,598.826,28671,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751730,141,2,318,205,282,0,0,0,4,217.46,11.45,598.861,2085,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748291,141,2,318,205,282,0,0,0,4,170.96,11.38,603.21,43046,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753732,141,2,244,1445,95,0,0,0,12,190.43,12.17,602.385,59555,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742236,141,2,632,385,359,0,0,0,4,217.54,10.15,609.344,17007,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751732,141,2,318,205,282,0,0,0,4,219.42,10.92,602.201,7602,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756694,141,2,632,385,359,0,0,0,2,56.29,13.93,614.195,49653,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756707,141,2,632,385,359,0,0,0,2,103.6,8.08,611.61,12296,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756708,141,2,632,385,359,0,0,0,2,112.28,6.87,612.693,35011,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742136,141,2,318,205,282,0,0,0,4,182.84,10.99,617.924,10568,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748292,141,2,318,205,282,0,0,0,4,185.29,11.38,614.536,53429,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742168,141,2,632,385,359,0,0,0,4,198.44,10.03,618.558,14802,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756682,141,2,262,351,26,0,0,0,2,41.72,15.67,621.752,23263,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756685,141,2,262,351,26,0,0,0,2,32.26,16,623.89,2296,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756689,141,2,262,351,26,0,0,0,2,31.15,16.01,625.723,26987,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756697,141,2,262,351,26,0,0,0,2,81.34,12.93,626.56,52849,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756703,141,2,262,351,26,0,0,0,2,91.22,10.48,619.816,55313,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756701,141,2,262,351,26,0,0,0,2,79.14,12.66,635.344,29707,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742231,141,2,632,385,359,0,0,0,4,228.25,6.83,632.597,1021,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754082,141,2,632,385,359,0,0,0,2,141.36,4.36,632.695,23818,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742050,141,2,262,351,26,0,0,0,2,140.66,5.45,650.085,16854,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073749215,141,2,262,351,26,0,0,0,2,147.96,4.49,657.346,30066,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742049,141,2,262,351,26,0,0,0,2,148.02,4.96,653.649,53823,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745230,141,2,632,385,359,0,0,0,4,236.41,17.17,559.658,5541,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742187,141,2,632,385,359,0,0,0,4,232.66,15.21,469.033,989,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742232,141,2,632,385,359,0,0,0,4,233.46,6.93,634.177,37067,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752527,141,2,632,385,359,0,0,0,2,71.74,14.97,666.195,21569,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752528,141,2,262,351,26,0,0,0,2,69.45,15.35,664.512,11795,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742053,141,2,262,351,26,0,0,0,2,105.33,9.83,667.185,36437,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742054,141,2,262,351,26,0,0,0,2,109.83,9.68,666.262,885,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742052,141,2,262,351,26,0,0,0,2,115.37,9.51,664.321,49985,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748289,141,2,632,385,359,0,0,0,2,108.36,9.44,676.887,49458,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742212,141,2,632,385,359,0,0,0,2,125.13,9.05,674.481,38887,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742195,141,2,318,205,282,0,0,0,4,239.8,15.02,477.232,27113,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745218,141,2,632,385,359,0,0,0,4,246.73,16.7,498.176,55299,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745232,141,2,632,385,359,0,0,0,4,243.9,17.75,564.658,45446,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742230,141,2,632,385,359,0,0,0,4,246.88,10.97,607.174,20820,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745236,141,2,632,385,359,0,0,0,4,252.86,12.57,582.213,42282,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745237,141,2,632,385,359,0,0,0,4,257.34,13.04,575.033,26928,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745235,141,2,318,205,282,0,0,0,4,253.21,12.38,586.114,31045,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745674,141,2,632,385,359,0,0,0,4,260.87,17.14,523.678,31108,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745233,141,2,632,385,359,0,0,0,4,258.57,15.2,556.474,34605,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745240,141,2,632,385,359,0,0,0,4,261.97,16.48,516.862,5853,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756067,141,2,318,205,282,0,0,0,3,84.72,14.66,174.508,20673,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757700,141,2,308,302,270,0,0,0,5,22.98,-6.4,165.647,47963,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757698,141,2,308,302,270,0,0,0,5,-0.88,-5.93,168.46,26302,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756071,141,2,632,385,359,0,0,0,3,81.75,15.07,170.3,32070,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756078,141,2,318,205,282,0,0,0,3,64.87,12.64,170.87,13778,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757697,141,2,308,302,270,0,0,0,5,11.2,-6.52,161.63,51566,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757702,141,2,308,302,270,0,0,0,5,-0.54,-6.41,163.077,35461,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756081,141,2,318,205,282,0,0,0,3,81.19,17.32,159.068,31831,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756086,141,2,318,205,282,0,0,0,3,78.34,17.33,155.373,28463,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757237,141,2,308,302,270,0,0,0,5,25.95,-6.51,147.895,50799,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756083,141,2,632,385,359,0,0,0,3,77.97,17.49,153.9,20394,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757675,141,2,308,302,270,0,0,0,5,-24.65,-6.29,150.016,64539,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757668,141,2,308,302,270,0,0,0,5,-42.51,-6.13,155.72,3818,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757672,141,2,308,302,270,0,0,0,5,-36.98,-6.53,158.786,24386,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757699,141,2,308,302,270,0,0,0,5,-1.58,-6.39,144.805,45733,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752701,141,2,632,385,359,0,0,0,3,54.78,13.06,142.447,16600,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757591,141,2,308,302,270,0,0,0,5,-8.4,-6.24,142.111,28577,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757684,141,2,308,302,270,0,0,0,5,-50.4,-6.05,168.889,56523,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757250,141,2,201,41,59,0,0,0,5,-57.25,-2,121.474,21667,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757039,141,2,308,302,270,0,0,0,5,-10.16,-6.69,122.857,45572,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757355,141,2,308,302,270,0,0,0,5,-74.33,-6.61,153.856,52698,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757020,141,2,308,302,270,0,0,0,5,8.49,-6.41,107.429,5576,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757037,141,2,308,302,270,0,0,0,5,10.68,-6.55,105.527,51899,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757409,141,2,308,302,270,0,0,0,5,-84.93,-6.22,152.254,64777,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757411,141,2,308,302,270,0,0,0,5,-82.68,-5.84,163.441,39900,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756121,141,2,294,432,192,0,0,0,5,-60.59,0.48,88.466,56345,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755905,141,2,294,432,192,0,0,0,5,-89.39,0.25,109.621,47164,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757253,141,2,201,41,59,0,0,0,5,-91,1.39,101.754,20232,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757406,141,2,308,302,270,0,0,0,5,-91.13,-5.24,142.189,5196,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757403,141,2,308,302,270,0,0,0,5,-97.98,-5.72,136.051,33745,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754085,141,2,294,432,192,0,0,0,5,-36.95,-3.17,36.5403,23361,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755914,141,2,294,432,192,0,0,0,5,-43.16,-4.69,29.0447,25798,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755471,141,2,201,41,59,0,0,0,5,-31.27,-5.45,5.11066,1391,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757347,141,2,201,41,59,0,0,0,5,-74.22,-4.97,25.3587,5686,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757555,141,2,326,352,0,0,0,0,11,124,28.77,-17.3504,60830,0,2,2147483647,2147483647,'\02VO\0\0\0O\0\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757353,141,2,330,353,0,5,0,0,11,125.38,28.71,-24.9264,55966,0,2,2147483647,2147483647,'\02=z\0\0z\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753656,141,2,289,160,152,0,0,0,10,78.44,-3.12,-31.1736,28988,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753657,141,2,289,160,152,0,0,0,10,69.7,-3.16,-30.7987,64900,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757279,141,2,294,432,192,0,0,0,5,-10.27,-1.4,-33.7913,412,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757563,141,2,330,353,0,5,0,0,11,105.37,29.79,-32.596,34080,0,2,2147483647,2147483647,'\02=z\0\0z\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757299,141,2,294,432,192,0,0,0,5,17.24,3.83,-38.4846,37805,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757576,141,2,326,352,0,0,0,0,11,122.35,28.2,-42.9355,3174,0,2,2147483647,2147483647,'\02VO\0\0\0O\0\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756126,141,2,294,432,192,0,0,0,5,-82.89,0.54,68.9512,3053,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753655,141,2,289,160,152,0,0,0,10,68.48,-2.63,-46.2824,29867,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752924,141,2,201,41,59,0,0,0,5,11.38,0.91,-45.5629,62634,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757557,141,2,330,353,0,5,0,0,11,102.74,30.75,-44.3244,30237,0,2,2147483647,2147483647,'\02=z\0\0z\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757793,141,2,294,432,192,0,0,0,5,-21.65,-4.57,-51.742,15227,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756145,141,2,294,432,192,0,0,0,5,-87.15,1.28,76.7592,32307,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742666,141,2,292,197,193,0,0,0,11,63.68,31.26,-68.2452,20526,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755513,141,2,201,41,59,0,0,0,5,-27.64,-6.38,-61.5612,22841,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742665,141,2,292,197,193,0,0,0,11,64.88,30.8,-75.4304,24370,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754492,141,2,244,1445,95,0,0,0,17,-6.76,-2.79,-72.3025,47590,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742722,141,2,279,38,98,0,0,0,11,54.4,30.42,-72.8485,15050,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748655,141,2,279,38,98,0,0,0,11,115.59,23.15,-69.2764,717,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742664,141,2,292,197,193,0,0,0,11,39.21,27.59,-84.7252,9372,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742667,141,2,292,197,193,0,0,0,11,95.99,28.74,-84.0351,41837,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757803,141,2,294,432,192,0,0,0,5,-17.55,-4.83,-81.7287,29995,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742724,141,2,279,38,98,0,0,0,11,91.48,29.67,-81.3482,46858,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757816,141,2,294,432,192,0,0,0,5,-12.39,-3.95,-89.9305,23901,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757833,141,2,294,432,192,0,0,0,5,-1.22,-1.88,-90.39,57443,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755864,141,2,288,175,141,0,0,0,11,111.61,22.34,-91.3968,31743,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757210,141,2,282,431,156,0,0,0,7,-104.57,-2.63,9.33855,22856,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756939,141,2,289,160,152,0,0,0,10,64.91,-1.18,-102.773,3640,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742723,141,2,279,38,98,0,0,0,11,87.87,29.02,-102.97,39797,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757405,141,2,282,431,156,0,0,0,6,-66.65,-3.63,-102.696,60395,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756935,141,2,289,160,152,0,0,0,10,75.36,-1.69,-104.143,46502,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742663,141,2,292,197,193,0,0,0,11,65.99,29.22,-109.861,22211,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755738,141,2,294,432,192,0,0,0,5,37.23,16.86,-108.374,41047,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757316,141,2,201,41,59,0,0,0,6,-65.16,-1.85,-109.749,35772,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757842,141,2,282,431,156,0,0,0,6,-91.92,0.76,-105.063,58889,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757402,141,2,282,431,156,0,0,0,6,-53.24,-4.85,-104.993,48464,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757650,141,2,294,432,192,0,0,0,5,6.25,-0.15,-113.241,14058,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755423,141,2,201,41,59,0,0,0,5,8.04,5.04,-128.702,22304,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755435,141,2,201,41,59,0,0,0,5,51.4,16.71,-125.2,5989,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757688,141,2,201,41,59,0,0,0,6,-88.27,2.63,-128.054,45865,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757690,141,2,244,1445,95,0,0,0,17,-96.54,2.54,-124.224,53450,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742719,141,2,1276,794,0,0,0,0,35,-26.17,-2.31,-130.147,23672,0,4,2147483647,2147483647,'\02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',' \0+\0 \0\0\0F\0 \0\0\0M\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755426,141,2,294,432,192,0,0,0,5,32,7.9,-143.66,20471,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755429,141,2,294,432,192,0,0,0,5,27.66,8.61,-139.12,21042,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757042,141,2,289,160,152,0,0,0,10,86.92,-1.16,-144.784,53135,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756848,141,2,201,41,59,0,0,0,5,68.99,-0.34,-146.495,29961,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756977,141,2,201,41,59,0,0,0,6,-122.44,11.19,-63.8184,29264,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756980,141,2,201,41,59,0,0,0,6,-124.41,12.49,-57.7543,26419,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757840,141,2,282,431,156,0,0,0,6,-124.84,9.83,-74.5866,27485,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742296,141,2,201,41,59,0,0,0,7,-122.77,0.15,15.4989,62823,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742718,141,2,1276,794,0,0,0,0,35,7.6,-2.07,-178.52,51966,0,4,2147483647,2147483647,'\02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',' \0+\0 \0\0\0F\0 \0\0\0M\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748421,141,2,201,41,59,0,0,0,6,-111.87,-0.23,-177.134,63251,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757685,141,2,294,432,192,0,0,0,5,49.08,0.77,-177.202,63460,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754158,141,2,201,41,59,0,0,0,6,-135.72,3.85,-131.361,42626,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757880,141,2,780,952,1,0,0,0,11,-136.6,6.4,-26.3571,6351,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755446,141,2,201,41,59,0,0,0,5,54.48,1.52,-190.372,35142,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757817,141,2,282,431,156,0,0,0,6,-128.94,2.68,-191.176,39235,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742429,141,2,201,41,59,0,0,0,5,-92.02,-0.18,-199.491,29721,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753562,141,2,201,41,59,0,0,0,5,-86.1,0.15,-202.001,62938,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755445,141,2,201,41,59,0,0,0,5,74.81,3.7,-205.924,38634,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757764,141,2,201,41,59,0,0,0,5,0.02,-1.31,-207.359,57772,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742440,141,2,201,41,59,0,0,0,5,29.56,6.14,-211.284,20844,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742501,141,2,294,432,192,0,0,0,5,-68.97,7.53,-207.888,18176,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755859,141,2,201,41,59,0,0,0,5,79.63,3.72,-206.359,55187,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755454,141,2,294,432,192,0,0,0,5,90.39,1.84,-189.997,62675,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757768,141,2,294,432,192,0,0,0,5,3.51,-0.04,-223.356,4514,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753518,141,2,294,432,192,0,0,0,5,92.76,3.13,-218.195,54166,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755451,141,2,294,432,192,0,0,0,5,94.73,1.3,-187.06,60315,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755466,141,2,201,41,59,0,0,0,5,109.42,1.06,-208.736,58812,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757659,141,2,292,197,193,0,0,0,10,110.74,4.37,-128.386,37182,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747202,141,2,294,432,192,0,0,0,5,-48.1,11.08,-227.004,60667,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757515,141,2,292,197,193,0,0,0,10,116.97,3.82,-132.882,17914,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757779,141,2,201,41,59,0,0,0,5,-59.28,9.68,-224.098,21557,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746859,141,2,254,736,3,0,0,0,6,32.1,19.98,-229.51,61029,0,2,2147483647,2147483647,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755865,141,2,288,175,141,0,0,0,11,113.19,21.37,-98.5066,20403,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755868,141,2,279,38,98,0,0,0,10,118.38,6.76,-122.188,30746,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755639,141,2,294,432,192,0,0,0,5,116.19,0.13,-205.623,48401,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746710,141,2,201,41,59,0,0,0,5,113.3,0.91,-235.743,48086,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757771,141,2,201,41,59,0,0,0,5,12.55,5.77,-236.426,23121,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755473,141,2,294,432,192,0,0,0,5,124.85,0.59,-229.52,31495,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742726,141,2,279,38,98,0,0,0,11,127.25,20.31,-106.926,62790,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746858,141,2,246,735,3,0,0,0,6,32.89,19.91,-233.899,29912,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073743019,141,2,294,432,192,0,0,0,5,-78.32,8.77,-234.84,5988,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747915,141,2,294,432,192,0,0,0,5,-22.95,10.04,-237.784,30991,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755866,141,2,288,175,141,0,0,0,11,121.25,21.26,-93.4687,40292,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756944,141,2,289,160,152,0,0,0,10,132.42,-1.41,-194.741,63905,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755474,141,2,294,432,192,0,0,0,5,129.94,1.24,-231.051,24863,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756946,141,2,289,160,152,0,0,0,10,144.01,-1.16,-211.372,24032,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750358,141,2,288,175,141,0,0,0,11,143.55,19.52,-109.682,5889,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750364,141,2,288,175,141,0,0,0,11,144.84,19.66,-105.156,40952,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750365,141,2,288,175,141,0,0,0,11,142.73,19.8,-83.4594,21983,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757309,141,2,279,38,98,0,0,0,10,140.52,-1.11,-188.579,29999,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742727,141,2,279,38,98,0,0,0,11,138.49,20.2,-80.0335,39344,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755809,141,2,292,197,193,0,0,0,10,142.06,3.19,-134.318,3672,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754529,141,2,288,175,141,0,0,0,11,141.92,16.23,-41.6363,21246,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754532,141,2,279,38,98,0,0,0,11,137.99,19.37,-60.8633,57694,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754539,141,2,288,175,141,0,0,0,11,144.6,15.61,-40.7551,35263,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746662,141,2,201,41,59,0,0,0,5,144.39,0.59,-234.452,51971,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756652,141,2,279,38,98,0,0,0,10,144.41,1.14,-165.002,13528,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756953,141,2,289,160,152,0,0,0,10,149.39,-0.81,-214.04,65244,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757527,141,2,292,197,193,0,0,0,10,147.68,3.72,-127.87,31626,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754547,141,2,288,175,141,0,0,0,11,153.65,18.72,-78.4185,47520,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754550,141,2,288,175,141,0,0,0,11,153.63,18.56,-76.0489,21415,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754553,141,2,288,175,141,0,0,0,11,148.77,19.04,-74.4356,19748,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757863,141,2,1348,1299,0,0,4,0,10,80.8,0.49,-245.758,32767,0,1,0,0,'2F\0\0\0\02\02\0\0\0\0','\0\0\0\0!\0\0\0\0\0\08\0\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747917,141,2,294,432,192,0,0,0,5,-12.78,4.59,-246.139,46136,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753586,141,2,246,735,3,0,0,0,6,43.68,20.39,-248.615,24932,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754988,141,2,288,175,141,0,0,0,11,155.27,17.03,-58.0947,56606,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757311,141,2,279,38,98,0,0,0,10,160.65,-1.69,-240.294,11920,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754528,141,2,279,38,98,0,0,0,11,163.16,12.63,-49.7546,13658,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756580,141,2,292,197,193,0,0,0,10,159.15,1.84,-176.612,17442,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754592,141,2,279,38,98,0,0,0,10,161.98,2.49,-119.188,46089,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755674,141,2,279,38,98,0,0,0,10,168.62,2.81,-168.761,20902,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756961,141,2,289,160,152,0,0,0,10,165.65,-1.69,-240.506,21430,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755796,141,2,279,38,98,0,0,0,11,169.69,2.72,-87.7072,7143,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755810,141,2,292,197,193,0,0,0,10,170.43,0,-115.867,52332,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755815,141,2,292,197,193,0,0,0,11,170.9,2.64,-83.4264,44128,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757743,141,2,288,175,141,0,0,0,11,171.95,7.43,-39.477,59871,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757745,141,2,288,175,141,0,0,0,11,172.57,7.34,-40.5044,37233,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756642,141,2,279,38,98,0,0,0,10,174.74,5.61,-200.303,23719,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756657,141,2,292,197,193,0,0,0,10,175.39,4.96,-190.334,59516,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754527,141,2,279,38,98,0,0,0,11,175.12,6.01,-28.3529,54815,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757556,141,2,326,352,0,0,0,0,11,141.4,28.53,-23.3314,42425,0,2,2147483647,2147483647,'\02VO\0\0\0O\0\022\0\0\0','\0*\0\0\0\08\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755689,141,2,292,197,193,0,0,0,10,191.1,2.22,-154.026,43403,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755697,141,2,292,197,193,0,0,0,10,192.35,3.37,-162.6,478,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753670,141,2,279,38,98,0,0,0,11,191.66,0.57,-39.152,2189,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757775,141,2,294,432,192,0,0,0,5,13.15,10.96,-252.524,62564,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753679,141,2,292,197,193,0,0,0,11,202.95,-1.94,-107.584,53404,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755374,141,2,279,38,98,0,0,0,10,202.1,7.05,-180.151,12662,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754616,141,2,292,197,193,0,0,0,11,209.56,-1.66,-80.8762,34159,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748574,141,2,246,735,3,0,0,0,6,27.08,19.83,-258.168,63004,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757314,141,2,279,38,98,0,0,0,10,186.48,-1.69,-260.417,32498,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755623,141,2,254,736,3,0,0,0,6,30.22,19.81,-261.464,21596,0,2,2147483647,2147483647,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755624,141,2,254,736,3,0,0,0,6,25.64,19.83,-262.593,42097,0,2,2147483647,2147483647,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754597,141,2,279,38,98,0,0,0,11,218.31,-1.74,-96.2006,44200,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073752770,141,2,292,197,193,0,0,0,11,230.06,-2.85,-58.1529,52206,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754611,141,2,292,197,193,0,0,0,11,227.33,-1.16,-114.267,15758,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742659,141,2,279,38,98,0,0,0,10,234.7,1.17,-164.976,49622,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742697,141,2,279,38,98,0,0,0,12,239.05,-17.96,-85.6509,28717,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742585,141,2,292,197,193,0,0,0,10,239.48,7,-189.005,9851,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754615,141,2,292,197,193,0,0,0,11,233.28,-0.51,-109.602,58326,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746379,141,2,308,302,270,0,0,0,12,254.19,-18.37,-98.3717,37652,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742660,141,2,279,38,98,0,0,0,10,259.71,9.14,-196.493,4031,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754508,141,2,308,302,270,0,0,0,12,261.62,-19.17,-123.243,50980,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073748936,141,2,279,38,98,0,0,0,11,220.07,-2.95,-48.317,60391,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754600,141,2,308,302,270,0,0,0,12,263.92,-18.69,-104.301,50479,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756907,141,2,216,26,126,0,0,0,14,260.15,-18.44,-83.0222,12444,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742583,141,2,292,197,193,0,0,0,10,260.83,13.56,-206.385,12319,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755651,141,2,279,38,98,0,0,0,14,270.39,-17.64,-174.279,63472,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757505,141,2,216,26,126,0,0,0,14,269.49,-19.03,-117.56,13047,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742694,141,2,279,38,98,0,0,0,14,269.84,-18.18,-165.438,45148,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742584,141,2,292,197,193,0,0,0,10,267.42,7.55,-194.791,26435,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755904,141,2,292,197,193,0,0,0,11,278.57,-13.77,-34.8601,33953,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073744876,141,2,308,302,270,0,0,0,14,282.82,-19.09,-147.82,58091,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746749,141,2,308,302,270,0,0,0,12,288.1,-19.29,-95.7813,18494,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073745471,141,2,308,302,270,0,0,0,12,288.19,-19.21,-124.966,33092,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073749339,141,2,279,38,98,0,0,0,12,285.46,-19.23,-125.517,41506,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757665,141,2,308,302,270,0,0,0,12,285.87,-19.21,-84.84,20736,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757510,141,2,216,26,126,0,0,0,14,294.72,-18.41,-134.38,33877,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742698,141,2,279,38,98,0,0,0,12,292.92,-19.24,-102.416,50523,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757663,141,2,279,38,98,0,0,0,12,300.02,-19.29,-69.1167,58608,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757667,141,2,308,302,270,0,0,0,12,294.03,-19.29,-91.3375,16782,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742637,141,2,308,302,270,0,0,0,14,309.1,-18.98,-169.461,29993,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757660,141,2,308,302,270,0,0,0,12,307.85,-19.29,-65.6699,5917,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757662,141,2,308,302,270,0,0,0,12,307.27,-19.29,-59.8321,16605,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742635,141,2,308,302,270,0,0,0,14,310.83,-18.36,-140.299,43713,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742636,141,2,308,302,270,0,0,0,14,310.64,-19.04,-167.744,20929,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757543,141,2,216,26,126,0,0,0,14,315.13,-18.76,-178.511,23500,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757673,141,2,216,26,126,0,0,0,14,313.2,-19.29,-73.788,39472,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754630,141,2,292,197,193,0,0,0,13,323.54,-13.72,-35.0975,61884,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742693,141,2,279,38,98,0,0,0,14,318.27,-19.11,-152.023,33285,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754629,141,2,292,197,193,0,0,0,13,329.54,-13.96,-31.9957,60439,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757518,141,2,216,26,126,0,0,0,14,333.33,-18.57,-136.46,2519,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757661,141,2,308,302,270,0,0,0,12,331.77,-18.79,-70.8015,595,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754620,141,2,279,38,98,0,0,0,13,329.15,-13.94,-35.2421,15040,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754659,141,2,279,38,98,0,0,0,14,344.14,-18.56,-84.9706,58807,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742630,141,2,308,302,270,0,0,0,14,345.66,-18.56,-106.489,57000,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757530,141,2,216,26,126,0,0,0,14,344.92,-17.83,-135.017,63663,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742692,141,2,279,38,98,0,0,0,14,351.58,-19.24,-106.611,27301,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742631,141,2,308,302,270,0,0,0,14,356.54,-19.29,-113.81,20641,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742634,141,2,308,302,270,0,0,0,14,354.07,-18.96,-146.959,35003,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755837,141,2,279,38,98,0,0,0,13,358.3,-17.16,-66.1784,2203,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755853,141,2,292,197,193,0,0,0,13,356.39,-14.87,-43.6618,24130,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073756924,141,2,216,26,126,0,0,0,14,354.43,-19.21,-85.9826,57034,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742632,141,2,308,302,270,0,0,0,14,365.45,-19.06,-137.721,22738,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757832,141,2,1325,1298,0,0,4,0,14,363.84,-17.56,-71.3948,3458,0,1,0,0,'\02e\0\0\0\0\02\02\0\0\0\0','\0\0\0\0!\0\0\0\0\0\08\0\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755856,141,2,292,197,193,0,0,0,13,367.32,-16.52,-61.5579,693,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742633,141,2,308,302,270,0,0,0,14,369.01,-19.14,-145.158,11356,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754675,141,2,308,302,270,0,0,0,14,369.55,-19.16,-105.623,22120,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754683,141,2,292,197,193,0,0,0,13,386.26,-17.72,-100.493,56592,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742700,141,2,279,38,98,0,0,0,13,385.08,-17.47,-121.433,39699,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754681,141,2,292,197,193,0,0,0,13,394.19,-17.83,-98.9006,40971,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757550,141,2,292,197,193,0,0,0,13,390.89,-17.53,-133.981,51581,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757906,141,2,780,952,1,0,3,0,12,110.62,1.58,-139.119,2306,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073746617,141,2,201,41,59,0,0,0,5,-11.91,12.28,-274.082,23624,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753390,141,2,294,432,192,0,0,0,5,-4.24,10.45,-280.567,49539,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755633,141,2,254,736,3,0,0,0,6,37.67,19.67,-277.326,44616,0,2,2147483647,2147483647,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742424,141,2,201,41,59,0,0,0,5,15.2,10.71,-292,5572,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755636,141,2,246,735,3,0,0,0,6,52.01,21.56,-300.106,56866,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742474,141,2,282,431,156,0,0,0,8,-67.35,24.43,-295.939,53871,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742399,141,2,201,41,59,0,0,0,8,-69.28,20.69,-306.804,12136,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742398,141,2,201,41,59,0,0,0,8,-65.39,22.4,-308.739,38530,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742423,141,2,201,41,59,0,0,0,8,-3.97,15.46,-309.288,28397,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073743020,141,2,294,432,192,0,0,0,5,-71.75,13.5,-246.187,60622,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742400,141,2,201,41,59,0,0,0,8,-90.91,9.4,-305.319,43664,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750986,141,2,282,431,156,0,0,0,8,-107.88,10.32,-323.241,23421,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750984,141,2,282,431,156,0,0,0,8,-74.8,15.09,-343.51,10015,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753567,141,2,244,1445,95,0,0,0,17,-116.58,-0.32,-233.967,30588,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750893,141,2,294,432,192,0,0,0,9,-142.09,-3.05,-316.501,22310,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750895,141,2,201,41,59,0,0,0,9,-145.03,-4.53,-311.952,48059,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757824,141,2,282,431,156,0,0,0,6,-140.43,4.66,-155.524,35391,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753393,141,2,294,432,192,0,0,0,9,-153.69,-4.97,-291.693,3742,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750894,141,2,294,432,192,0,0,0,9,-149.21,-4.49,-328.964,65225,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750897,141,2,201,41,59,0,0,0,6,-150.18,13.13,-184.3,41749,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742397,141,2,201,41,59,0,0,0,8,-35.68,21.23,-345.325,56932,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755612,141,2,282,431,156,0,0,0,8,18.16,15.53,-345.975,25709,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747944,141,2,282,431,156,0,0,0,8,-16.5,19.09,-345.822,52908,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747952,141,2,282,431,156,0,0,0,8,-32.8,20.84,-346.626,13400,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073757363,141,2,282,431,156,0,0,0,6,-156.53,13.93,-195.314,4289,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747215,141,2,294,432,192,0,0,0,9,-167.17,-1.86,-343.467,26696,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742387,141,2,201,41,59,0,0,0,9,-166.59,4.11,-248.467,21785,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747000,141,2,294,432,192,0,0,0,9,-180.09,4.39,-262.187,2454,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747004,141,2,294,432,192,0,0,0,9,-181.13,-3.32,-293.111,14569,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073754799,141,2,201,41,59,0,0,0,9,-185.91,-4.09,-312.631,58229,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747445,141,2,294,432,192,0,0,0,9,-183.64,-3.97,-320.373,1824,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747212,141,2,294,432,192,0,0,0,9,-146.07,-0.93,-356.725,14439,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742384,141,2,201,41,59,0,0,0,9,-159.55,-0.54,-358.589,49094,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753397,141,2,294,432,192,0,0,0,9,-189.22,-4.14,-314.379,42004,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750987,141,2,201,41,59,0,0,0,8,-94.02,18.25,-366.474,29431,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747220,141,2,294,432,192,0,0,0,9,-169.1,4.54,-377.28,38845,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750991,141,2,282,431,156,0,0,0,8,-121.31,25.41,-373.042,40240,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073750990,141,2,282,431,156,0,0,0,8,-124.29,25.82,-382.001,5442,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073751557,141,2,293,197,193,0,0,0,12,-208.61,2.28,-380.914,44513,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755934,141,2,201,41,59,0,0,0,8,-113.83,27.36,-393.187,29964,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742383,141,2,201,41,59,0,0,0,9,-174.76,10.83,-393.904,61462,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742386,141,2,201,41,59,0,0,0,9,-207.61,18.33,-247.72,44060,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073753398,141,2,294,432,192,0,0,0,9,-214.45,-3.4,-339.342,12582,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742535,141,2,294,432,192,0,0,0,9,-212.71,15.18,-403.011,39839,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073755509,141,2,294,432,192,0,0,0,9,-214.87,1.71,-288.939,12341,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742523,141,2,294,432,192,0,0,0,9,-217.47,16.13,-257.454,27513,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742524,141,2,294,432,192,0,0,0,9,-215.72,16.08,-256.181,39367,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742478,141,2,294,432,192,0,0,0,9,-219.21,16.6,-408.561,35915,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742536,141,2,294,432,192,0,0,0,9,-177.38,15.05,-409.337,32106,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742378,141,2,201,41,59,0,0,0,9,-203.41,16.91,-414.429,60987,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742392,141,2,201,41,59,0,0,0,8,-152.47,27.67,-421.05,33833,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742591,141,2,244,1445,95,0,0,0,17,-140.8,27.59,-412.768,18366,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073747437,141,2,201,41,59,0,0,0,9,-230.11,4.64,-286.572,35322,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742472,141,2,282,431,156,0,0,0,8,-139.94,33.72,-445.53,56355,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742394,141,2,201,41,59,0,0,0,8,-115.11,30.16,-449.624,33380,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742479,141,2,294,432,192,0,0,0,9,-207.86,20.35,-449.664,7995,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143621,148,2,91,100,110,0,0,0,43,-577.95,21.56,-216.396,3142,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143624,148,2,117,135,384,0,0,0,34,-564.76,49.64,-217.889,63320,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150481,148,2,91,100,110,0,0,0,33,-482.33,58.99,-218.495,56056,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150485,148,2,91,101,109,0,0,0,33,-488.44,58.08,-220.096,27490,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150494,148,2,48,48,29,0,0,0,32,-396.28,64.26,-219.054,59150,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143639,148,2,91,98,107,0,0,0,34,-564.06,50.45,-211.687,16804,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150441,148,2,117,135,384,0,0,0,34,-471.56,59.29,-213.544,11280,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074123446,148,2,117,135,384,0,0,0,32,-386.83,64.49,-231.564,29065,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150516,148,2,48,48,29,0,0,0,32,-390.31,69.18,-254.885,60899,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074056089,148,2,91,98,107,0,0,0,34,-547.24,50.94,-202.174,31644,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150489,148,2,91,101,109,0,0,0,33,-452.71,56.39,-206.001,13542,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150491,148,2,91,101,109,0,0,0,33,-453.72,56.19,-203.46,32320,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143717,148,2,91,98,107,0,0,0,43,-532.06,20.64,-200.697,5663,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104430,148,2,91,101,109,0,0,0,43,-536.28,20.67,-200.764,3219,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073938681,148,2,91,98,107,0,0,0,34,-546.33,50.89,-201.402,31921,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074124927,148,2,91,99,108,0,0,0,43,-532.28,21.08,-204.515,4542,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150503,148,2,91,101,109,0,0,0,33,-449.56,54.15,-195.458,36193,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074123365,148,2,91,100,110,0,0,0,43,-557.42,19.99,-187.538,51592,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147058,148,2,48,48,29,0,0,0,32,-369.7,66.44,-231.924,28051,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150519,148,2,48,48,29,0,0,0,32,-361.28,63.8,-258.087,20375,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146411,148,2,91,99,108,0,0,0,43,-498.14,17.82,-164.74,4017,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147086,148,2,48,48,29,0,0,0,32,-354.02,67.73,-226.221,6558,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146414,148,2,91,100,110,0,0,0,43,-499.71,17.5,-161.117,56148,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147447,148,2,130,53,97,0,0,0,31,-355.51,63.3,-156.663,9133,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147462,148,2,130,53,97,0,0,0,31,-352.46,62.47,-150.431,46225,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150529,148,2,130,53,97,0,0,0,31,-434.96,61.48,-145.651,65106,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143659,148,2,130,53,97,0,0,0,31,-425.27,61.47,-145.105,46526,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149430,148,2,48,48,29,0,0,0,32,-353.28,67.63,-224.067,17027,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143641,148,2,91,101,109,0,0,0,34,-571.61,49.78,-227.671,45678,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143649,148,2,91,99,108,0,0,0,43,-579.17,22.7,-237.956,41991,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149059,148,2,131,30,81,0,0,0,43,-578.56,17.68,-170.282,60353,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115531,148,2,91,98,107,0,0,0,43,-575.18,11.23,-130.628,16616,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115532,148,2,91,100,110,0,0,0,43,-576.32,11.37,-133.691,6620,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115533,148,2,91,99,108,0,0,0,43,-564.17,11.86,-132.802,45703,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074124649,148,2,117,135,384,0,0,0,31,-417.34,61.79,-130.524,27182,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143619,148,2,91,98,107,0,0,0,43,-583.89,22.26,-236.686,41862,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149263,148,2,130,53,97,0,0,0,31,-410.24,60.99,-116.872,31994,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074118708,148,2,130,53,97,0,0,0,31,-461.3,63.32,-109.555,64425,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150518,148,2,48,48,29,0,0,0,32,-354.45,65.78,-253.792,26209,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150020,148,2,48,48,29,0,0,0,32,-344.65,66.94,-223.61,61911,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074131210,148,2,117,135,384,0,0,0,32,-348.5,67.32,-235.016,8189,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074118703,148,2,130,53,97,0,0,0,31,-465.5,63.03,-102.797,32297,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145863,148,2,130,53,97,0,0,0,31,-418.36,61.05,-102.666,6313,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074127185,148,2,117,135,384,0,0,0,31,-399.55,57.43,-89.9314,13079,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074123667,148,2,236,305,265,0,0,0,32,-513.25,40.42,-79.3393,62943,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150428,148,2,130,53,97,0,0,0,31,-362.88,62.02,-84.3213,35535,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150429,148,2,130,53,97,0,0,0,31,-365.67,62.19,-81.111,35184,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074109031,148,2,236,305,265,0,0,0,32,-504.14,41.89,-80.6768,16971,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141639,148,2,91,99,108,0,0,0,33,-477.1,71.08,-299.46,15062,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074133053,148,2,91,101,109,0,0,0,43,-557.33,25.69,-261.024,17910,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148603,148,2,131,30,81,0,0,0,43,-552.6,25.12,-253.319,21277,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147580,148,2,91,98,107,0,0,0,43,-558.16,29.44,-229.861,53197,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074117576,148,2,91,99,108,0,0,0,33,-479.38,75.15,-316.412,16073,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074117581,148,2,91,100,110,0,0,0,33,-481.82,73.93,-315.381,41001,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143622,148,2,91,101,109,0,0,0,43,-596.64,21.44,-225.969,26520,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074078771,148,2,91,101,109,0,0,0,43,-618.04,23.01,-192.896,20951,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150477,148,2,91,98,107,0,0,0,33,-462.75,65.71,-268.177,65040,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150487,148,2,91,101,109,0,0,0,33,-456.9,62.64,-234.088,8856,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104994,148,2,236,305,265,0,0,0,32,-513.51,40.42,-79.6551,57703,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074126722,148,2,236,305,265,0,0,0,32,-519.26,40.87,-62.0619,47858,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150439,148,2,236,305,265,0,0,0,32,-481.39,46.42,-63.4691,54178,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150445,148,2,236,305,265,0,0,0,32,-484.43,45.97,-62.8671,58397,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150446,148,2,236,305,265,0,0,0,32,-505.84,41.59,-51.6,65337,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146230,148,2,130,53,97,0,0,0,33,-414.83,64.45,-55.2985,39675,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147916,148,2,91,101,109,0,0,0,33,-484,67.07,-268.051,63551,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125507,148,2,117,135,384,0,0,0,34,-488.39,68.75,-277.163,7435,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074121792,148,2,91,98,107,0,0,0,33,-504.23,73.38,-289.556,4087,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074121783,148,2,91,99,108,0,0,0,33,-507.37,74.14,-293.898,59823,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073805779,148,2,117,135,384,0,0,0,34,-500.56,72.47,-293.858,50587,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147609,148,2,131,30,81,0,0,0,43,-507.39,24.02,-246.307,17831,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115024,148,2,91,100,110,0,0,0,34,-508,58.09,-259.421,57620,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148599,148,2,91,98,107,0,0,0,34,-511.16,52.33,-230.824,10795,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104340,148,2,91,98,107,0,0,0,43,-486.65,18.83,-230.98,5548,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142022,148,2,91,99,108,0,0,0,33,-487.93,67.85,-269.794,392,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742863,148,2,453,580,0,0,0,0,35,-307.5,63.45,-285.688,32767,0,4,2147483647,0,'\02\0\0\0\02\02\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150421,148,2,2190,2267,384,0,0,0,29,-307.24,60.31,-131.021,21261,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150613,148,2,48,48,29,0,0,0,29,-306.19,61.98,-163.47,32643,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150644,148,2,48,48,29,0,0,0,29,-307.12,61.38,-158.183,19925,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147846,148,2,2190,2267,384,0,0,0,29,-292.94,59.6,-125.034,8801,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149411,148,2,48,48,29,0,0,0,29,-295.24,67.29,-218.534,19177,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150670,148,2,48,48,29,0,0,0,29,-291.2,61.13,-169.269,10859,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149418,148,2,48,48,29,0,0,0,29,-291.12,67.35,-218.221,39577,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150573,148,2,2190,2267,384,0,0,0,29,-289.69,61.12,-171.384,29169,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150352,148,2,48,48,29,0,0,0,29,-241.51,66,-175.433,49359,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150364,148,2,48,48,29,0,0,0,29,-243.3,65.35,-175.424,28734,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150656,148,2,48,48,29,0,0,0,29,-291.82,60.48,-117.801,8579,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742862,148,2,453,582,0,0,0,0,35,-206.13,55.31,-126.978,15470,0,4,2147483647,0,'\02\0\0\0\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150746,148,2,519,1213,0,0,4,0,1,-322.76,60.91,-97.9591,63437,0,1,2147483647,0,'2A;\0\0;\0\0\0\0K\02\0\0\0','\0\\0\0\\0 \0d\0\0g\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147701,148,2,221,178,154,0,0,0,30,-304.64,62.24,-99.3098,54473,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149366,148,2,221,178,154,0,0,0,30,-278.04,61.23,-86.2514,10623,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150745,148,2,130,53,97,0,12,0,28,-337.87,61.15,-76.9278,57772,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145460,148,2,91,101,109,0,0,0,43,-514.62,22.54,-226.313,59960,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147604,148,2,91,100,110,0,0,0,43,-530.3,25.22,-251.977,25479,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074126489,148,2,117,135,384,0,0,0,34,-525.79,49.81,-245.501,53559,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145475,148,2,91,99,108,0,0,0,43,-529.62,25.06,-255.837,9584,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074127346,148,2,91,99,108,0,0,0,34,-531.1,49.68,-250.864,52419,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150447,148,2,236,305,265,0,0,0,32,-512.58,40.41,-59.6179,55921,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146168,148,2,130,53,97,0,0,0,33,-418.89,62.4,-46.7604,23946,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139985,148,2,91,100,110,0,0,0,34,-536.28,42.79,-228.716,20130,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074131340,148,2,130,53,97,0,0,0,33,-455.73,63.08,-30.0253,6968,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146202,148,2,130,53,97,0,0,0,33,-419.02,58.84,-34.1301,50440,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074131347,148,2,130,53,97,0,0,0,33,-445.04,57.65,-20.2285,33807,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150669,148,2,238,304,145,0,0,0,31,-400.11,49.51,5.79553,4555,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150239,148,2,238,304,145,0,0,0,31,-430.65,49.54,0.335085,26262,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150268,148,2,238,304,145,0,0,0,31,-434.24,49.68,33.0577,17532,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146471,148,2,2057,2193,145,0,0,0,31,-320.1,58.15,-10.6655,58676,1,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147962,148,2,221,178,154,0,0,0,30,-315.35,62.57,-46.0749,24156,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149014,148,2,221,178,154,0,0,0,30,-308.38,63.13,-52.4908,3322,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149571,148,2,2057,2193,145,0,0,0,31,-304.69,57.74,-12.0992,60595,1,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147951,148,2,117,135,384,0,0,0,30,-275.43,63.68,-49.246,55675,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149044,148,2,2919,3175,27,0,0,0,50,-267.89,63.39,-20.0865,50274,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149182,148,2,221,178,154,0,0,0,30,-264,63.89,-45.4584,4591,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145150,148,2,117,135,384,0,0,0,31,-404.94,53.4,33.3382,53478,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148135,148,2,238,304,145,0,3,0,31,-401.76,53.51,36.3503,110,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149941,148,2,207,182,167,0,0,0,31,-506.14,65.51,38.9582,56779,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142867,148,2,238,304,145,0,0,0,31,-440.31,49.51,48.1553,21767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149939,148,2,207,182,167,0,0,0,31,-532.69,65.69,45.8654,44014,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149638,148,2,55,58,233,0,0,0,33,-289.73,62,24.6731,18091,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149642,148,2,55,58,233,0,0,0,33,-265.58,55.92,33.4562,3817,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150690,148,2,238,304,145,0,0,0,31,-383.73,51.5,51.5443,15153,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145154,148,2,117,135,384,0,0,0,33,-360.06,57.98,63.176,42802,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145174,148,2,117,135,384,0,0,0,31,-418.47,55.22,63.4967,27179,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074005480,148,2,117,135,384,0,0,0,33,-316.07,64.31,62.3932,29757,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149022,148,2,207,182,167,0,0,0,31,-460.93,64.6,72.2725,44451,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149956,148,2,207,182,167,0,0,0,31,-523.41,65.54,61.3492,13552,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149714,148,2,207,182,167,0,0,0,31,-502.3,65.67,84.67,5607,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149959,148,2,207,182,167,0,0,0,31,-525.11,65.62,97.203,51215,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149979,148,2,207,182,167,0,0,0,31,-522.77,65.62,99.3184,64732,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134810,148,2,39,39,50,0,0,0,14,-336.36,21.31,157.206,5885,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134817,148,2,54,57,60,0,0,0,14,-339.68,21.38,160.008,64956,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134811,148,2,39,39,50,0,0,0,14,-336.71,21.95,168.612,29220,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134816,148,2,39,39,50,0,0,0,14,-301.03,22.1,129.475,1859,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134812,148,2,39,39,50,0,0,0,14,-294.43,21.49,170.757,51979,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134813,148,2,39,39,50,0,0,0,14,-292.6,20.82,118.714,57085,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134814,148,2,39,39,50,0,0,0,14,-300.4,21.53,166.055,10545,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140841,148,2,39,39,50,0,0,0,14,-291.4,20.25,130.769,18792,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134788,148,2,39,39,50,0,0,0,14,-267.85,20.39,91.1813,51817,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134790,148,2,39,39,50,0,0,0,14,-266.93,18.32,60.2164,15290,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134793,148,2,54,57,60,0,0,0,14,-263.65,17.35,61.4501,55248,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134798,148,2,39,39,50,0,0,0,14,-255.98,9.96,10.2603,12281,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134799,148,2,54,57,60,0,0,0,14,-242.41,8.66,-2.98973,11773,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149646,148,2,55,58,233,0,0,0,33,-241.82,60.07,34.6751,410,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134795,148,2,39,39,50,0,0,0,14,-240.22,13.67,51.2775,36047,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134797,148,2,39,39,50,0,0,0,14,-236.39,15.01,52.6155,63584,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074103166,148,2,117,135,384,0,0,0,34,-238.33,63.4,25.7918,45971,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149667,148,2,55,58,233,0,0,0,33,-224.23,68.62,11.5693,27009,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149327,148,2,221,178,154,0,0,0,30,-216.82,57.96,-68.8442,27465,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149345,148,2,221,178,154,0,0,0,30,-219.54,58.1,-56.9581,46903,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150513,148,2,218,771,14,0,0,0,10,-223.2,1.41,-71.2168,63605,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148555,148,2,218,771,14,0,0,0,10,-214.17,1.5,-82.278,56195,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150475,148,2,54,57,60,0,0,0,10,-206.71,2.54,-60.9597,17874,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150493,148,2,218,771,14,0,0,0,10,-212.57,1.5,-84.179,30121,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150504,148,2,218,771,14,0,0,0,10,-213.86,1.44,-83.871,41709,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074117891,148,2,39,39,50,0,0,0,14,-202.96,4.83,4.34942,43187,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149017,148,2,218,771,14,0,0,0,10,-212.64,2.07,-91.4491,24977,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149658,148,2,55,58,233,0,0,0,33,-203.65,57.63,49.9289,61934,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149169,148,2,221,178,154,0,0,0,30,-203.89,50.67,-40.6238,49399,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147381,148,2,218,771,14,0,0,0,10,-211.95,1.94,-88.2669,27437,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150474,148,2,218,771,14,0,0,0,10,-199.56,2.07,-65.1038,41840,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150486,148,2,54,57,60,0,0,0,10,-196.63,2.46,-25.251,4266,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150492,148,2,218,771,14,0,0,0,10,-193.91,2.13,-41.0693,23183,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148966,148,2,55,58,233,0,0,0,33,-192.64,77.68,42.3563,30295,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150650,148,2,218,771,14,0,0,0,10,-195.51,2.22,-40.1862,51844,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148986,148,2,117,135,384,0,0,0,34,-196.69,74.54,21.4171,14820,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150662,148,2,446,483,233,0,0,0,34,-183.72,77.7,38.0484,41274,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150462,148,2,218,771,14,0,0,0,10,-187.08,3.03,-83.1889,42863,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125641,148,2,55,58,233,0,0,0,33,-188.4,78.27,45.2144,20146,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147095,148,2,218,771,14,0,0,0,10,-199.68,3.4,-108.169,12367,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147362,148,2,10,10,21,0,0,0,10,-179.58,3.14,-107.269,63500,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150575,148,2,13,12,39,0,0,0,7,-179.93,32.81,-96.5614,24383,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134847,148,2,54,57,60,0,0,0,10,-178.79,4.5,-119.585,653,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147537,148,2,54,57,60,0,0,0,10,-175.52,29.33,-86.8876,19789,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147539,148,2,54,57,60,0,0,0,10,-173.17,28.73,-89.1809,16691,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150574,148,2,13,12,39,0,0,0,7,-167.33,25.98,-90.4907,12888,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074120932,148,2,54,57,60,0,0,0,10,-167.14,1.9,-14.6108,1461,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150576,148,2,13,12,39,0,0,0,7,-163.68,21.02,-74.7567,15963,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147541,148,2,54,57,60,0,0,0,10,-157.63,17.41,-70.6175,64613,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150816,148,2,203,32,35,0,0,0,14,-147.79,0.98,-53.7358,62786,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150848,148,2,177,39,50,0,0,0,14,-133.75,0.19,-32.0003,55685,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150849,148,2,177,39,50,0,0,0,14,-156.74,1.36,-31.9995,56346,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150850,148,2,177,39,50,0,0,0,14,-155.63,1.71,-37.2865,56346,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150851,148,2,177,39,50,0,0,0,14,-156.32,1.47,-33.7076,56346,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150847,148,2,177,39,50,0,0,0,14,-128.46,-0.57,-35.9663,55685,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150856,148,2,1401,1012,409,0,40,0,13,-145.29,0.32,-28.6584,46189,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150845,148,2,177,39,50,0,0,0,14,-133.11,-0.19,-89.4992,46000,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150846,148,2,177,39,50,0,0,0,14,-152.15,1.71,-90.0925,24874,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150861,148,2,177,39,50,0,0,0,13,-141.14,1.25,-35.3338,51535,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150862,148,2,180,22,48,0,0,0,10,-139.61,1.09,-35.4518,51535,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150863,148,2,177,39,50,0,0,0,13,-120.09,-1.37,-94.0399,37070,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134825,148,2,39,39,50,0,0,0,14,-353.27,21.33,191.185,8482,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134821,148,2,54,57,60,0,0,0,14,-330.22,21.34,206.3,548,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134819,148,2,54,57,60,0,0,0,14,-293.87,21.59,206.024,9600,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134824,148,2,54,57,60,0,0,0,14,-333.53,21.13,211.305,16795,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150888,148,2,177,39,50,0,0,0,13,-149.87,0.64,-28.8264,11249,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150891,148,2,177,39,50,0,3,0,13,-146.81,1.88,-43.3122,44406,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150889,148,2,177,39,50,0,0,0,13,-155.9,1.83,-53.7081,5640,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150887,148,2,177,39,50,0,0,0,13,-141.37,0.37,-15.2466,53811,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150885,148,2,177,39,50,0,0,0,13,-107.21,-2.38,-81.9306,42207,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150886,148,2,177,39,50,0,0,0,13,-105.78,-4.1,-62.9076,36742,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150864,148,2,180,22,48,0,0,0,10,-141.96,1.64,-113.779,5442,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146081,148,2,54,57,60,0,0,0,10,-88.09,1.92,-123.946,45279,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147667,148,2,54,57,60,0,0,0,10,-93.27,2.11,-123.136,5642,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150640,148,2,10,10,21,0,0,0,10,-117.68,7.55,-144.928,13479,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150608,148,2,54,57,60,0,0,0,10,-106.55,6.09,-153.253,55956,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149007,148,2,10,10,21,0,0,0,10,-119.28,10.7,-161.837,48793,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150289,148,2,10,10,21,0,0,0,10,-124.75,12.37,-167.874,24985,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149035,148,2,10,10,21,0,0,0,10,-78.19,1.4,-143.797,56388,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149040,148,2,10,10,21,0,0,0,10,-77.28,0.69,-130.946,33008,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149045,148,2,10,10,21,0,0,0,10,-73.92,0.9,-141.706,32051,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150892,148,2,473,503,63,0,0,0,13,-76.53,-5.4,-62.313,388,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142319,148,2,197,159,76,0,0,0,7,-65.58,-3.37,-198.9,54901,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142325,148,2,197,159,76,0,0,0,7,-63.52,-3.25,-211.634,61586,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149064,148,2,10,10,21,0,0,0,10,-68.46,1.35,-163.448,2045,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150611,148,2,10,10,21,0,0,0,10,-61.65,-2.68,-115.279,23490,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074132487,148,2,197,159,76,0,0,0,7,-53.93,-3.49,-186.83,55121,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148398,148,2,197,159,76,0,0,0,7,-51.9,-3.49,-180.504,32900,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148910,148,2,54,57,60,0,0,0,10,-58.67,1.21,-159.385,25355,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149061,148,2,10,10,21,0,0,0,10,-59.19,-0.14,-166.955,46711,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148829,148,2,54,57,60,0,0,0,10,-57.74,-3.82,-94.6154,14214,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147545,148,2,56,59,385,0,0,0,7,-46.83,-2.59,-210.275,59267,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147554,148,2,56,59,385,0,0,0,7,-35.43,-3.49,-179.448,46004,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148390,148,2,54,57,60,0,0,0,10,-25.99,1.18,-138.777,10689,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150708,148,2,10,10,21,0,0,0,10,-30.01,-4.18,-95.0337,54491,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150735,148,2,128,1446,104,0,0,0,12,-31.91,0.91,-223.546,12277,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148925,148,2,54,57,60,0,0,0,10,-23.46,-1.69,-101.473,19276,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150241,148,2,10,10,21,0,0,0,10,-19.14,0.59,-139.413,52620,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074114481,148,2,49,49,57,0,0,0,2,-8.9,3.94,-218.33,64799,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125019,148,2,37,37,25,0,0,0,2,-16.71,3.12,-229.033,29436,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125023,148,2,49,49,57,0,0,0,2,-16.75,3.05,-231.639,9758,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074138082,148,2,37,37,25,0,0,0,2,-15.7,1.44,-197.876,16128,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141200,148,2,37,37,25,0,0,0,2,-2.59,1.6,-187.65,24598,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148625,148,2,56,59,385,0,0,0,7,-0.93,-11.25,-122.926,12550,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147257,148,2,56,59,385,0,0,0,7,-8.43,-3.45,-134.922,29790,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074109375,148,2,37,37,25,0,0,0,2,-8.39,4.86,-235.431,13692,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147560,148,2,56,59,385,0,0,0,7,-6.66,-3.42,-134.944,35458,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141198,148,2,37,37,25,0,0,0,2,-0.87,1.77,-186.554,20979,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141204,148,2,37,37,25,0,0,0,2,4.69,2.41,-177.618,19701,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148629,148,2,197,159,76,0,0,0,7,8.28,-10.98,-107.746,53789,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074097948,148,2,49,49,57,0,0,0,2,7.08,5.03,-252.01,6698,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141044,148,2,49,49,57,0,0,0,2,3.02,2.01,-189.069,18626,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074103960,148,2,37,37,25,0,0,0,2,7.62,5.57,-247.935,23900,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074138030,148,2,37,37,25,0,0,0,2,12.71,3.53,-142.608,10960,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074109369,148,2,49,49,57,0,0,0,2,13.24,5.79,-248.925,44259,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150590,148,2,197,159,76,0,0,0,7,11.49,-11.11,-75.6975,16944,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074138047,148,2,49,49,57,0,0,0,2,13.58,3.99,-145.607,47452,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074138037,148,2,49,49,57,0,0,0,2,20.3,2.91,-137.764,46572,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150580,148,2,197,159,76,0,0,0,7,22.48,-11.37,-66.3192,44772,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742859,148,2,453,583,0,0,0,0,35,-4.84,-6.28,-22.2019,65534,0,4,2147483647,0,'2&\0\0\0\0&\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150902,148,2,780,952,1,0,0,0,60,-284.91,63.2,-31.9209,63353,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0I\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150922,148,2,2190,2267,384,0,0,0,29,-288.59,59.58,-121.736,32762,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150924,148,2,2190,2267,384,0,0,0,29,-290.94,61.81,-175.341,32759,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150926,148,2,2190,2267,384,0,0,0,29,-303.82,59.83,-130.572,32757,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150915,148,2,177,39,50,0,3,0,13,-123.82,0.07,-102.34,2219,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150938,148,2,491,475,79,0,0,0,13,-120.48,3.23,-126.757,37586,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150939,148,2,491,475,79,0,0,0,13,-118.07,3.39,-128,37586,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150940,148,2,491,475,79,0,0,0,13,-142.36,0.49,-88.1202,38603,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150941,148,2,491,475,79,0,0,0,13,-131.64,-0.65,-85.0862,38603,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150942,148,2,491,475,79,0,0,0,13,-144.24,1.61,-36.0888,51535,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150943,148,2,491,475,79,0,0,0,13,-151.4,1.5,-33.3678,51535,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150944,148,2,56,59,385,0,0,0,11,-0.15,-6.68,-57.2573,23555,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150949,148,2,197,159,76,0,0,0,7,11.38,-11.23,-110.549,49170,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150934,148,2,197,159,76,0,0,0,7,22.43,-11.52,-69.7232,1406,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150946,148,2,197,159,76,0,0,0,7,19.27,-11.64,-77.3182,63889,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148356,148,2,49,49,57,0,0,0,2,38.72,4.36,-180.845,29704,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148888,148,2,37,37,25,0,0,0,2,42.67,6.85,-224.263,23287,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148350,148,2,49,49,57,0,0,0,2,39.58,4.09,-187.778,5311,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148894,148,2,37,37,25,0,0,0,2,42.49,7.1,-225.001,24657,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148884,148,2,37,37,25,0,0,0,2,47.55,4.8,-207.294,13864,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148889,148,2,49,49,57,0,0,0,2,51.24,5.9,-220.073,21518,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148893,148,2,37,37,25,0,0,0,2,45.19,5.92,-217.49,37949,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150712,148,2,37,37,25,0,0,0,2,45.2,8.33,-237.257,10918,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150727,148,2,128,1446,104,0,0,0,12,49.96,9.66,-244.647,54114,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148357,148,2,37,37,25,0,0,0,2,59.05,4.62,-184.917,56007,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148358,148,2,37,37,25,0,0,0,2,52.2,4.43,-174.959,58483,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148359,148,2,37,37,25,0,0,0,2,55.67,4.58,-183.903,18598,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148179,148,2,37,37,25,0,0,0,1,67.27,17.54,-265.641,62876,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148883,148,2,37,37,25,0,0,0,2,71.44,5.46,-208.484,21533,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148182,148,2,49,49,57,0,0,0,1,74.64,11.43,-244.315,5432,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148000,148,2,37,37,25,0,0,0,1,73.4,16.85,-261.669,52713,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150957,148,2,56,59,385,0,0,0,11,-0.09,-7.05,-52.388,25486,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148175,148,2,37,37,25,0,0,0,1,81.85,18.68,-266.171,34918,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149285,148,2,47,47,28,0,0,0,3,84.65,4.43,-188.873,64731,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148959,148,2,197,159,76,0,0,0,7,50.37,-11.69,-44.005,56475,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148364,148,2,49,49,57,0,0,0,3,89.16,4.39,-185.837,31156,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742850,148,2,541,901,480,0,0,0,1,95.63,-2.03,-42.3824,1599,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742861,148,2,453,580,0,0,0,0,35,96.4,-5.9,-121.434,5776,0,4,2147483647,0,'\02\0\0\0\02\02\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742880,148,2,541,901,480,0,0,0,1,101.47,-2.01,-46.1767,5565,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742881,148,2,541,901,480,0,0,0,1,98.69,-2.03,-43.9429,3947,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149289,148,2,47,47,28,0,0,0,3,98.48,4.47,-198.12,20561,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742879,148,2,541,901,480,0,0,0,1,104.15,-1.97,-48.333,6641,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148424,148,2,49,49,57,0,0,0,3,111.67,0.87,-168.568,58440,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074137512,148,2,43,43,36,0,0,0,9,88.18,-5.54,3.63018,17143,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074132791,148,2,56,59,385,0,0,0,9,84.22,-6.35,12.8627,5550,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140613,148,2,56,59,385,0,0,0,9,87.81,-5.74,14.5688,59106,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150956,148,2,43,43,36,0,0,0,9,101.81,-6.36,42.5077,4610,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147584,148,2,43,43,36,0,0,0,9,126.28,-5.22,13.6885,41519,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742851,148,2,541,901,480,0,0,0,1,126.42,-6.97,-84.5294,50265,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742852,148,2,541,901,480,0,0,0,1,126.7,-6.95,-90.4596,49276,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742855,148,2,541,901,480,0,0,0,1,127.64,-6.33,-100.389,32767,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742854,148,2,541,901,480,0,0,0,1,132.95,-6.33,-100.115,34111,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148397,148,2,49,49,57,0,0,0,3,130.14,2.19,-171.344,34964,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150704,148,2,47,47,28,0,0,0,3,137.32,1.83,-164.398,1054,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149111,148,2,47,47,28,0,0,0,3,137.08,0.08,-144.287,58024,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147578,148,2,43,43,36,0,0,0,9,131.54,-6.52,15.6705,34226,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742853,148,2,541,901,480,0,0,0,1,137.91,-6.44,-96.6084,20749,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150705,148,2,49,49,57,0,0,0,3,140,1.2,-158.014,39316,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143851,148,2,47,47,28,0,0,0,3,145.93,1.19,-120.79,26245,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742858,148,2,453,580,0,0,0,0,35,85.41,-5.99,59.9662,49147,0,4,2147483647,0,'\02\0\0\0\02\02\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140123,148,2,49,49,57,0,0,0,3,151.04,0.26,-124.631,53700,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148173,148,2,43,43,36,0,0,0,9,132.06,-5.57,60.2253,40931,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150310,148,2,43,43,36,0,0,0,9,128.89,-5.96,61.0762,53253,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139651,148,2,47,47,28,0,0,0,3,158.66,0.04,-112.054,31165,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147603,148,2,43,43,36,0,0,0,9,159.78,-7.92,-11.3377,57280,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147610,148,2,43,43,36,0,0,0,9,157.49,-7.54,-18.5203,10256,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150365,148,2,56,59,385,0,0,0,9,102.72,-8.08,78.2805,33572,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149125,148,2,47,47,28,0,0,0,3,163.65,-0.42,-109.785,34523,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742857,148,2,453,582,0,0,0,0,35,14.36,-8.1,100.499,37082,0,4,2147483647,0,'\02\0\0\0\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742860,148,2,453,582,0,0,0,0,35,164.01,-5.9,-52.4863,49599,0,4,2147483647,0,'\02\0\0\0\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149491,148,2,56,59,385,0,0,0,7,94.47,-11.69,100.44,11853,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149492,148,2,56,59,385,0,0,0,7,99.64,-11.57,115.316,31922,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150181,148,2,197,159,76,0,0,0,7,151.84,-11.69,126.578,44681,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150140,148,2,197,159,76,0,0,0,7,160.29,-11.59,121.933,20942,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150831,148,2,197,159,76,0,0,0,7,122.03,-11.62,129.775,37528,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149042,148,2,56,59,385,0,0,0,7,139.73,-11.32,136.443,51526,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150869,148,2,197,159,76,0,0,0,7,130.5,-11.56,134.428,62078,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149510,148,2,22,22,48,0,0,0,10,76.54,-10.68,158.27,59663,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147816,148,2,22,22,48,0,0,0,10,109.2,-8.14,157.223,56247,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147822,148,2,22,22,48,0,0,0,10,112.66,-8.15,156.545,49463,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150948,148,2,22,22,48,0,0,0,10,43.49,-13.11,170.006,28560,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149514,148,2,22,22,48,0,0,0,10,90.94,-9.8,165.003,45020,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149518,148,2,54,57,60,0,0,0,10,84.11,-11.96,171.069,25787,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149519,148,2,22,22,48,0,0,0,10,93.95,-10.1,168.238,56323,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150945,148,2,54,57,60,0,0,0,10,53.83,-15.72,178.004,42546,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150749,148,2,22,22,48,0,0,0,10,70.49,-17.72,188.314,22891,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150965,148,2,590,12,39,0,3,0,8,96.3,-15.84,195.344,2727,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150966,148,2,590,12,39,0,3,0,8,98.12,-15.46,193.713,15455,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148185,148,2,54,57,60,0,0,0,10,87.11,-16.05,191.37,27065,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150753,148,2,22,22,48,0,0,0,10,44.07,-18.67,195.12,36915,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150968,148,2,492,21,63,0,3,0,15,-144.43,1.66,-39.2313,52834,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150977,148,2,43,43,36,0,0,0,9,104.98,-7.76,52.7616,34317,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150756,148,2,22,22,48,0,0,0,10,36.14,-20.24,201.78,18727,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150762,148,2,22,22,48,0,0,0,10,86.31,-18.75,204.807,62727,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150764,148,2,22,22,48,0,0,0,10,79.55,-20.3,207.426,16235,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147251,148,2,54,57,60,0,0,0,10,100.57,-21.72,226.598,36015,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143288,148,2,22,22,48,0,0,0,10,25.66,-23.71,234.503,19543,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144365,148,2,22,22,48,0,0,0,10,89.71,-24.03,237.669,31839,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143506,148,2,22,22,48,0,0,0,10,34.37,-24.76,241.597,16255,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148200,148,2,22,22,48,0,0,0,10,113.61,-25.27,247.984,1387,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144367,148,2,54,57,60,0,0,0,10,87.69,-25.31,242.434,38315,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148068,148,2,54,57,60,0,0,0,10,39.99,-25.46,244.196,24318,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143309,148,2,22,22,48,0,0,0,10,36.69,-27.02,258.878,57754,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141593,148,2,22,22,48,0,0,0,10,90.15,-27.93,260.898,64779,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149831,148,2,10,10,21,0,0,0,12,-10.69,-24.16,264.295,38116,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134497,148,2,22,22,48,0,0,0,10,59.4,-24.47,264.376,50989,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150984,148,2,780,952,1,0,40,0,7,58.61,-23.72,224.674,59776,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150145,148,2,197,159,76,0,0,0,7,176.4,-11.67,108.352,34668,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149908,148,2,197,159,76,0,0,0,7,187.25,-10.9,50.5798,49132,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149063,148,2,56,59,385,0,0,0,7,186.11,-11.69,111.271,5270,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150159,148,2,197,159,76,0,0,0,7,196.86,-11.6,89.4904,53466,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149915,148,2,197,159,76,0,0,0,7,192.39,-11.69,70.0348,56481,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149085,148,2,56,59,385,0,0,0,7,191.97,-11.69,64.4542,45217,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149172,148,2,56,59,385,0,0,0,6,199.89,-11.5,25.4753,24732,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148852,148,2,56,59,385,0,0,0,9,183.08,-8.1,1.31549,32526,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148845,148,2,43,43,36,0,0,0,9,183.12,-8.52,-6.15485,3166,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149765,148,2,196,160,152,0,0,0,6,213.88,-11.42,11.1075,8355,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149777,148,2,196,160,152,0,0,0,6,210.93,-9.35,34.7802,50126,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150841,148,2,21,21,63,0,0,0,9,209.88,23.57,168.008,14667,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147619,148,2,43,43,36,0,0,0,5,203.5,-9.51,-14.4106,21327,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147613,148,2,43,43,36,0,0,0,5,201.64,-8.85,-24.4016,43094,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149787,148,2,196,160,152,0,0,0,6,215.82,-11.45,8.51106,52813,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150826,148,2,21,21,63,0,0,0,9,205.04,24.2,205.827,7615,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150985,148,2,21,21,63,0,0,0,9,230.95,23.65,218.854,25457,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150992,148,2,21,21,63,0,0,0,9,227.03,24.06,228.667,14536,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135918,148,2,41,41,59,0,0,0,8,229.4,23.71,164,27394,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149019,148,2,21,21,63,0,0,0,9,228.91,24.31,175.342,56358,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074042807,148,2,41,41,59,0,0,0,8,212.28,23.51,231,63423,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143972,148,2,41,41,59,0,0,0,8,208.45,23.51,225.549,11497,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145559,148,2,41,41,59,0,0,0,8,232.47,24.04,209.7,2693,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150967,148,2,21,21,63,0,0,0,9,236.64,23.51,154.733,2684,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141595,148,2,22,22,48,0,0,0,10,85.92,-27.09,265.837,44874,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149833,148,2,10,10,21,0,0,0,12,-18.19,-25.15,268.246,25528,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144714,148,2,54,57,60,0,0,0,12,8.33,-30.75,274.234,17401,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139371,148,2,22,22,48,0,0,0,10,52.13,-23.31,272.781,24775,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141596,148,2,22,22,48,0,0,0,10,66.63,-22.86,283.314,38356,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148207,148,2,22,22,48,0,0,0,10,126.3,-28.89,287.781,26121,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145653,148,2,54,57,60,0,0,0,10,98.37,-25.45,284.974,9566,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149251,148,2,54,57,60,0,0,0,12,-28.11,-29.13,288.327,19210,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149461,148,2,10,10,21,0,3,0,12,14.19,-25.98,300.315,1552,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141590,148,2,22,22,48,0,0,0,10,98.23,-23.54,294.825,33590,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141592,148,2,22,22,48,0,0,0,10,91.28,-22.29,292.941,28537,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149844,148,2,10,10,21,0,0,0,12,-58.98,-27.6,293.923,55698,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134503,148,2,22,22,48,0,0,0,10,56.74,-22.3,292.711,26916,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150909,148,2,2919,3175,27,0,0,0,50,94.72,-21.47,300.528,6693,1,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148220,148,2,54,57,60,0,0,0,12,6.42,-26.16,311.133,55135,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150994,148,2,21,21,63,0,0,0,9,209.8,23.46,211.348,14536,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149362,148,2,10,10,21,0,0,0,12,-5.7,-28.61,321.391,2875,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742856,148,2,453,581,0,0,0,0,35,213.25,-32.03,324.145,27122,0,4,2147483647,0,'2&\0\0\0\0&\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148710,148,2,39,39,50,0,0,0,11,143.89,-30.11,330.331,60225,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148735,148,2,39,39,50,0,0,0,11,165.33,-31.79,335.244,51719,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149448,148,2,10,10,21,0,0,0,12,-9.27,-28.69,329.654,29244,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150691,148,2,54,57,60,0,0,0,11,160.5,-31.72,348.12,43904,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148722,148,2,39,39,50,0,0,0,11,160.32,-31.7,350.923,22798,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134539,148,2,54,57,60,0,0,0,12,-56.83,-31.97,346.314,48082,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140498,148,2,10,10,21,0,0,0,12,-64.78,-31.73,350.211,7823,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149437,148,2,10,10,21,0,0,0,12,-12.31,-29.9,354.156,6406,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149309,148,2,10,10,21,0,0,0,12,-69.95,-31.55,347.728,25765,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146060,148,2,129,306,78,0,0,0,12,-82.86,-39.81,327.641,37452,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150238,148,2,20,20,17,0,0,0,13,-85.73,-40.05,306.272,32484,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143021,148,2,54,57,60,0,0,0,12,-47.1,-30.69,366.003,54660,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149936,148,2,20,20,17,0,0,0,13,-87.75,-39.6,288.202,48905,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150983,148,2,20,20,17,0,0,0,13,-94.45,-39.9,311.837,23064,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150988,148,2,20,20,17,0,0,0,13,-88,-39.52,324.766,50947,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148685,148,2,129,306,78,0,0,0,12,-92.57,-39.93,307.152,37931,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134554,148,2,10,10,21,0,0,0,12,-68.19,-28.87,374.591,48540,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146834,148,2,129,306,78,0,0,0,12,-99.44,-39.77,320.152,17314,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149991,148,2,20,20,17,0,0,0,13,-102.95,-39.63,300.105,11265,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135231,148,2,54,57,60,0,0,0,12,-96.11,-30.56,267.981,54141,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144555,148,2,10,10,21,0,0,0,12,-109.78,-30.35,270.325,6624,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150995,148,2,10,10,21,0,0,0,12,-28.71,-27.4,280.689,57949,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150063,148,2,20,20,17,0,0,0,13,-109.88,-39.06,307.71,19118,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147930,148,2,10,10,21,0,0,0,12,9.17,-27.75,390.489,13330,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150197,148,2,20,20,17,0,0,0,13,-118.1,-38.59,309.788,10527,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146674,148,2,129,306,78,0,0,0,12,-114.53,-38.81,301.494,57727,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150137,148,2,20,20,17,0,0,0,13,-112.55,-38.96,313.254,39175,0,2,2147483647,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144109,148,2,54,57,60,0,0,0,12,-128.11,-25.65,364.052,21122,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150578,148,2,10,10,21,0,0,0,12,-135.68,-24.32,264.964,49256,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146758,148,2,10,10,21,0,0,0,12,-132.45,-29.24,338.374,44733,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136233,148,2,54,57,60,0,0,0,12,-141.94,-28.85,307.486,54497,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074101360,148,2,22,22,48,0,0,0,13,-37.24,-29.46,402.823,64437,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149840,148,2,54,57,60,0,0,0,12,-160.57,-28.15,292.586,48632,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149992,148,2,10,10,21,0,0,0,12,-169.93,-27.43,289.322,50819,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149997,148,2,10,10,21,0,0,0,12,-167.88,-27.13,296.046,53002,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150998,148,2,10,10,21,0,0,0,12,-48.92,-35.91,310.263,57949,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143046,148,2,54,57,60,0,0,0,13,-27.16,-32.93,413.023,34566,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074096563,148,2,22,22,48,0,0,0,13,-44.4,-30.73,418.507,53873,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125959,148,2,22,22,48,0,0,0,13,-46.84,-33.41,431.003,3045,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136313,148,2,54,57,60,0,0,0,13,-64.15,-34.17,440.274,18657,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136312,148,2,22,22,48,0,0,0,13,-65.95,-35.42,451.223,26717,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136314,148,2,22,22,48,0,0,0,13,-83.96,-35.58,462.884,5692,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136317,148,2,22,22,48,0,0,0,13,-73.28,-35.87,462.535,62052,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136321,148,2,54,57,60,0,0,0,13,-84.85,-35.61,466.253,12916,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136302,148,2,22,22,48,0,0,0,13,-43.77,-36.56,468.919,35591,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136304,148,2,54,57,60,0,0,0,13,-19.14,-35.63,469.139,35339,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136305,148,2,22,22,48,0,0,0,13,-22.4,-35.4,461.049,63342,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144085,148,2,54,57,60,0,0,0,13,-9.1,-34.06,430.753,48247,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144090,148,2,22,22,48,0,0,0,13,-14.49,-33.56,447.765,23317,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144088,148,2,22,22,48,0,0,0,13,3.21,-31.69,441.698,32442,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074124489,148,2,22,22,48,0,0,0,13,-4.91,-31.96,449.044,12033,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136322,148,2,22,22,48,0,0,0,13,-77.53,-36.14,477.456,54581,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074101329,148,2,22,22,48,0,0,0,13,-60.97,-34.48,491.388,38843,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074125927,148,2,22,22,48,0,0,0,13,-63.35,-35.6,492.866,29893,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151029,148,2,188,39,50,0,3,0,10,-52.4,-32.96,427.468,12623,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151030,148,2,188,39,50,0,3,0,10,-62.37,-31.59,424.233,50709,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151034,148,2,10,10,21,0,0,0,12,-54.82,-25.98,293.217,55802,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151035,148,2,10,10,21,0,0,0,12,7.61,-28.32,293.094,12663,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148216,148,2,39,39,50,0,0,0,11,144.87,-27.97,359.862,14488,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134590,148,2,39,39,50,0,0,0,11,150.1,-24.89,409.453,54192,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145698,148,2,39,39,50,0,0,0,11,171.62,-30.67,389.408,1692,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145715,148,2,39,39,50,0,0,0,11,170.55,-28.7,403.534,58116,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150993,148,2,21,21,63,0,0,0,9,221.67,23.51,226.533,15600,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150681,148,2,39,39,50,0,0,0,11,194.89,-32.74,366.672,426,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150696,148,2,54,57,60,0,0,0,11,175.91,-31.95,364.86,14679,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145677,148,2,39,39,50,0,0,0,11,179.85,-31.99,364.097,30618,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145419,148,2,39,39,50,0,0,0,11,194.86,-32.51,374.702,51727,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146576,148,2,39,39,50,0,0,0,11,174.34,-30.58,392.014,22962,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074145722,148,2,39,39,50,0,0,0,11,174.09,-30.88,390.643,55851,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150728,148,2,54,57,60,0,0,0,11,175.28,-30.1,396.724,64610,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142864,148,2,39,39,50,0,0,0,11,199.58,-31.86,411.338,11780,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150732,148,2,54,57,60,0,0,0,11,211.21,-31.42,397.448,11881,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150682,148,2,39,39,50,0,0,0,11,215.63,-31.43,397.714,19897,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150693,148,2,13,12,39,0,0,0,11,218.14,-32.84,385.09,58765,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150694,148,2,13,12,39,0,0,0,11,245.18,-30.15,402.488,47046,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148818,148,2,13,12,39,0,0,0,11,224.7,-29.73,418.675,5404,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148647,148,2,39,39,50,0,0,0,11,213.45,-27.26,425.003,45157,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144393,148,2,39,39,50,0,0,0,11,208.56,-27.77,425.774,60242,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148794,148,2,54,57,60,0,0,0,11,131.97,-20.13,422.464,37466,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150685,148,2,54,57,60,0,0,0,11,250.62,-29.93,408.389,64330,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134535,148,2,39,39,50,0,0,0,11,270.47,-30.65,395.027,28174,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142834,148,2,39,39,50,0,0,0,11,279.37,-31.27,391.545,12818,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151038,148,2,780,952,1,0,3,0,12,277.34,-31.26,394.629,44632,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','%\0\0%\0\0\0\0\0\0\0I%\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151068,148,2,54,57,60,0,3,0,8,278.88,-31.44,396.887,1894,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151074,148,2,54,57,60,0,0,0,8,279.47,-31.19,396.986,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142720,148,2,39,39,50,0,0,0,11,191.9,-28.63,430.3,12854,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142856,148,2,39,39,50,0,0,0,11,167.81,-27.93,435.134,3259,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134599,148,2,39,39,50,0,0,0,11,139.95,-23.38,451.607,32092,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134600,148,2,39,39,50,0,0,0,11,126.95,-21.01,449.264,509,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148804,148,2,54,57,60,0,0,0,11,149.18,-24.89,449.428,23124,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134601,148,2,39,39,50,0,0,0,11,141.27,-23.51,456.2,6486,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136441,148,2,39,39,50,0,0,0,11,174.96,-25.14,457.993,64022,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134595,148,2,54,57,60,0,0,0,11,176.45,-21.8,465.884,32622,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134596,148,2,39,39,50,0,0,0,11,170.65,-22.15,466.46,13457,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134602,148,2,39,39,50,0,0,0,11,168.04,-22.48,493.614,57038,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134622,148,2,54,57,60,0,0,0,11,137.74,-21.49,494.385,32407,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134613,148,2,39,39,50,0,0,0,11,146.72,-22.25,511.741,51274,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074074454,148,2,39,39,50,0,0,0,11,145.73,-21.8,518.855,48284,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151080,148,2,54,57,60,0,0,0,8,279.1,-31.48,391.257,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151078,148,2,54,57,60,0,3,0,8,283.67,-31.62,394.053,49158,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150931,148,2,54,57,60,0,0,0,8,300.4,-32.29,392.4,27278,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150932,148,2,54,57,60,0,0,0,8,301.81,-32.22,396.405,65511,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150686,148,2,39,39,50,0,0,0,11,303.99,-31.81,405.383,41873,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150687,148,2,39,39,50,0,0,0,11,305.82,-31.88,403.306,29926,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150695,148,2,13,12,39,0,0,0,11,303.97,-31.81,410.238,51183,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150933,148,2,54,57,60,0,0,0,8,332.05,-31.65,409.039,58246,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148663,148,2,39,39,50,0,0,0,11,329.18,-31.62,409.418,31902,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148664,148,2,39,39,50,0,0,0,11,331.12,-31.58,405.524,5299,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148673,148,2,13,12,39,0,0,0,11,338.99,-31.69,404.985,46879,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142044,148,2,39,39,50,0,0,0,11,340.37,-30.57,441.657,27785,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151081,148,2,54,57,60,0,0,0,8,276.01,-31.4,394.904,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149562,148,2,13,12,39,0,0,0,11,360.28,-31.84,437.796,41994,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149421,148,2,13,12,39,0,0,0,11,378,-31.87,439.175,17244,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149838,148,2,13,12,39,0,0,0,11,371.2,-32.09,451.2,52787,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148047,148,2,13,12,39,0,0,0,11,372.48,-32.54,413.628,50247,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151083,148,2,54,57,60,0,0,0,8,276.93,-30.95,396.593,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151091,148,2,54,57,60,0,0,0,8,280.01,-32.01,389.745,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151096,148,2,54,57,60,0,0,0,8,276.72,-31.51,393.191,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151097,148,2,54,57,60,0,0,0,10,85.21,-24.66,238.355,63276,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151098,148,2,22,22,48,0,0,0,10,91.19,-24.11,238.216,32425,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151105,148,2,21,21,63,0,0,0,9,209.8,23.46,211.348,14536,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151060,148,2,10,10,21,0,0,0,12,-27.12,-27.56,280.767,31425,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151014,148,2,56,59,385,0,0,0,9,103.67,-7.05,72.6842,14317,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151108,148,2,21,21,63,0,0,0,9,207.9,23.87,208.768,37221,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151109,148,2,21,21,63,0,0,0,9,211.47,23.45,169.805,14536,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149003,148,2,41,41,59,0,0,0,8,241.58,24.49,180.793,47828,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139166,148,2,56,59,385,0,0,0,6,251.43,-8.01,23.3881,63562,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149516,148,2,41,41,59,0,0,0,8,275.05,23.49,192.944,53154,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149526,148,2,41,41,59,0,0,0,8,272.36,24.6,196.711,2158,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149374,148,2,128,1446,104,0,0,0,17,272.69,-2.29,60.9999,61615,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148611,148,2,56,59,385,0,0,0,6,234.19,-11.69,-3.70519,10523,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149123,148,2,43,43,36,0,0,0,7,286.45,-6.67,27.5644,35921,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150974,148,2,21,21,63,0,0,0,9,288.47,22.19,188.731,50892,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150976,148,2,21,21,63,0,0,0,9,289.81,18.11,161.775,24340,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135897,148,2,43,43,36,0,0,0,7,286.88,-4.99,45.1031,5429,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150773,148,2,41,41,59,0,0,0,8,286.09,20.36,178.218,24065,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139254,148,2,43,43,36,0,0,0,7,288.51,-4.18,51.6561,15192,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149496,148,2,41,41,59,0,0,0,8,287.8,18.11,159,36781,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151104,148,2,21,21,63,0,0,0,9,286.07,21.83,187.622,9234,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149356,148,2,196,160,152,0,0,0,6,231.38,-11.55,-15.1254,31367,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151110,148,2,43,43,36,0,0,0,9,104.2,-7,47.5623,45670,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150987,148,2,21,21,63,0,0,0,9,298.74,16.97,162.87,59280,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150039,148,2,43,43,36,0,0,0,5,212.54,-9.41,-37.8066,35275,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150042,148,2,56,59,385,0,0,0,5,215.77,-9.16,-42.3956,14054,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150048,148,2,56,59,385,0,0,0,5,214.51,-9.59,-38.1401,16658,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149910,148,2,43,43,36,0,0,0,5,223.89,-8.27,-55.8428,2147,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149540,148,2,196,160,152,0,0,0,6,246.54,-11.49,-56.9791,14244,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150609,148,2,43,43,36,0,0,0,5,185.12,-7.71,-55.0616,24519,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149914,148,2,43,43,36,0,0,0,5,222.09,-7.35,-62.3129,29503,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141021,148,2,56,59,385,0,0,0,6,262.26,-10.77,-62.9685,12419,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149788,148,2,43,43,36,0,0,0,5,227.35,-7.3,-65.0394,19522,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150007,148,2,43,43,36,0,0,0,5,198.36,-7.36,-69.5077,16746,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150008,148,2,43,43,36,0,0,0,5,202.16,-6.82,-74.3313,6388,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149522,148,2,196,160,152,0,0,0,6,248.12,-11.69,-70.3363,4087,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151095,148,2,518,485,0,0,4,0,3,144.13,-7,-76.6385,11765,0,1,2147483647,0,'\02G7\0\0\0G\0\0\0\0\0\0','\0\0 \0\0 \0d\0 \0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115919,148,2,43,43,36,0,0,0,7,307.44,-1.53,62.0021,6048,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144496,148,2,43,43,36,0,0,0,7,304.95,2.87,96.1268,6536,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146693,148,2,32,32,35,0,0,0,6,286.56,-5.84,-80.2775,12564,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149515,148,2,196,160,152,0,0,0,6,261.28,-11.69,-77.8985,57468,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140116,148,2,49,49,57,0,0,0,3,181.74,-4.25,-82.8547,37539,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144491,148,2,43,43,36,0,0,0,7,308.99,2.43,94.2244,23919,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149128,148,2,43,43,36,0,0,0,7,317.11,-4.88,16.6463,45312,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147698,148,2,32,32,35,0,0,0,6,313.74,-5.16,-78.3729,43724,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146697,148,2,32,32,35,0,0,0,6,311.9,-5.35,-74.0328,7411,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149358,148,2,56,59,385,0,0,0,6,263.33,-11.6,-88.116,50485,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149506,148,2,56,59,385,0,0,0,6,258.5,-11.31,-96.6034,1143,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074131677,148,2,47,47,28,0,0,0,4,175.01,-1.16,-97.9002,33234,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149503,148,2,196,160,152,0,0,0,6,253.09,-11.12,-112.539,1940,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146989,148,2,43,43,36,0,0,0,5,293.71,-4.93,-118.83,63385,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149349,148,2,43,43,36,0,0,0,5,268.21,-9.06,-117.266,19595,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144556,148,2,43,43,36,0,0,0,5,294.99,-6.32,-123.16,42501,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149828,148,2,49,49,57,0,0,0,4,229.47,-8.18,-125.359,37181,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148376,148,2,49,49,57,0,0,0,3,162.53,-0.27,-136.641,11440,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149115,148,2,47,47,28,0,0,0,3,167.36,0.08,-144.362,19059,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149122,148,2,47,47,28,0,0,0,3,166.95,0.21,-145.38,28353,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148383,148,2,47,47,28,0,0,0,3,175.43,-1.09,-140.791,15518,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150703,148,2,47,47,28,0,0,0,3,147.39,2.22,-162.646,26451,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150713,148,2,128,1446,104,0,0,0,12,165.41,4.13,-166.911,40028,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148427,148,2,47,47,28,0,0,0,3,132.51,3.63,-177.641,34347,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149293,148,2,47,47,28,0,0,0,3,127.42,4.19,-180.303,2349,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148155,148,2,49,49,57,0,0,0,3,128.06,4.6,-181.178,29025,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148204,148,2,47,47,28,0,0,0,3,130.13,4.48,-182.184,25415,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150991,148,2,10,10,21,0,0,0,10,-58.67,-3.66,-118.076,59386,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149269,148,2,37,37,25,0,0,0,1,118.87,18.41,-246.077,25648,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149271,148,2,49,49,57,0,0,0,1,108.88,16.28,-249.01,7621,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148029,148,2,37,37,25,0,0,0,1,112.49,17.23,-244.165,36491,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150706,148,2,49,49,57,0,0,0,3,167.43,3.54,-165.333,12607,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134828,148,2,47,47,28,0,0,0,3,191.79,-1.61,-158.379,18181,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074134832,148,2,47,47,28,0,0,0,4,197.58,-1.2,-167.313,31922,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074096322,148,2,47,47,28,0,0,0,4,197.16,0.11,-173.235,10737,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151126,148,2,43,43,36,0,0,0,9,104.2,-7,47.5623,45670,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148362,148,2,47,47,28,0,0,0,4,223.39,-6.81,-140.994,32305,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148321,148,2,47,47,28,0,0,0,4,220.58,-6.33,-144.98,4707,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148346,148,2,49,49,57,0,0,0,4,219.96,-5.84,-156.213,9628,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074105979,148,2,47,47,28,0,0,0,4,207.38,-1.98,-171.837,1512,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150894,148,2,196,160,152,0,0,0,6,253.32,-11.51,-144.041,49639,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150879,148,2,196,160,152,0,0,0,6,258.3,-11.25,-158.599,53785,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148843,148,2,56,59,385,0,0,0,6,262.58,-10.51,-152.62,45403,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144553,148,2,43,43,36,0,0,0,5,279.84,-6.96,-153.643,10138,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074143221,148,2,56,59,385,0,0,0,5,276.74,-8.07,-139.114,33772,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074146987,148,2,43,43,36,0,0,0,5,284.59,-6.43,-148.065,28788,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148499,148,2,43,43,36,0,0,0,5,288.99,-4.46,-170.336,47899,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149671,148,2,47,47,28,0,0,0,4,226.93,-4.04,-178.151,18302,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136789,148,2,49,49,57,0,0,0,4,201.6,-0.9,-173.25,63590,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140702,148,2,43,43,36,0,0,0,5,300.5,-3.1,-181.859,24716,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148518,148,2,49,49,57,0,0,0,4,243.17,-1.77,-187.475,53121,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148523,148,2,196,160,152,0,0,0,6,250.61,-10.41,-183.525,19826,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148525,148,2,56,59,385,0,0,0,6,265,-10.67,-183.888,28891,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148850,148,2,196,160,152,0,0,0,6,261.13,-11.25,-182.555,3484,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144568,148,2,47,47,28,0,0,0,4,245.61,0.01,-192.048,47879,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141251,148,2,56,59,385,0,0,0,5,286.89,-4.76,-196.211,23006,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074090333,148,2,47,47,28,0,0,0,4,237.38,-0.45,-192.129,20420,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140707,148,2,43,43,36,0,0,0,5,307.1,-2.52,-193.159,62732,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140705,148,2,43,43,36,0,0,0,5,302.92,-3.22,-190.008,41327,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144577,148,2,47,47,28,0,0,0,4,262.8,1.9,-221.441,62198,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144580,148,2,49,49,57,0,0,0,4,266.23,1.84,-226.743,59160,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144588,148,2,47,47,28,0,0,0,4,254.16,1.94,-243.051,9054,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148176,148,2,49,49,57,0,0,0,1,90.98,14.15,-257.256,49081,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148184,148,2,49,49,57,0,0,0,1,106.15,15.04,-256.647,52769,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150908,148,2,37,37,25,0,0,0,1,119.04,17.57,-264.335,18436,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150911,148,2,49,49,57,0,0,0,1,118.43,17.49,-261.216,4127,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148290,148,2,37,37,25,0,0,0,1,108.81,16.19,-267.07,37839,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1073742864,148,2,453,582,0,0,0,0,35,125.7,25.38,-311.621,32767,0,4,2147483647,0,'\02\0\0\0\022\0\0\0\0','\0 \0\0\"\0 \0@\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142857,148,2,47,47,28,0,0,0,4,264.53,2.14,-242.111,48415,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140713,148,2,49,49,57,0,0,0,4,273.96,-0.09,-252.824,26175,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149524,148,2,128,1446,104,0,0,0,12,291.02,-0.63,-281.778,45431,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104830,148,2,47,47,28,0,0,0,4,305.19,0.48,-264.538,62204,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144221,148,2,32,32,35,0,0,0,6,317.61,-4.96,-109.749,5523,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151140,148,2,43,43,36,0,0,0,9,103.73,-6.6,45.0434,1931,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147852,148,2,41,41,59,0,0,0,8,316.59,8.94,129.09,40207,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149126,148,2,43,43,36,0,0,0,7,318.27,-4.29,23.0588,42374,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147853,148,2,43,43,36,0,0,0,7,322.78,2.78,94.0339,20755,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149485,148,2,41,41,59,0,0,0,8,325.8,9.38,128.876,20022,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150055,148,2,41,41,59,0,0,0,8,322.27,10.24,135.626,65061,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150172,148,2,32,32,35,0,0,0,6,334.15,-2.01,6.66452,63501,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135566,148,2,43,43,36,0,0,0,7,334.25,2.53,69.0643,10995,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074139160,148,2,43,43,36,0,0,0,7,340.76,-0.16,30.8679,37990,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135564,148,2,43,43,36,0,0,0,7,341.74,4.2,73.2116,33485,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150346,148,2,32,32,35,0,0,0,6,336.86,-3.85,-30.407,62869,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074135561,148,2,43,43,36,0,0,0,7,346.18,6.51,84.2166,15692,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148671,148,2,43,43,36,0,0,0,7,344.28,2.77,49.071,12907,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149250,148,2,43,43,36,0,0,0,7,344.15,0.34,31.3496,23766,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150348,148,2,32,32,35,0,0,0,6,348.12,-2.65,-35.5355,48933,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150354,148,2,32,32,35,0,0,0,6,346.72,-3.52,-42.227,50554,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151071,148,2,21,21,63,0,0,0,9,317.61,13.99,155.547,13388,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136038,148,2,43,43,36,0,0,0,7,355.1,3.64,46.5134,50058,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074136047,148,2,43,43,36,0,0,0,7,354.9,5.38,60.3438,44502,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148662,148,2,43,43,36,0,0,0,7,371.05,6.75,51.6504,18008,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148666,148,2,43,43,36,0,0,0,7,373.13,7.32,53.1833,60140,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150358,148,2,32,32,35,0,0,0,6,371.05,0.77,-31.6182,36006,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150361,148,2,32,32,35,0,0,0,6,374.17,2.27,-26.097,54742,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151135,148,2,195,14,46,0,0,0,8,374.6,12.17,82.5845,60196,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151137,148,2,195,14,46,0,0,0,8,378.66,12.13,76.9302,10379,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149239,148,2,128,1446,104,0,0,0,17,400.51,20.44,87.9504,59910,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151138,148,2,195,14,46,0,0,0,8,399.99,21.81,106.646,47432,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150290,148,2,195,14,46,0,0,0,8,410.11,18.62,60.6525,37948,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150227,148,2,195,14,46,0,0,0,8,417.89,26.83,102.206,6328,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150314,148,2,195,14,46,0,0,0,8,414.33,27.28,124.921,38769,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151128,148,2,780,952,1,0,3,0,8,418.81,26,91.9442,12538,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151141,148,2,542,606,44,0,3,0,8,413.35,24.17,90.6686,46264,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150300,148,2,195,14,46,0,0,0,8,422.2,25.92,83.9154,37852,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150311,148,2,195,14,46,0,0,0,8,424.01,30.65,126.412,13046,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151144,148,2,542,606,44,0,0,0,8,428.37,29.95,101.794,22326,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151146,148,2,542,606,44,0,3,0,8,429.06,29.24,93.5921,31117,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151149,148,2,542,606,44,0,0,0,8,396.54,20.73,106.111,32767,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151136,148,2,445,486,385,0,0,0,8,124.6,-11.24,132.555,43241,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151153,148,2,542,606,44,0,3,0,8,423.65,28.14,97.7343,41723,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150049,148,2,32,32,35,0,0,0,8,399.49,3.18,-44.562,34560,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150059,148,2,32,32,35,0,0,0,8,390.46,1.15,-64.5787,56785,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144927,148,2,32,32,35,0,0,0,6,343.13,-4.55,-83.0447,61761,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144525,148,2,43,43,36,0,0,0,5,329.99,-3.17,-150.777,9115,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074114391,148,2,43,43,36,0,0,0,5,313.75,-1.33,-172.819,15593,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144527,148,2,43,43,36,0,0,0,5,332.17,-2.22,-157.893,49378,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074083418,148,2,49,49,57,0,0,0,4,331.75,1.56,-250.457,43808,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074083433,148,2,49,49,57,0,0,0,4,332.7,0.6,-264.145,10324,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074116582,148,2,43,43,36,0,0,0,5,331.55,-0.25,-184.693,45210,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074116591,148,2,43,43,36,0,0,0,5,332.67,0.72,-198.096,8712,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140749,148,2,47,47,28,0,0,0,4,329.25,0.67,-280.902,32381,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074083419,148,2,49,49,57,0,0,0,4,336.77,0.96,-256.371,19425,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074116575,148,2,43,43,36,0,0,0,5,343.51,-0.52,-175.668,36359,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074116580,148,2,43,43,36,0,0,0,5,342.44,-0.36,-176.233,62049,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140732,148,2,47,47,28,0,0,0,4,333.34,0.36,-288.87,45380,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140733,148,2,47,47,28,0,0,0,4,325.05,-0.54,-288.381,48837,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144586,148,2,32,32,35,0,0,0,6,350.42,-2.35,-133.745,62668,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074128626,148,2,49,49,57,0,0,0,4,287.43,0.44,-298.134,23783,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074129634,148,2,47,47,28,0,0,0,4,297.34,-0.89,-308.181,13315,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074129630,148,2,47,47,28,0,0,0,4,298.6,-0.9,-308.848,57126,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140721,148,2,49,49,57,0,0,0,4,322.07,-1.01,-317.194,46347,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140723,148,2,49,49,57,0,0,0,4,322.93,-0.98,-316.957,13470,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104926,148,2,47,47,28,0,0,0,4,279.3,0.58,-315.438,49484,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074140934,148,2,49,49,57,0,0,0,4,348.09,0.57,-325.052,42576,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074128699,148,2,49,49,57,0,0,0,4,269.4,0.17,-322.935,46289,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074115347,148,2,43,43,36,0,0,0,5,360.73,-1.96,-176.617,13756,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141258,148,2,32,32,35,0,0,0,6,362,-1.48,-147.225,35183,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144592,148,2,32,32,35,0,0,0,6,362.53,0.93,-125.475,15963,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074128705,148,2,49,49,57,0,0,0,4,265.21,0.01,-331.233,4520,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074104927,148,2,49,49,57,0,0,0,4,273.06,-0.41,-329.995,14575,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074009939,148,2,47,47,28,0,0,0,4,300.02,-1.03,-347.773,28951,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074149504,148,2,128,1446,104,0,0,0,12,331.56,-0.81,-346.03,3971,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151182,148,2,206,53,97,0,0,0,7,217.81,-9.89,-37.7062,19072,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151184,148,2,201,41,59,0,3,0,7,180.54,-8.29,-15.3134,21625,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151183,148,2,206,53,97,0,0,0,7,224.6,-11.52,-10.0272,27986,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151185,148,2,206,53,97,0,2,0,7,161.14,-8.1,-6.28527,17327,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151189,148,2,780,952,1,0,3,0,8,156.99,-7.89,-4.54815,3504,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151186,148,2,200,49,57,0,0,0,7,160.74,-8.06,-7.72929,6411,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151191,148,2,206,53,97,0,0,0,7,167.45,-7.77,1.8385,36996,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150065,148,2,32,32,35,0,0,0,8,377.93,1.98,-85.4915,44049,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074142775,148,2,32,32,35,0,0,0,8,392.9,4.22,-86.6978,24145,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074144598,148,2,32,32,35,0,0,0,8,387.15,3.93,-97.2133,31348,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141294,148,2,32,32,35,0,0,0,8,403.69,5.49,-104.92,28199,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074148263,148,2,32,32,35,0,0,0,8,418.36,6.44,-70.2381,8832,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074147286,148,2,32,32,35,0,0,0,8,413.54,4.48,-38.1386,25512,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150392,148,2,32,32,35,0,0,0,8,418.76,6.72,-59.9881,45754,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150384,148,2,32,32,35,0,0,0,8,421.58,5.65,-34.6228,42272,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150399,148,2,32,32,35,0,0,0,8,424.05,6.59,-42.9658,4849,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150306,148,2,195,14,46,0,0,0,8,437.58,32.75,101.809,45106,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150302,148,2,195,14,46,0,0,0,8,439.73,32.37,94.4745,63269,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141334,148,2,32,32,35,0,0,0,8,412.63,5.46,-108.978,63906,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074141328,148,2,32,32,35,0,0,0,8,413.46,5.18,-114.566,49736,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151197,148,2,1328,1212,0,0,4,0,6,306.05,-5.84,-50.6831,47238,0,1,2147483647,0,'\026\0\0\06\0\0\0\0K\02\0\0\0','\0\\0\0\\0 \0d\0\0g\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150430,148,2,120,142,97,0,0,0,9,442.2,11.14,-112.608,63327,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150577,148,2,120,142,97,0,0,0,9,453.64,14.17,-90.2773,24399,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150482,148,2,120,142,97,0,0,0,9,476.26,24.15,-121.256,46629,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150579,148,2,120,142,97,0,0,0,9,480.49,23.93,-92.7206,15898,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150959,148,2,120,142,97,0,0,0,9,485.86,25.59,-96.2301,53452,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150450,148,2,120,142,97,0,0,0,9,451.95,25.61,-36.4714,12459,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150472,148,2,120,142,97,0,0,0,9,460.64,25.35,-41.3979,2660,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150643,148,2,120,142,97,0,0,0,9,468.17,26.66,-32.8979,39469,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150426,148,2,120,142,97,0,0,0,9,453.82,34.41,-9.01859,49010,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150641,148,2,120,142,97,0,0,0,9,480.77,41.5,-9.54569,30842,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150431,148,2,120,142,97,0,0,0,9,448.1,32.05,-8.84877,12718,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150605,148,2,120,142,97,0,0,0,9,480.67,31.27,-49.2197,14547,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150964,148,2,120,142,97,0,0,0,9,481.02,41.49,-10.6275,17636,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151208,148,2,49,49,57,0,0,0,4,380.23,3.88,-1.45476,8242,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151204,148,2,49,49,57,0,0,0,4,393.86,6.45,9.21236,41392,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151205,148,2,49,49,57,0,0,0,4,361.71,2.86,25.1846,12510,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151207,148,2,49,49,57,0,0,0,4,385.42,5.82,23.2229,40577,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151206,148,2,49,49,57,0,0,0,4,369.12,8.37,59.382,35369,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151220,148,2,32,32,35,0,0,0,6,351,-2.12,-128.3,33857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150958,148,2,120,142,97,0,0,0,9,483.99,31.42,-51.695,26993,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150515,148,2,120,142,97,0,0,0,9,484.29,32.1,-40.2311,25024,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150961,148,2,120,142,97,0,0,0,9,501.74,43.09,-27.8585,2815,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151002,148,2,120,142,97,0,0,0,9,511.96,43.87,-41.3549,38300,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151000,148,2,120,142,97,0,0,0,9,508.12,43.53,-43.4722,49556,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074150601,148,2,120,142,97,0,0,0,9,500.2,29.08,-83.8012,41475,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151238,148,2,32,32,35,0,0,0,6,359.3,0.13,-125.91,14545,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151248,148,2,32,32,35,0,0,0,8,386.67,4.34,-103.087,33857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151258,148,2,32,32,35,0,0,0,8,394,3.56,-87.1443,33856,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151264,148,2,32,32,35,0,0,0,8,406.31,5.57,-103.38,33857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151269,148,2,32,32,35,0,0,0,8,412.28,5.64,-106.424,33856,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151273,148,2,32,32,35,0,0,0,8,409.01,5.21,-112.078,33857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151275,148,2,49,49,57,0,0,0,4,389.86,4.66,8.58016,10307,0,2,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +INSERT INTO `battlenpc` VALUES (1074151287,148,2,32,32,35,0,0,0,8,417.52,5.76,-68.2326,33857,0,1,0,0,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0','\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'); +/*!40000 ALTER TABLE `battlenpc` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:07 diff --git a/sql/charabase.sql b/sql/charabase.sql new file mode 100644 index 00000000..3bd44d10 --- /dev/null +++ b/sql/charabase.sql @@ -0,0 +1,101 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charabase` +-- + +DROP TABLE IF EXISTS `charabase`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charabase` ( + `AccountId` int(11) NOT NULL, + `Hp` int(10) DEFAULT NULL, + `Mp` int(10) DEFAULT NULL, + `Tp` int(10) DEFAULT NULL, + `Gp` int(10) DEFAULT NULL, + `Mode` int(3) DEFAULT '0', + `Mount` int(3) DEFAULT NULL, + `Voice` int(3) DEFAULT NULL, + `Customize` binary(26) DEFAULT NULL, + `ModelMainWeapon` int(20) DEFAULT NULL, + `ModelSubWeapon` int(20) DEFAULT NULL, + `ModelSystemWeapon` int(20) DEFAULT NULL, + `ModelEquip` binary(40) DEFAULT NULL, + `EmoteModeType` int(3) DEFAULT NULL, + `CharacterId` int(20) NOT NULL DEFAULT '0', + `CharacterType` int(5) DEFAULT NULL, + `Name` varchar(32) DEFAULT NULL, + `FirstLogin` int(20) DEFAULT NULL, + `Language` int(10) DEFAULT NULL, + `IsNewGame` int(3) DEFAULT NULL, + `Invisible` int(3) DEFAULT NULL, + `InstantiationTime` int(20) DEFAULT NULL, + `PrimaryTerritoryType` int(5) DEFAULT NULL, + `PrimaryTerritoryId` int(20) DEFAULT NULL, + `Pos_0_0` float DEFAULT NULL, + `Pos_0_1` float DEFAULT NULL, + `Pos_0_2` float DEFAULT NULL, + `Pos_0_3` float DEFAULT NULL, + `PrimaryLayoutId` int(10) DEFAULT NULL, + `PrimaryExclusiveId` int(10) DEFAULT NULL, + `PrimaryMoveType` int(3) DEFAULT NULL, + `PrimaryContentId` int(10) DEFAULT NULL, + `SecondaryTerritoryType` int(5) DEFAULT NULL, + `SecondaryTerritoryId` int(20) DEFAULT NULL, + `Pos_1_0` float DEFAULT NULL, + `Pos_1_1` float DEFAULT NULL, + `Pos_1_2` float DEFAULT NULL, + `Pos_1_3` float DEFAULT NULL, + `SecondaryLayoutId` int(10) DEFAULT NULL, + `Effectiveness` int(3) DEFAULT NULL, + `Update` int(10) DEFAULT NULL, + `Pos_0` float DEFAULT NULL, + `Pos_1` float DEFAULT NULL, + `Pos_2` float DEFAULT NULL, + `Pos_3` float DEFAULT NULL, + `ContentId` bigint(20) DEFAULT NULL, + `IsNewAdventurer` int(1) NOT NULL DEFAULT '1', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT NULL, + PRIMARY KEY (`CharacterId`), + KEY `ContentId` (`ContentId`), + KEY `Name` (`Name`), + KEY `AccountId` (`AccountId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charabase` +-- + +LOCK TABLES `charabase` WRITE; +/*!40000 ALTER TABLE `charabase` DISABLE KEYS */; +/*!40000 ALTER TABLE `charabase` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:07 diff --git a/sql/characlass.sql b/sql/characlass.sql new file mode 100644 index 00000000..7c092871 --- /dev/null +++ b/sql/characlass.sql @@ -0,0 +1,100 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `characlass` +-- + +DROP TABLE IF EXISTS `characlass`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `characlass` ( + `Lv_0` int(5) DEFAULT '0', + `Exp_0` int(10) DEFAULT '0', + `Lv_1` int(5) DEFAULT '0', + `Exp_1` int(10) DEFAULT '0', + `Lv_2` int(5) DEFAULT '0', + `Exp_2` int(10) DEFAULT '0', + `Lv_3` int(5) DEFAULT '0', + `Exp_3` int(10) DEFAULT '0', + `Lv_4` int(5) DEFAULT '0', + `Exp_4` int(10) DEFAULT '0', + `Lv_5` int(5) DEFAULT '0', + `Exp_5` int(10) DEFAULT '0', + `Lv_6` int(5) DEFAULT '0', + `Exp_6` int(10) DEFAULT '0', + `Lv_7` int(5) DEFAULT '0', + `Exp_7` int(10) DEFAULT '0', + `Lv_8` int(5) DEFAULT '0', + `Exp_8` int(10) DEFAULT '0', + `Lv_9` int(5) DEFAULT '0', + `Exp_9` int(10) DEFAULT '0', + `Lv_10` int(5) DEFAULT '0', + `Exp_10` int(10) DEFAULT '0', + `Lv_11` int(5) DEFAULT '0', + `Exp_11` int(10) DEFAULT '0', + `Lv_12` int(5) DEFAULT '0', + `Exp_12` int(10) DEFAULT '0', + `Lv_13` int(5) DEFAULT '0', + `Exp_13` int(10) DEFAULT '0', + `Lv_14` int(5) DEFAULT '0', + `Exp_14` int(10) DEFAULT '0', + `Lv_15` int(5) DEFAULT '0', + `Exp_15` int(10) DEFAULT '0', + `Lv_16` int(5) DEFAULT '0', + `Exp_16` int(10) DEFAULT '0', + `Lv_17` int(5) DEFAULT '0', + `Exp_17` int(10) DEFAULT '0', + `Lv_18` int(5) DEFAULT '0', + `Exp_18` int(10) DEFAULT '0', + `Lv_19` int(5) NOT NULL DEFAULT '0', + `Exp_19` int(10) NOT NULL DEFAULT '0', + `Lv_20` int(5) NOT NULL DEFAULT '0', + `Exp_20` int(10) NOT NULL DEFAULT '0', + `Lv_21` int(5) NOT NULL DEFAULT '0', + `Exp_21` int(10) NOT NULL DEFAULT '0', + `Lv_22` int(5) NOT NULL DEFAULT '0', + `Exp_22` int(10) NOT NULL DEFAULT '0', + `Lv_23` int(5) DEFAULT '0', + `Exp_23` int(10) DEFAULT '0', + `CharacterId` int(20) NOT NULL DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT NULL, + PRIMARY KEY (`CharacterId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `characlass` +-- + +LOCK TABLES `characlass` WRITE; +/*!40000 ALTER TABLE `characlass` DISABLE KEYS */; +/*!40000 ALTER TABLE `characlass` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:12 diff --git a/sql/charadetail.sql b/sql/charadetail.sql new file mode 100644 index 00000000..c8430552 --- /dev/null +++ b/sql/charadetail.sql @@ -0,0 +1,115 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charadetail` +-- + +DROP TABLE IF EXISTS `charadetail`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +CREATE TABLE charadetail ( + `GuardianDeity` int(3) DEFAULT NULL, + `BirthDay` int(3) DEFAULT NULL, + `BirthMonth` int(3) NOT NULL, + `Class` int(3) DEFAULT NULL, + `MakeValid` int(3) DEFAULT NULL, + `RetainerId` int(20) DEFAULT NULL, + `RetainerName` varchar(32) DEFAULT NULL, + `CreateUnixTime` int(20) DEFAULT NULL, + `IsActive` int(3) DEFAULT NULL, + `IsRename` int(3) DEFAULT NULL, + `Status` int(3) DEFAULT NULL, + `Platform` varchar(32) DEFAULT NULL, + `TotalPlayTime` int(10) DEFAULT '0', + `TotalPlayTimeSecond` float DEFAULT NULL, + `FirstClass` int(3) DEFAULT NULL, + `HomePoint` int(3) DEFAULT NULL, + `FavoritePoint` binary(3) DEFAULT NULL, + `RestPoint` int(10) DEFAULT NULL, + `RentalTimer` float DEFAULT NULL, + `StartTown` int(3) DEFAULT NULL, + `ActiveTitle` int(5) DEFAULT NULL, + `TitleList` binary(32) DEFAULT NULL, + `Achievement` binary(16) DEFAULT NULL, + `Aetheryte` binary(12) DEFAULT NULL, + `HowTo` binary(32) DEFAULT NULL, + `Minions` binary(32) DEFAULT NULL, + `Mounts` binary(11) DEFAULT NULL, + `EquippedMannequin` int(5) DEFAULT NULL, + `ConfigFlags` smallint(5) NOT NULL DEFAULT '0', + `GatheringHistoryPointId` binary(8) DEFAULT NULL, + `GatheringDivisionOpenFla` binary(40) DEFAULT NULL, + `GatheringItemGetFlags` binary(60) DEFAULT NULL, + `RecipeDivisionOpenFlags` binary(80) DEFAULT NULL, + `RecipeCreateFlags` binary(100) DEFAULT NULL, + `QuestCompleteFlags` binary(200) DEFAULT NULL, + `LeveCompleteFlags` binary(200) DEFAULT NULL, + `OpeningSequence` int(3) DEFAULT NULL, + `QuestTracking` binary(10) DEFAULT NULL, + `LeveTicketNum` int(3) DEFAULT NULL, + `LeveTicketLastGetTime` int(10) DEFAULT NULL, + `GuildleveAssignmentSeed` int(5) DEFAULT NULL, + `GuildleveAssignmentCount` int(3) DEFAULT NULL, + `GuildleveFactionCreditBr` int(5) DEFAULT NULL, + `GuildleveFactionCreditAz` int(5) DEFAULT NULL, + `GuildleveFactionCreditHo` int(5) DEFAULT NULL, + `GrandCompany` int(3) DEFAULT NULL, + `GrandCompanyRank` binary(3) DEFAULT NULL, + `Discovery` blob, + `ContentRetryTime` blob, + `ContentJoinTime` int(10) DEFAULT NULL, + `ContentClearFlag` blob, + `TownWarpFstFlags` binary(2) DEFAULT NULL, + `PathId` int(10) DEFAULT NULL, + `StepIndex` int(5) DEFAULT NULL, + `ChocoboTaxiStandFlags` binary(8) DEFAULT NULL, + `unlocks` binary(64) DEFAULT NULL, + `CharacterId` int(20) NOT NULL DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +ALTER TABLE charadetail + ADD PRIMARY KEY (`CharacterId`); + +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charadetail` +-- + +LOCK TABLES `charadetail` WRITE; +/*!40000 ALTER TABLE `charadetail` DISABLE KEYS */; +/*!40000 ALTER TABLE `charadetail` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:12 diff --git a/sql/charaglobalitem.sql b/sql/charaglobalitem.sql new file mode 100644 index 00000000..bca3438b --- /dev/null +++ b/sql/charaglobalitem.sql @@ -0,0 +1,77 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charaglobalitem` +-- + +DROP TABLE IF EXISTS `charaglobalitem`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charaglobalitem` ( + `CharacterId` int(20) NOT NULL DEFAULT '0', + `itemId` int(20) NOT NULL DEFAULT '0', + `storageId` int(5) NOT NULL DEFAULT '0', + `containerIndex` int(5) NOT NULL DEFAULT '0', + `stack` int(10) DEFAULT '1', + `catalogId` int(10) DEFAULT '0', + `reservedFlag` int(10) DEFAULT '0', + `signatureId` int(20) DEFAULT '0', + `flags` int(3) DEFAULT '0', + `durability` int(5) DEFAULT '30000', + `refine` int(5) DEFAULT '0', + `materia_0` int(5) DEFAULT '0', + `materia_1` int(5) DEFAULT '0', + `materia_2` int(5) DEFAULT '0', + `materia_3` int(5) DEFAULT '0', + `materia_4` int(5) DEFAULT '0', + `stain` int(3) DEFAULT '0', + `pattern` int(10) DEFAULT '0', + `buffer_0` int(3) DEFAULT '0', + `buffer_1` int(3) DEFAULT '0', + `buffer_2` int(3) DEFAULT '0', + `buffer_3` int(3) DEFAULT '0', + `buffer_4` int(3) DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`itemId`), + KEY `CharacterId` (`CharacterId`), + KEY `storageId` (`storageId`), + KEY `storageId_2` (`storageId`,`containerIndex`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charaglobalitem` +-- + +LOCK TABLES `charaglobalitem` WRITE; +/*!40000 ALTER TABLE `charaglobalitem` DISABLE KEYS */; +/*!40000 ALTER TABLE `charaglobalitem` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:12 diff --git a/sql/charainfoblacklist.sql b/sql/charainfoblacklist.sql new file mode 100644 index 00000000..c263c5fe --- /dev/null +++ b/sql/charainfoblacklist.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charainfoblacklist` +-- + +DROP TABLE IF EXISTS `charainfoblacklist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charainfoblacklist` ( + `CharacterId` int(20) NOT NULL, + `CharacterIdList` blob, + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL, + PRIMARY KEY (`CharacterId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charainfoblacklist` +-- + +LOCK TABLES `charainfoblacklist` WRITE; +/*!40000 ALTER TABLE `charainfoblacklist` DISABLE KEYS */; +/*!40000 ALTER TABLE `charainfoblacklist` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:08 diff --git a/sql/charainfofriendlist.sql b/sql/charainfofriendlist.sql new file mode 100644 index 00000000..f65c174f --- /dev/null +++ b/sql/charainfofriendlist.sql @@ -0,0 +1,54 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charainfofriendlist` +-- + +DROP TABLE IF EXISTS `charainfofriendlist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charainfofriendlist` ( + `CharacterId` int(20) NOT NULL, + `CharacterIdList` blob, + `InviteDataList` blob, + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL, + PRIMARY KEY (`CharacterId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charainfofriendlist` +-- + +LOCK TABLES `charainfofriendlist` WRITE; +/*!40000 ALTER TABLE `charainfofriendlist` DISABLE KEYS */; +/*!40000 ALTER TABLE `charainfofriendlist` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:09 diff --git a/sql/charainfolinkshell.sql b/sql/charainfolinkshell.sql new file mode 100644 index 00000000..1a608f23 --- /dev/null +++ b/sql/charainfolinkshell.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charainfolinkshell` +-- + +DROP TABLE IF EXISTS `charainfolinkshell`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charainfolinkshell` ( + `CharacterId` int(20) NOT NULL, + `LinkshellIdList` binary(64) DEFAULT NULL, + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL, + PRIMARY KEY (`CharacterId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charainfolinkshell` +-- + +LOCK TABLES `charainfolinkshell` WRITE; +/*!40000 ALTER TABLE `charainfolinkshell` DISABLE KEYS */; +/*!40000 ALTER TABLE `charainfolinkshell` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:09 diff --git a/sql/charainfosearch.sql b/sql/charainfosearch.sql new file mode 100644 index 00000000..8d88bd54 --- /dev/null +++ b/sql/charainfosearch.sql @@ -0,0 +1,54 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charainfosearch` +-- + +DROP TABLE IF EXISTS `charainfosearch`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charainfosearch` ( + `CharacterId` int(20) DEFAULT NULL, + `SelectClassId` int(3) DEFAULT '0', + `SelectRegion` int(3) DEFAULT '0', + `SearchComment` binary(193) DEFAULT "", + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charainfosearch` +-- + +LOCK TABLES `charainfosearch` WRITE; +/*!40000 ALTER TABLE `charainfosearch` DISABLE KEYS */; +/*!40000 ALTER TABLE `charainfosearch` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:09 diff --git a/sql/charaitemcrystal.sql b/sql/charaitemcrystal.sql new file mode 100644 index 00000000..e7fa731c --- /dev/null +++ b/sql/charaitemcrystal.sql @@ -0,0 +1,74 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charaitemcrystal` +-- + +DROP TABLE IF EXISTS `charaitemcrystal`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charaitemcrystal` ( + `CharacterId` int(20) DEFAULT '0', + `storageId` int(10) DEFAULT '2001', + `type` int(5) DEFAULT '0', + `idx` int(5) NOT NULL AUTO_INCREMENT, + `container_0` int(20) DEFAULT '0', + `container_1` int(20) DEFAULT '0', + `container_2` int(20) DEFAULT '0', + `container_3` int(20) DEFAULT '0', + `container_4` int(20) DEFAULT '0', + `container_5` int(20) DEFAULT '0', + `container_6` int(20) DEFAULT '0', + `container_7` int(20) DEFAULT '0', + `container_8` int(20) DEFAULT '0', + `container_9` int(20) DEFAULT '0', + `container_10` int(20) DEFAULT '0', + `container_11` int(20) DEFAULT '0', + `container_12` int(20) DEFAULT '0', + `container_13` int(20) DEFAULT '0', + `container_14` int(20) DEFAULT '0', + `container_15` int(20) DEFAULT '0', + `container_16` int(20) DEFAULT '0', + `container_17` int(20) DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`idx`), + KEY `CharacterId` (`CharacterId`) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charaitemcrystal` +-- + +LOCK TABLES `charaitemcrystal` WRITE; +/*!40000 ALTER TABLE `charaitemcrystal` DISABLE KEYS */; +/*!40000 ALTER TABLE `charaitemcrystal` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:10 diff --git a/sql/charaitemcurrency.sql b/sql/charaitemcurrency.sql new file mode 100644 index 00000000..e3d2c4cb --- /dev/null +++ b/sql/charaitemcurrency.sql @@ -0,0 +1,68 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charaitemcurrency` +-- + +DROP TABLE IF EXISTS `charaitemcurrency`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charaitemcurrency` ( + `CharacterId` int(20) NOT NULL, + `storageId` int(10) DEFAULT '2000', + `type` int(5) DEFAULT '0', + `idx` int(5) NOT NULL AUTO_INCREMENT, + `container_0` int(20) DEFAULT '0', + `container_1` int(20) DEFAULT '0', + `container_2` int(20) DEFAULT '0', + `container_3` int(20) DEFAULT '0', + `container_4` int(20) DEFAULT '0', + `container_5` int(20) DEFAULT '0', + `container_6` int(20) DEFAULT '0', + `container_7` int(20) DEFAULT '0', + `container_8` int(20) NOT NULL DEFAULT '0', + `container_9` int(20) NOT NULL DEFAULT '0', + `container_10` int(20) NOT NULL DEFAULT '0', + `container_11` int(20) NOT NULL DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`idx`), + UNIQUE KEY `CharacterId` (`CharacterId`) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charaitemcurrency` +-- + +LOCK TABLES `charaitemcurrency` WRITE; +/*!40000 ALTER TABLE `charaitemcurrency` DISABLE KEYS */; +/*!40000 ALTER TABLE `charaitemcurrency` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:11 diff --git a/sql/charaitemgearset.sql b/sql/charaitemgearset.sql new file mode 100644 index 00000000..b318d00d --- /dev/null +++ b/sql/charaitemgearset.sql @@ -0,0 +1,70 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charaitemgearset` +-- + +DROP TABLE IF EXISTS `charaitemgearset`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charaitemgearset` ( + `CharacterId` int(20) NOT NULL, + `storageId` int(10) NOT NULL, + `type` int(5) DEFAULT '0', + `idx` int(5) NOT NULL AUTO_INCREMENT, + `container_0` int(20) DEFAULT '0', + `container_1` int(20) DEFAULT '0', + `container_2` int(20) DEFAULT '0', + `container_3` int(20) DEFAULT '0', + `container_4` int(20) DEFAULT '0', + `container_5` int(20) DEFAULT '0', + `container_6` int(20) DEFAULT '0', + `container_7` int(20) DEFAULT '0', + `container_8` int(20) DEFAULT '0', + `container_9` int(20) DEFAULT '0', + `container_10` int(20) DEFAULT '0', + `container_11` int(20) DEFAULT '0', + `container_12` int(20) DEFAULT '0', + `container_13` int(20) DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`CharacterId`,`storageId`), + UNIQUE KEY `idx` (`idx`) +) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charaitemgearset` +-- + +LOCK TABLES `charaitemgearset` WRITE; +/*!40000 ALTER TABLE `charaitemgearset` DISABLE KEYS */; +/*!40000 ALTER TABLE `charaitemgearset` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:11 diff --git a/sql/charaiteminventory.sql b/sql/charaiteminventory.sql new file mode 100644 index 00000000..15b78f31 --- /dev/null +++ b/sql/charaiteminventory.sql @@ -0,0 +1,81 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `charaiteminventory` +-- + +DROP TABLE IF EXISTS `charaiteminventory`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `charaiteminventory` ( + `CharacterId` int(20) NOT NULL, + `storageId` int(10) NOT NULL, + `type` int(5) DEFAULT '0', + `idx` int(5) NOT NULL AUTO_INCREMENT, + `container_0` int(20) DEFAULT '0', + `container_1` int(20) DEFAULT '0', + `container_2` int(20) DEFAULT '0', + `container_3` int(20) DEFAULT '0', + `container_4` int(20) DEFAULT '0', + `container_5` int(20) DEFAULT '0', + `container_6` int(20) DEFAULT '0', + `container_7` int(20) DEFAULT '0', + `container_8` int(20) DEFAULT '0', + `container_9` int(20) DEFAULT '0', + `container_10` int(20) DEFAULT '0', + `container_11` int(20) DEFAULT '0', + `container_12` int(20) DEFAULT '0', + `container_13` int(20) DEFAULT '0', + `container_14` int(20) DEFAULT '0', + `container_15` int(20) DEFAULT '0', + `container_16` int(20) DEFAULT '0', + `container_17` int(20) DEFAULT '0', + `container_18` int(20) DEFAULT '0', + `container_19` int(20) DEFAULT '0', + `container_20` int(20) DEFAULT '0', + `container_21` int(20) DEFAULT '0', + `container_22` int(20) DEFAULT '0', + `container_23` int(20) DEFAULT '0', + `container_24` int(20) DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`CharacterId`,`storageId`), + UNIQUE KEY `idx` (`idx`) +) ENGINE=MyISAM AUTO_INCREMENT=161 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `charaiteminventory` +-- + +LOCK TABLES `charaiteminventory` WRITE; +/*!40000 ALTER TABLE `charaiteminventory` DISABLE KEYS */; +/*!40000 ALTER TABLE `charaiteminventory` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:10 diff --git a/sql/charaquest.sql b/sql/charaquest.sql new file mode 100644 index 00000000..3faacc74 --- /dev/null +++ b/sql/charaquest.sql @@ -0,0 +1,309 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +CREATE TABLE charaquest ( + `QuestId_0` int(5) DEFAULT '0', + `Sequence_0` int(3) DEFAULT '0', + `Flags_0` int(3) DEFAULT '0', + `Variables_0_0` int(3) DEFAULT '0', + `Variables_0_1` int(3) DEFAULT '0', + `Variables_0_2` int(3) DEFAULT '0', + `Variables_0_3` int(3) DEFAULT '0', + `Variables_0_4` int(3) DEFAULT '0', + `Variables_0_5` int(3) DEFAULT '0', + `Variables_0_6` int(3) DEFAULT '0', + `QuestId_1` int(5) DEFAULT '0', + `Sequence_1` int(3) DEFAULT '0', + `Flags_1` int(3) DEFAULT '0', + `Variables_1_0` int(3) DEFAULT '0', + `Variables_1_1` int(3) DEFAULT '0', + `Variables_1_2` int(3) DEFAULT '0', + `Variables_1_3` int(3) DEFAULT '0', + `Variables_1_4` int(3) DEFAULT '0', + `Variables_1_5` int(3) DEFAULT '0', + `Variables_1_6` int(3) DEFAULT '0', + `QuestId_2` int(5) DEFAULT '0', + `Sequence_2` int(3) DEFAULT '0', + `Flags_2` int(3) DEFAULT '0', + `Variables_2_0` int(3) DEFAULT '0', + `Variables_2_1` int(3) DEFAULT '0', + `Variables_2_2` int(3) DEFAULT '0', + `Variables_2_3` int(3) DEFAULT '0', + `Variables_2_4` int(3) DEFAULT '0', + `Variables_2_5` int(3) DEFAULT '0', + `Variables_2_6` int(3) DEFAULT '0', + `QuestId_3` int(5) DEFAULT '0', + `Sequence_3` int(3) DEFAULT '0', + `Flags_3` int(3) DEFAULT '0', + `Variables_3_0` int(3) DEFAULT '0', + `Variables_3_1` int(3) DEFAULT '0', + `Variables_3_2` int(3) DEFAULT '0', + `Variables_3_3` int(3) DEFAULT '0', + `Variables_3_4` int(3) DEFAULT '0', + `Variables_3_5` int(3) DEFAULT '0', + `Variables_3_6` int(3) DEFAULT '0', + `QuestId_4` int(5) DEFAULT '0', + `Sequence_4` int(3) DEFAULT '0', + `Flags_4` int(3) DEFAULT '0', + `Variables_4_0` int(3) DEFAULT '0', + `Variables_4_1` int(3) DEFAULT '0', + `Variables_4_2` int(3) DEFAULT '0', + `Variables_4_3` int(3) DEFAULT '0', + `Variables_4_4` int(3) DEFAULT '0', + `Variables_4_5` int(3) DEFAULT '0', + `Variables_4_6` int(3) DEFAULT '0', + `QuestId_5` int(5) DEFAULT '0', + `Sequence_5` int(3) DEFAULT '0', + `Flags_5` int(3) DEFAULT '0', + `Variables_5_0` int(3) DEFAULT '0', + `Variables_5_1` int(3) DEFAULT '0', + `Variables_5_2` int(3) DEFAULT '0', + `Variables_5_3` int(3) DEFAULT '0', + `Variables_5_4` int(3) DEFAULT '0', + `Variables_5_5` int(3) DEFAULT '0', + `Variables_5_6` int(3) DEFAULT '0', + `QuestId_6` int(5) DEFAULT '0', + `Sequence_6` int(3) DEFAULT '0', + `Flags_6` int(3) DEFAULT '0', + `Variables_6_0` int(3) DEFAULT '0', + `Variables_6_1` int(3) DEFAULT '0', + `Variables_6_2` int(3) DEFAULT '0', + `Variables_6_3` int(3) DEFAULT '0', + `Variables_6_4` int(3) DEFAULT '0', + `Variables_6_5` int(3) DEFAULT '0', + `Variables_6_6` int(3) DEFAULT '0', + `QuestId_7` int(5) DEFAULT '0', + `Sequence_7` int(3) DEFAULT '0', + `Flags_7` int(3) DEFAULT '0', + `Variables_7_0` int(3) DEFAULT '0', + `Variables_7_1` int(3) DEFAULT '0', + `Variables_7_2` int(3) DEFAULT '0', + `Variables_7_3` int(3) DEFAULT '0', + `Variables_7_4` int(3) DEFAULT '0', + `Variables_7_5` int(3) DEFAULT '0', + `Variables_7_6` int(3) DEFAULT '0', + `QuestId_8` int(5) DEFAULT '0', + `Sequence_8` int(3) DEFAULT '0', + `Flags_8` int(3) DEFAULT '0', + `Variables_8_0` int(3) DEFAULT '0', + `Variables_8_1` int(3) DEFAULT '0', + `Variables_8_2` int(3) DEFAULT '0', + `Variables_8_3` int(3) DEFAULT '0', + `Variables_8_4` int(3) DEFAULT '0', + `Variables_8_5` int(3) DEFAULT '0', + `Variables_8_6` int(3) DEFAULT '0', + `QuestId_9` int(5) DEFAULT '0', + `Sequence_9` int(3) DEFAULT '0', + `Flags_9` int(3) DEFAULT '0', + `Variables_9_0` int(3) DEFAULT '0', + `Variables_9_1` int(3) DEFAULT '0', + `Variables_9_2` int(3) DEFAULT '0', + `Variables_9_3` int(3) DEFAULT '0', + `Variables_9_4` int(3) DEFAULT '0', + `Variables_9_5` int(3) DEFAULT '0', + `Variables_9_6` int(3) DEFAULT '0', + `QuestId_10` int(5) DEFAULT '0', + `Sequence_10` int(3) DEFAULT '0', + `Flags_10` int(3) DEFAULT '0', + `Variables_10_0` int(3) DEFAULT '0', + `Variables_10_1` int(3) DEFAULT '0', + `Variables_10_2` int(3) DEFAULT '0', + `Variables_10_3` int(3) DEFAULT '0', + `Variables_10_4` int(3) DEFAULT '0', + `Variables_10_5` int(3) DEFAULT '0', + `Variables_10_6` int(3) DEFAULT '0', + `QuestId_11` int(5) DEFAULT '0', + `Sequence_11` int(3) DEFAULT '0', + `Flags_11` int(3) DEFAULT '0', + `Variables_11_0` int(3) DEFAULT '0', + `Variables_11_1` int(3) DEFAULT '0', + `Variables_11_2` int(3) DEFAULT '0', + `Variables_11_3` int(3) DEFAULT '0', + `Variables_11_4` int(3) DEFAULT '0', + `Variables_11_5` int(3) DEFAULT '0', + `Variables_11_6` int(3) DEFAULT '0', + `QuestId_12` int(5) DEFAULT '0', + `Sequence_12` int(3) DEFAULT '0', + `Flags_12` int(3) DEFAULT '0', + `Variables_12_0` int(3) DEFAULT '0', + `Variables_12_1` int(3) DEFAULT '0', + `Variables_12_2` int(3) DEFAULT '0', + `Variables_12_3` int(3) DEFAULT '0', + `Variables_12_4` int(3) DEFAULT '0', + `Variables_12_5` int(3) DEFAULT '0', + `Variables_12_6` int(3) DEFAULT '0', + `QuestId_13` int(5) DEFAULT '0', + `Sequence_13` int(3) DEFAULT '0', + `Flags_13` int(3) DEFAULT '0', + `Variables_13_0` int(3) DEFAULT '0', + `Variables_13_1` int(3) DEFAULT '0', + `Variables_13_2` int(3) DEFAULT '0', + `Variables_13_3` int(3) DEFAULT '0', + `Variables_13_4` int(3) DEFAULT '0', + `Variables_13_5` int(3) DEFAULT '0', + `Variables_13_6` int(3) DEFAULT '0', + `QuestId_14` int(5) DEFAULT '0', + `Sequence_14` int(3) DEFAULT '0', + `Flags_14` int(3) DEFAULT '0', + `Variables_14_0` int(3) DEFAULT '0', + `Variables_14_1` int(3) DEFAULT '0', + `Variables_14_2` int(3) DEFAULT '0', + `Variables_14_3` int(3) DEFAULT '0', + `Variables_14_4` int(3) DEFAULT '0', + `Variables_14_5` int(3) DEFAULT '0', + `Variables_14_6` int(3) DEFAULT '0', + `QuestId_15` int(5) DEFAULT '0', + `Sequence_15` int(3) DEFAULT '0', + `Flags_15` int(3) DEFAULT '0', + `Variables_15_0` int(3) DEFAULT '0', + `Variables_15_1` int(3) DEFAULT '0', + `Variables_15_2` int(3) DEFAULT '0', + `Variables_15_3` int(3) DEFAULT '0', + `Variables_15_4` int(3) DEFAULT '0', + `Variables_15_5` int(3) DEFAULT '0', + `Variables_15_6` int(3) DEFAULT '0', + `QuestId_16` int(5) DEFAULT '0', + `Sequence_16` int(3) DEFAULT '0', + `Flags_16` int(3) DEFAULT '0', + `Variables_16_0` int(3) DEFAULT '0', + `Variables_16_1` int(3) DEFAULT '0', + `Variables_16_2` int(3) DEFAULT '0', + `Variables_16_3` int(3) DEFAULT '0', + `Variables_16_4` int(3) DEFAULT '0', + `Variables_16_5` int(3) DEFAULT '0', + `Variables_16_6` int(3) DEFAULT '0', + `QuestId_17` int(5) DEFAULT '0', + `Sequence_17` int(3) DEFAULT '0', + `Flags_17` int(3) DEFAULT '0', + `Variables_17_0` int(3) DEFAULT '0', + `Variables_17_1` int(3) DEFAULT '0', + `Variables_17_2` int(3) DEFAULT '0', + `Variables_17_3` int(3) DEFAULT '0', + `Variables_17_4` int(3) DEFAULT '0', + `Variables_17_5` int(3) DEFAULT '0', + `Variables_17_6` int(3) DEFAULT '0', + `QuestId_18` int(5) DEFAULT '0', + `Sequence_18` int(3) DEFAULT '0', + `Flags_18` int(3) DEFAULT '0', + `Variables_18_0` int(3) DEFAULT '0', + `Variables_18_1` int(3) DEFAULT '0', + `Variables_18_2` int(3) DEFAULT '0', + `Variables_18_3` int(3) DEFAULT '0', + `Variables_18_4` int(3) DEFAULT '0', + `Variables_18_5` int(3) DEFAULT '0', + `Variables_18_6` int(3) DEFAULT '0', + `QuestId_19` int(5) DEFAULT '0', + `Sequence_19` int(3) DEFAULT '0', + `Flags_19` int(3) DEFAULT '0', + `Variables_19_0` int(3) DEFAULT '0', + `Variables_19_1` int(3) DEFAULT '0', + `Variables_19_2` int(3) DEFAULT '0', + `Variables_19_3` int(3) DEFAULT '0', + `Variables_19_4` int(3) DEFAULT '0', + `Variables_19_5` int(3) DEFAULT '0', + `Variables_19_6` int(3) DEFAULT '0', + `QuestId_20` int(5) DEFAULT '0', + `Sequence_20` int(3) DEFAULT '0', + `Flags_20` int(3) DEFAULT '0', + `Variables_20_0` int(3) DEFAULT '0', + `Variables_20_1` int(3) DEFAULT '0', + `Variables_20_2` int(3) DEFAULT '0', + `Variables_20_3` int(3) DEFAULT '0', + `Variables_20_4` int(3) DEFAULT '0', + `Variables_20_5` int(3) DEFAULT '0', + `Variables_20_6` int(3) DEFAULT '0', + `QuestId_21` int(5) DEFAULT '0', + `Sequence_21` int(3) DEFAULT '0', + `Flags_21` int(3) DEFAULT '0', + `Variables_21_0` int(3) DEFAULT '0', + `Variables_21_1` int(3) DEFAULT '0', + `Variables_21_2` int(3) DEFAULT '0', + `Variables_21_3` int(3) DEFAULT '0', + `Variables_21_4` int(3) DEFAULT '0', + `Variables_21_5` int(3) DEFAULT '0', + `Variables_21_6` int(3) DEFAULT '0', + `QuestId_22` int(5) DEFAULT '0', + `Sequence_22` int(3) DEFAULT '0', + `Flags_22` int(3) DEFAULT '0', + `Variables_22_0` int(3) DEFAULT '0', + `Variables_22_1` int(3) DEFAULT '0', + `Variables_22_2` int(3) DEFAULT '0', + `Variables_22_3` int(3) DEFAULT '0', + `Variables_22_4` int(3) DEFAULT '0', + `Variables_22_5` int(3) DEFAULT '0', + `Variables_22_6` int(3) DEFAULT '0', + `QuestId_23` int(5) DEFAULT '0', + `Sequence_23` int(3) DEFAULT '0', + `Flags_23` int(3) DEFAULT '0', + `Variables_23_0` int(3) DEFAULT '0', + `Variables_23_1` int(3) DEFAULT '0', + `Variables_23_2` int(3) DEFAULT '0', + `Variables_23_3` int(3) DEFAULT '0', + `Variables_23_4` int(3) DEFAULT '0', + `Variables_23_5` int(3) DEFAULT '0', + `Variables_23_6` int(3) DEFAULT '0', + `QuestId_24` int(5) DEFAULT '0', + `Sequence_24` int(3) DEFAULT '0', + `Flags_24` int(3) DEFAULT '0', + `Variables_24_0` int(3) DEFAULT '0', + `Variables_24_1` int(3) DEFAULT '0', + `Variables_24_2` int(3) DEFAULT '0', + `Variables_24_3` int(3) DEFAULT '0', + `Variables_24_4` int(3) DEFAULT '0', + `Variables_24_5` int(3) DEFAULT '0', + `Variables_24_6` int(3) DEFAULT '0', + `QuestId_25` int(5) DEFAULT '0', + `Sequence_25` int(3) DEFAULT '0', + `Flags_25` int(3) DEFAULT '0', + `Variables_25_0` int(3) DEFAULT '0', + `Variables_25_1` int(3) DEFAULT '0', + `Variables_25_2` int(3) DEFAULT '0', + `Variables_25_3` int(3) DEFAULT '0', + `Variables_25_4` int(3) DEFAULT '0', + `Variables_25_5` int(3) DEFAULT '0', + `Variables_25_6` int(3) DEFAULT '0', + `QuestId_26` int(5) DEFAULT '0', + `Sequence_26` int(3) DEFAULT '0', + `Flags_26` int(3) DEFAULT '0', + `Variables_26_0` int(3) DEFAULT '0', + `Variables_26_1` int(3) DEFAULT '0', + `Variables_26_2` int(3) DEFAULT '0', + `Variables_26_3` int(3) DEFAULT '0', + `Variables_26_4` int(3) DEFAULT '0', + `Variables_26_5` int(3) DEFAULT '0', + `Variables_26_6` int(3) DEFAULT '0', + `QuestId_27` int(5) DEFAULT '0', + `Sequence_27` int(3) DEFAULT '0', + `Flags_27` int(3) DEFAULT '0', + `Variables_27_0` int(3) DEFAULT '0', + `Variables_27_1` int(3) DEFAULT '0', + `Variables_27_2` int(3) DEFAULT '0', + `Variables_27_3` int(3) DEFAULT '0', + `Variables_27_4` int(3) DEFAULT '0', + `Variables_27_5` int(3) DEFAULT '0', + `Variables_27_6` int(3) DEFAULT '0', + `QuestId_28` int(5) DEFAULT '0', + `Sequence_28` int(3) DEFAULT '0', + `Flags_28` int(3) DEFAULT '0', + `Variables_28_0` int(3) DEFAULT '0', + `Variables_28_1` int(3) DEFAULT '0', + `Variables_28_2` int(3) DEFAULT '0', + `Variables_28_3` int(3) DEFAULT '0', + `Variables_28_4` int(3) DEFAULT '0', + `Variables_28_5` int(3) DEFAULT '0', + `Variables_28_6` int(3) DEFAULT '0', + `QuestId_29` int(5) DEFAULT '0', + `Sequence_29` int(3) DEFAULT '0', + `Flags_29` int(3) DEFAULT '0', + `Variables_29_0` int(3) DEFAULT '0', + `Variables_29_1` int(3) DEFAULT '0', + `Variables_29_2` int(3) DEFAULT '0', + `Variables_29_3` int(3) DEFAULT '0', + `Variables_29_4` int(3) DEFAULT '0', + `Variables_29_5` int(3) DEFAULT '0', + `Variables_29_6` int(3) DEFAULT '0', + `CharacterId` int(20) DEFAULT '0', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/sql/discoveryinfo.sql b/sql/discoveryinfo.sql new file mode 100644 index 00000000..fbb5a930 --- /dev/null +++ b/sql/discoveryinfo.sql @@ -0,0 +1,148 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `discoveryinfo` +-- + +DROP TABLE IF EXISTS `discoveryinfo`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `discoveryinfo` ( + `id` int(10) NOT NULL, + `map_id` int(3) NOT NULL, + `discover_id` int(3) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discoveryinfo` +-- + +LOCK TABLES `discoveryinfo` WRITE; +/*!40000 ALTER TABLE `discoveryinfo` DISABLE KEYS */; +INSERT INTO `discoveryinfo` VALUES (-858993460,-858993460,-858993460); +INSERT INTO `discoveryinfo` VALUES (1367775,4,2); +INSERT INTO `discoveryinfo` VALUES (1367776,4,2); +INSERT INTO `discoveryinfo` VALUES (1367777,4,2); +INSERT INTO `discoveryinfo` VALUES (1367778,4,1); +INSERT INTO `discoveryinfo` VALUES (1367779,4,2); +INSERT INTO `discoveryinfo` VALUES (1367780,4,3); +INSERT INTO `discoveryinfo` VALUES (1367785,4,3); +INSERT INTO `discoveryinfo` VALUES (1367795,4,4); +INSERT INTO `discoveryinfo` VALUES (1367798,4,1); +INSERT INTO `discoveryinfo` VALUES (1367800,4,2); +INSERT INTO `discoveryinfo` VALUES (2052662,4,7); +INSERT INTO `discoveryinfo` VALUES (2052665,4,7); +INSERT INTO `discoveryinfo` VALUES (2052668,4,8); +INSERT INTO `discoveryinfo` VALUES (2052672,4,13); +INSERT INTO `discoveryinfo` VALUES (2052682,4,9); +INSERT INTO `discoveryinfo` VALUES (2052693,4,12); +INSERT INTO `discoveryinfo` VALUES (2052695,4,12); +INSERT INTO `discoveryinfo` VALUES (2052699,4,10); +INSERT INTO `discoveryinfo` VALUES (2052703,4,11); +INSERT INTO `discoveryinfo` VALUES (2052705,4,11); +INSERT INTO `discoveryinfo` VALUES (2052710,4,6); +INSERT INTO `discoveryinfo` VALUES (2052711,4,6); +INSERT INTO `discoveryinfo` VALUES (2052712,4,6); +INSERT INTO `discoveryinfo` VALUES (2052714,4,5); +INSERT INTO `discoveryinfo` VALUES (2052715,4,5); +INSERT INTO `discoveryinfo` VALUES (2052716,4,5); +INSERT INTO `discoveryinfo` VALUES (2052721,4,5); +INSERT INTO `discoveryinfo` VALUES (2052722,4,5); +INSERT INTO `discoveryinfo` VALUES (2052730,4,11); +INSERT INTO `discoveryinfo` VALUES (2465005,4,4); +INSERT INTO `discoveryinfo` VALUES (2465008,4,6); +INSERT INTO `discoveryinfo` VALUES (2465009,4,6); +INSERT INTO `discoveryinfo` VALUES (2465010,4,2); +INSERT INTO `discoveryinfo` VALUES (2465013,4,2); +INSERT INTO `discoveryinfo` VALUES (2465018,4,13); +INSERT INTO `discoveryinfo` VALUES (2465020,4,12); +INSERT INTO `discoveryinfo` VALUES (2496289,4,5); +INSERT INTO `discoveryinfo` VALUES (2652480,4,1); +INSERT INTO `discoveryinfo` VALUES (2652487,4,1); +INSERT INTO `discoveryinfo` VALUES (2652490,4,1); +INSERT INTO `discoveryinfo` VALUES (3907879,4,1); +INSERT INTO `discoveryinfo` VALUES (3907888,4,1); +INSERT INTO `discoveryinfo` VALUES (3907899,4,2); +INSERT INTO `discoveryinfo` VALUES (3907901,4,2); +INSERT INTO `discoveryinfo` VALUES (3907902,4,2); +INSERT INTO `discoveryinfo` VALUES (3907903,4,2); +INSERT INTO `discoveryinfo` VALUES (3907908,4,2); +INSERT INTO `discoveryinfo` VALUES (3907909,4,2); +INSERT INTO `discoveryinfo` VALUES (3975004,15,1); +INSERT INTO `discoveryinfo` VALUES (3975007,15,2); +INSERT INTO `discoveryinfo` VALUES (3975010,15,3); +INSERT INTO `discoveryinfo` VALUES (3975011,15,4); +INSERT INTO `discoveryinfo` VALUES (3975013,15,5); +INSERT INTO `discoveryinfo` VALUES (3975015,15,6); +INSERT INTO `discoveryinfo` VALUES (3975016,15,7); +INSERT INTO `discoveryinfo` VALUES (4161435,15,1); +INSERT INTO `discoveryinfo` VALUES (4161439,15,3); +INSERT INTO `discoveryinfo` VALUES (4161440,15,3); +INSERT INTO `discoveryinfo` VALUES (4161442,15,3); +INSERT INTO `discoveryinfo` VALUES (4161449,15,4); +INSERT INTO `discoveryinfo` VALUES (4161454,15,6); +INSERT INTO `discoveryinfo` VALUES (4161456,15,6); +INSERT INTO `discoveryinfo` VALUES (4161457,15,6); +INSERT INTO `discoveryinfo` VALUES (4164846,18,2); +INSERT INTO `discoveryinfo` VALUES (4164858,18,1); +INSERT INTO `discoveryinfo` VALUES (4164861,18,1); +INSERT INTO `discoveryinfo` VALUES (4164900,18,3); +INSERT INTO `discoveryinfo` VALUES (4164905,18,3); +INSERT INTO `discoveryinfo` VALUES (4164911,18,4); +INSERT INTO `discoveryinfo` VALUES (4164912,18,4); +INSERT INTO `discoveryinfo` VALUES (4164914,18,4); +INSERT INTO `discoveryinfo` VALUES (4164917,18,5); +INSERT INTO `discoveryinfo` VALUES (4164923,18,6); +INSERT INTO `discoveryinfo` VALUES (4164924,18,7); +INSERT INTO `discoveryinfo` VALUES (4164926,18,10); +INSERT INTO `discoveryinfo` VALUES (4164929,18,7); +INSERT INTO `discoveryinfo` VALUES (4164931,18,8); +INSERT INTO `discoveryinfo` VALUES (4164934,18,8); +INSERT INTO `discoveryinfo` VALUES (4164937,18,9); +INSERT INTO `discoveryinfo` VALUES (4164938,18,9); +INSERT INTO `discoveryinfo` VALUES (4164940,18,10); +INSERT INTO `discoveryinfo` VALUES (4164942,18,11); +INSERT INTO `discoveryinfo` VALUES (4164944,18,11); +INSERT INTO `discoveryinfo` VALUES (4164945,18,12); +INSERT INTO `discoveryinfo` VALUES (4164947,18,13); +INSERT INTO `discoveryinfo` VALUES (4164953,18,17); +INSERT INTO `discoveryinfo` VALUES (4164955,18,18); +INSERT INTO `discoveryinfo` VALUES (4164956,18,18); +INSERT INTO `discoveryinfo` VALUES (4164957,18,18); +INSERT INTO `discoveryinfo` VALUES (4164958,18,19); +INSERT INTO `discoveryinfo` VALUES (4242609,15,1); +INSERT INTO `discoveryinfo` VALUES (4242610,15,1); +INSERT INTO `discoveryinfo` VALUES (4248537,53,17); +INSERT INTO `discoveryinfo` VALUES (4665752,18,11); +INSERT INTO `discoveryinfo` VALUES (416144215,1,-858993460); +INSERT INTO `discoveryinfo` VALUES (416494218,10,-858993460); +/*!40000 ALTER TABLE `discoveryinfo` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:13 diff --git a/sql/export.sh b/sql/export.sh new file mode 100644 index 00000000..c82598f9 --- /dev/null +++ b/sql/export.sh @@ -0,0 +1,16 @@ +#!/bin/bash +EXPORT_PATH="C://coding//repositories//ffxiv related//sapphire//Sapphire2Clone//sql//" +USER=root +PASS=root +DBNAME=sapphire +#test -d EXPORT_PATH || mkdir -p $EXPORT_PATH +list=`mysqlshow -u $USER -p$PASS $DBNAME` +for T in $list; +do + if [[ "$T" != "information_schema" ]] && [[ "$T" != "performance_schema" ]] && + [[ "$T" != "mysql" ]] && [[ "$T" != "${DBNAME}"* ]] && [[ "$T" != *"*"* ]] && [[ "$T" != *"+"* ]] && [[ "$T" != *"+="* ]] && + [[ "$T" != "Tables" ]] && [[ "$T" != "Database"* ]] && [[ "$T" != "" ]]; then + echo "Backing up " $T; + mysqldump -u $USER -p$PASS $DBNAME $T --extended-insert=FALSE > "${EXPORT_PATH}/${T}.sql" + fi +done; \ No newline at end of file diff --git a/sql/import.sh b/sql/import.sh new file mode 100644 index 00000000..f7006dba --- /dev/null +++ b/sql/import.sh @@ -0,0 +1,29 @@ +#!/bin/bash +IMPORT_PATH="C://coding//repositories//ffxiv related//sapphire//Sapphire2Clone//sql//" +USER=root +PASS=root +DBNAME=sapphire + +ECHO Creating Database $DBNAME +mysqladmin -h localhost -u $USER -p$PASS DROP $DBNAME + +ECHO Creating Database $DBNAME +mysqladmin -h localhost -u $USER -p$PASS CREATE $DBNAME IF NOT EXISTS $DBNAME + +ECHO Loading $DBNAME tables into the database +sh cd $IMPORT_PATH + +"C:\program files\mysql\mysql server 5.7\bin\mysql" $DBNAME -h localhost -u $USER -p$PASS -e 'SET AUTOCOMMIT=0;' + +for X in '*.sql'; +do + for Y in $X + do + echo Importing $Y; + "C:\program files\mysql\mysql server 5.7\bin\mysql" $DBNAME -h localhost -u $USER -p$PASS < $Y + done +done + +"C:\program files\mysql\mysql server 5.7\bin\mysql" $DBNAME -h localhost -u $USER -p$PASS -e 'COMMIT;' + +ECHO Finished! \ No newline at end of file diff --git a/sql/infolinkshell.sql b/sql/infolinkshell.sql new file mode 100644 index 00000000..66e7b224 --- /dev/null +++ b/sql/infolinkshell.sql @@ -0,0 +1,57 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `infolinkshell` +-- + +DROP TABLE IF EXISTS `infolinkshell`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `infolinkshell` ( + `LinkshellId` int(20) NOT NULL AUTO_INCREMENT, + `MasterCharacterId` int(20) DEFAULT NULL, + `CharacterIdList` blob, + `LinkshellName` varchar(32) DEFAULT NULL, + `LeaderIdList` blob, + `InviteIdList` blob, + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` date DEFAULT NULL, + PRIMARY KEY (`LinkshellId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `infolinkshell` +-- + +LOCK TABLES `infolinkshell` WRITE; +/*!40000 ALTER TABLE `infolinkshell` DISABLE KEYS */; +/*!40000 ALTER TABLE `infolinkshell` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:15 diff --git a/sql/uniqueiddata.sql b/sql/uniqueiddata.sql new file mode 100644 index 00000000..e397fbec --- /dev/null +++ b/sql/uniqueiddata.sql @@ -0,0 +1,505 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `uniqueiddata` +-- + +DROP TABLE IF EXISTS `uniqueiddata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `uniqueiddata` ( + `NextId` int(20) NOT NULL AUTO_INCREMENT, + `IdName` varchar(16) DEFAULT 'NOT SET', + `IS_DELETE` int(3) DEFAULT '0', + `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', + `UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`NextId`) +) ENGINE=MyISAM AUTO_INCREMENT=1000447 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `uniqueiddata` +-- + +LOCK TABLES `uniqueiddata` WRITE; +/*!40000 ALTER TABLE `uniqueiddata` DISABLE KEYS */; +INSERT INTO `uniqueiddata` VALUES (1,'NOT_SET',0,0,'2016-02-10 22:04:00'); +INSERT INTO `uniqueiddata` VALUES (2,'NOT_SET',0,0,'2016-02-10 22:04:23'); +INSERT INTO `uniqueiddata` VALUES (3,'NOT_SET',0,0,'2016-02-10 22:06:29'); +INSERT INTO `uniqueiddata` VALUES (4,'NOT_SET',0,0,'2016-02-10 22:06:29'); +INSERT INTO `uniqueiddata` VALUES (5,'NOT_SET',0,0,'2016-02-10 22:06:29'); +INSERT INTO `uniqueiddata` VALUES (6,'NOT_SET',0,0,'2016-02-10 22:06:29'); +INSERT INTO `uniqueiddata` VALUES (1000001,'NOT_SET',0,0,'2016-02-10 22:07:59'); +INSERT INTO `uniqueiddata` VALUES (1000002,'NOT_SET',0,0,'2016-02-10 22:07:59'); +INSERT INTO `uniqueiddata` VALUES (1000003,'NOT_SET',0,0,'2016-02-10 22:07:59'); +INSERT INTO `uniqueiddata` VALUES (1000004,'NOT_SET',0,0,'2016-02-10 22:07:59'); +INSERT INTO `uniqueiddata` VALUES (1000005,'NOT_SET',0,0,'2016-02-11 00:50:07'); +INSERT INTO `uniqueiddata` VALUES (1000006,'NOT_SET',0,0,'2016-02-11 00:54:50'); +INSERT INTO `uniqueiddata` VALUES (1000007,'NOT_SET',0,0,'2016-02-11 20:46:38'); +INSERT INTO `uniqueiddata` VALUES (1000008,'NOT_SET',0,0,'2016-02-11 20:46:47'); +INSERT INTO `uniqueiddata` VALUES (1000009,'NOT_SET',0,0,'2016-02-11 20:46:54'); +INSERT INTO `uniqueiddata` VALUES (1000010,'NOT_SET',0,0,'2016-02-11 20:47:04'); +INSERT INTO `uniqueiddata` VALUES (1000011,'NOT_SET',0,0,'2016-02-11 20:47:11'); +INSERT INTO `uniqueiddata` VALUES (1000012,'NOT_SET',0,0,'2016-02-11 22:20:14'); +INSERT INTO `uniqueiddata` VALUES (1000013,'NOT_SET',0,0,'2016-02-12 08:18:49'); +INSERT INTO `uniqueiddata` VALUES (1000014,'NOT_SET',0,0,'2016-02-12 08:22:10'); +INSERT INTO `uniqueiddata` VALUES (1000015,'NOT_SET',0,0,'2016-02-12 18:54:51'); +INSERT INTO `uniqueiddata` VALUES (1000016,'NOT_SET',0,0,'2016-02-12 18:54:51'); +INSERT INTO `uniqueiddata` VALUES (1000017,'NOT_SET',0,0,'2016-02-12 18:54:51'); +INSERT INTO `uniqueiddata` VALUES (1000018,'NOT_SET',0,0,'2016-02-12 18:54:51'); +INSERT INTO `uniqueiddata` VALUES (1000019,'NOT_SET',0,0,'2016-02-12 18:54:51'); +INSERT INTO `uniqueiddata` VALUES (1000020,'NOT_SET',0,0,'2016-02-13 11:45:32'); +INSERT INTO `uniqueiddata` VALUES (1000021,'NOT_SET',0,0,'2016-02-13 11:45:40'); +INSERT INTO `uniqueiddata` VALUES (1000022,'NOT_SET',0,0,'2016-02-13 11:45:43'); +INSERT INTO `uniqueiddata` VALUES (1000023,'NOT_SET',0,0,'2016-02-13 11:46:02'); +INSERT INTO `uniqueiddata` VALUES (1000024,'NOT_SET',0,0,'2016-02-13 11:46:04'); +INSERT INTO `uniqueiddata` VALUES (1000025,'NOT_SET',0,0,'2016-02-13 11:46:06'); +INSERT INTO `uniqueiddata` VALUES (1000026,'NOT_SET',0,0,'2016-02-13 11:46:08'); +INSERT INTO `uniqueiddata` VALUES (1000027,'NOT_SET',0,0,'2016-02-13 11:46:10'); +INSERT INTO `uniqueiddata` VALUES (1000028,'NOT_SET',0,0,'2016-02-13 11:50:06'); +INSERT INTO `uniqueiddata` VALUES (1000029,'NOT_SET',0,0,'2016-02-13 11:51:25'); +INSERT INTO `uniqueiddata` VALUES (1000030,'NOT_SET',0,0,'2016-02-13 11:55:58'); +INSERT INTO `uniqueiddata` VALUES (1000031,'NOT_SET',0,0,'2016-02-13 11:56:02'); +INSERT INTO `uniqueiddata` VALUES (1000032,'NOT_SET',0,0,'2016-02-13 11:56:05'); +INSERT INTO `uniqueiddata` VALUES (1000033,'NOT_SET',0,0,'2016-02-13 11:56:08'); +INSERT INTO `uniqueiddata` VALUES (1000034,'NOT_SET',0,0,'2016-02-13 11:56:11'); +INSERT INTO `uniqueiddata` VALUES (1000035,'NOT_SET',0,0,'2016-02-13 11:56:17'); +INSERT INTO `uniqueiddata` VALUES (1000036,'NOT_SET',0,0,'2016-02-13 11:57:24'); +INSERT INTO `uniqueiddata` VALUES (1000037,'NOT_SET',0,0,'2016-02-13 11:57:27'); +INSERT INTO `uniqueiddata` VALUES (1000038,'NOT_SET',0,0,'2016-02-13 11:57:28'); +INSERT INTO `uniqueiddata` VALUES (1000039,'NOT_SET',0,0,'2016-02-13 11:57:29'); +INSERT INTO `uniqueiddata` VALUES (1000040,'NOT_SET',0,0,'2016-02-13 11:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000041,'NOT_SET',0,0,'2016-02-13 11:57:31'); +INSERT INTO `uniqueiddata` VALUES (1000042,'NOT_SET',0,0,'2016-02-13 11:57:34'); +INSERT INTO `uniqueiddata` VALUES (1000043,'NOT_SET',0,0,'2016-02-13 11:57:36'); +INSERT INTO `uniqueiddata` VALUES (1000044,'NOT_SET',0,0,'2016-02-13 11:57:37'); +INSERT INTO `uniqueiddata` VALUES (1000045,'NOT_SET',0,0,'2016-02-13 11:57:38'); +INSERT INTO `uniqueiddata` VALUES (1000046,'NOT_SET',0,0,'2016-02-13 11:57:39'); +INSERT INTO `uniqueiddata` VALUES (1000047,'NOT_SET',0,0,'2016-02-13 11:57:40'); +INSERT INTO `uniqueiddata` VALUES (1000048,'NOT_SET',0,0,'2016-02-13 11:57:41'); +INSERT INTO `uniqueiddata` VALUES (1000049,'NOT_SET',0,0,'2016-02-13 11:57:42'); +INSERT INTO `uniqueiddata` VALUES (1000050,'NOT_SET',0,0,'2016-02-13 11:57:44'); +INSERT INTO `uniqueiddata` VALUES (1000051,'NOT_SET',0,0,'2016-02-13 11:57:47'); +INSERT INTO `uniqueiddata` VALUES (1000052,'NOT_SET',0,0,'2016-02-13 11:57:50'); +INSERT INTO `uniqueiddata` VALUES (1000053,'NOT_SET',0,0,'2016-02-13 11:57:52'); +INSERT INTO `uniqueiddata` VALUES (1000054,'NOT_SET',0,0,'2016-02-13 11:57:55'); +INSERT INTO `uniqueiddata` VALUES (1000055,'NOT_SET',0,0,'2016-02-13 11:57:57'); +INSERT INTO `uniqueiddata` VALUES (1000056,'NOT_SET',0,0,'2016-02-13 11:57:57'); +INSERT INTO `uniqueiddata` VALUES (1000057,'NOT_SET',0,0,'2016-02-13 11:58:00'); +INSERT INTO `uniqueiddata` VALUES (1000058,'NOT_SET',0,0,'2016-02-13 11:58:00'); +INSERT INTO `uniqueiddata` VALUES (1000059,'NOT_SET',0,0,'2016-02-13 11:58:02'); +INSERT INTO `uniqueiddata` VALUES (1000060,'NOT_SET',0,0,'2016-02-13 11:58:02'); +INSERT INTO `uniqueiddata` VALUES (1000061,'NOT_SET',0,0,'2016-02-13 13:03:07'); +INSERT INTO `uniqueiddata` VALUES (1000062,'NOT_SET',0,0,'2016-02-13 13:03:07'); +INSERT INTO `uniqueiddata` VALUES (1000063,'NOT_SET',0,0,'2016-02-13 13:03:51'); +INSERT INTO `uniqueiddata` VALUES (1000064,'NOT_SET',0,0,'2016-02-13 13:03:51'); +INSERT INTO `uniqueiddata` VALUES (1000065,'NOT_SET',0,0,'2016-02-13 13:04:03'); +INSERT INTO `uniqueiddata` VALUES (1000066,'NOT_SET',0,0,'2016-02-13 13:04:07'); +INSERT INTO `uniqueiddata` VALUES (1000067,'NOT_SET',0,0,'2016-02-13 13:04:20'); +INSERT INTO `uniqueiddata` VALUES (1000068,'NOT_SET',0,0,'2016-02-13 13:04:41'); +INSERT INTO `uniqueiddata` VALUES (1000069,'NOT_SET',0,0,'2016-02-15 11:43:46'); +INSERT INTO `uniqueiddata` VALUES (1000070,'NOT_SET',0,0,'2016-02-15 11:43:46'); +INSERT INTO `uniqueiddata` VALUES (1000071,'NOT_SET',0,0,'2016-02-15 11:43:46'); +INSERT INTO `uniqueiddata` VALUES (1000072,'NOT_SET',0,0,'2016-02-15 11:43:46'); +INSERT INTO `uniqueiddata` VALUES (1000073,'NOT_SET',0,0,'2016-02-15 11:43:46'); +INSERT INTO `uniqueiddata` VALUES (1000074,'NOT_SET',0,0,'2016-02-15 12:12:37'); +INSERT INTO `uniqueiddata` VALUES (1000075,'NOT_SET',0,0,'2016-02-15 12:12:37'); +INSERT INTO `uniqueiddata` VALUES (1000076,'NOT_SET',0,0,'2016-02-15 12:12:37'); +INSERT INTO `uniqueiddata` VALUES (1000077,'NOT_SET',0,0,'2016-02-15 12:12:37'); +INSERT INTO `uniqueiddata` VALUES (1000078,'NOT_SET',0,0,'2016-02-15 12:12:37'); +INSERT INTO `uniqueiddata` VALUES (1000079,'NOT_SET',0,0,'2016-02-15 13:19:52'); +INSERT INTO `uniqueiddata` VALUES (1000080,'NOT_SET',0,0,'2016-02-15 13:22:50'); +INSERT INTO `uniqueiddata` VALUES (1000081,'NOT_SET',0,0,'2016-02-15 13:28:08'); +INSERT INTO `uniqueiddata` VALUES (1000082,'NOT_SET',0,0,'2016-02-15 13:31:52'); +INSERT INTO `uniqueiddata` VALUES (1000083,'NOT_SET',0,0,'2016-02-15 13:33:42'); +INSERT INTO `uniqueiddata` VALUES (1000084,'NOT_SET',0,0,'2016-02-15 13:35:28'); +INSERT INTO `uniqueiddata` VALUES (1000085,'NOT_SET',0,0,'2016-02-15 13:50:28'); +INSERT INTO `uniqueiddata` VALUES (1000086,'NOT_SET',0,0,'2016-02-15 14:28:11'); +INSERT INTO `uniqueiddata` VALUES (1000087,'NOT_SET',0,0,'2016-02-15 14:28:21'); +INSERT INTO `uniqueiddata` VALUES (1000088,'NOT_SET',0,0,'2016-02-15 14:28:25'); +INSERT INTO `uniqueiddata` VALUES (1000089,'NOT_SET',0,0,'2016-02-15 14:28:26'); +INSERT INTO `uniqueiddata` VALUES (1000090,'NOT_SET',0,0,'2016-02-15 14:28:26'); +INSERT INTO `uniqueiddata` VALUES (1000091,'NOT_SET',0,0,'2016-02-15 14:28:27'); +INSERT INTO `uniqueiddata` VALUES (1000092,'NOT_SET',0,0,'2016-02-15 14:28:27'); +INSERT INTO `uniqueiddata` VALUES (1000093,'NOT_SET',0,0,'2016-02-15 14:28:35'); +INSERT INTO `uniqueiddata` VALUES (1000094,'NOT_SET',0,0,'2016-02-15 14:40:54'); +INSERT INTO `uniqueiddata` VALUES (1000095,'NOT_SET',0,0,'2016-02-15 14:42:22'); +INSERT INTO `uniqueiddata` VALUES (1000096,'NOT_SET',0,0,'2016-02-15 14:44:54'); +INSERT INTO `uniqueiddata` VALUES (1000097,'NOT_SET',0,0,'2016-02-15 14:50:58'); +INSERT INTO `uniqueiddata` VALUES (1000098,'NOT_SET',0,0,'2016-02-15 14:51:00'); +INSERT INTO `uniqueiddata` VALUES (1000099,'NOT_SET',0,0,'2016-02-15 14:51:02'); +INSERT INTO `uniqueiddata` VALUES (1000100,'NOT_SET',0,0,'2016-02-15 14:51:02'); +INSERT INTO `uniqueiddata` VALUES (1000101,'NOT_SET',0,0,'2016-02-15 14:51:15'); +INSERT INTO `uniqueiddata` VALUES (1000102,'NOT_SET',0,0,'2016-02-15 14:51:17'); +INSERT INTO `uniqueiddata` VALUES (1000103,'NOT_SET',0,0,'2016-02-15 14:51:18'); +INSERT INTO `uniqueiddata` VALUES (1000104,'NOT_SET',0,0,'2016-02-15 14:51:19'); +INSERT INTO `uniqueiddata` VALUES (1000105,'NOT_SET',0,0,'2016-02-15 14:51:19'); +INSERT INTO `uniqueiddata` VALUES (1000106,'NOT_SET',0,0,'2016-02-15 14:51:20'); +INSERT INTO `uniqueiddata` VALUES (1000107,'NOT_SET',0,0,'2016-02-15 14:51:20'); +INSERT INTO `uniqueiddata` VALUES (1000108,'NOT_SET',0,0,'2016-02-15 14:51:21'); +INSERT INTO `uniqueiddata` VALUES (1000109,'NOT_SET',0,0,'2016-02-15 14:51:21'); +INSERT INTO `uniqueiddata` VALUES (1000110,'NOT_SET',0,0,'2016-02-15 14:51:22'); +INSERT INTO `uniqueiddata` VALUES (1000111,'NOT_SET',0,0,'2016-02-15 14:51:23'); +INSERT INTO `uniqueiddata` VALUES (1000112,'NOT_SET',0,0,'2016-02-15 14:51:24'); +INSERT INTO `uniqueiddata` VALUES (1000113,'NOT_SET',0,0,'2016-02-15 14:51:25'); +INSERT INTO `uniqueiddata` VALUES (1000114,'NOT_SET',0,0,'2016-02-15 14:51:26'); +INSERT INTO `uniqueiddata` VALUES (1000115,'NOT_SET',0,0,'2016-02-15 14:51:26'); +INSERT INTO `uniqueiddata` VALUES (1000116,'NOT_SET',0,0,'2016-02-15 14:51:27'); +INSERT INTO `uniqueiddata` VALUES (1000117,'NOT_SET',0,0,'2016-02-15 14:51:28'); +INSERT INTO `uniqueiddata` VALUES (1000118,'NOT_SET',0,0,'2016-02-15 14:51:29'); +INSERT INTO `uniqueiddata` VALUES (1000119,'NOT_SET',0,0,'2016-02-15 14:51:29'); +INSERT INTO `uniqueiddata` VALUES (1000120,'NOT_SET',0,0,'2016-02-15 14:51:30'); +INSERT INTO `uniqueiddata` VALUES (1000121,'NOT_SET',0,0,'2016-02-15 14:51:31'); +INSERT INTO `uniqueiddata` VALUES (1000122,'NOT_SET',0,0,'2016-02-15 14:51:31'); +INSERT INTO `uniqueiddata` VALUES (1000123,'NOT_SET',0,0,'2016-02-15 14:51:31'); +INSERT INTO `uniqueiddata` VALUES (1000124,'NOT_SET',0,0,'2016-02-15 14:51:32'); +INSERT INTO `uniqueiddata` VALUES (1000125,'NOT_SET',0,0,'2016-02-15 14:51:32'); +INSERT INTO `uniqueiddata` VALUES (1000126,'NOT_SET',0,0,'2016-02-15 14:51:33'); +INSERT INTO `uniqueiddata` VALUES (1000127,'NOT_SET',0,0,'2016-02-15 14:51:33'); +INSERT INTO `uniqueiddata` VALUES (1000128,'NOT_SET',0,0,'2016-02-15 14:52:22'); +INSERT INTO `uniqueiddata` VALUES (1000129,'NOT_SET',0,0,'2016-02-15 14:52:22'); +INSERT INTO `uniqueiddata` VALUES (1000130,'NOT_SET',0,0,'2016-02-15 14:52:39'); +INSERT INTO `uniqueiddata` VALUES (1000131,'NOT_SET',0,0,'2016-02-15 14:52:39'); +INSERT INTO `uniqueiddata` VALUES (1000132,'NOT_SET',0,0,'2016-02-15 15:09:49'); +INSERT INTO `uniqueiddata` VALUES (1000133,'NOT_SET',0,0,'2016-02-15 15:09:56'); +INSERT INTO `uniqueiddata` VALUES (1000134,'NOT_SET',0,0,'2016-02-15 15:10:05'); +INSERT INTO `uniqueiddata` VALUES (1000135,'NOT_SET',0,0,'2016-02-15 15:10:05'); +INSERT INTO `uniqueiddata` VALUES (1000136,'NOT_SET',0,0,'2016-02-15 15:10:14'); +INSERT INTO `uniqueiddata` VALUES (1000137,'NOT_SET',0,0,'2016-02-15 15:10:14'); +INSERT INTO `uniqueiddata` VALUES (1000138,'NOT_SET',0,0,'2016-02-15 15:10:34'); +INSERT INTO `uniqueiddata` VALUES (1000139,'NOT_SET',0,0,'2016-02-15 15:10:34'); +INSERT INTO `uniqueiddata` VALUES (1000140,'NOT_SET',0,0,'2016-02-15 15:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000141,'NOT_SET',0,0,'2016-02-15 15:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000142,'NOT_SET',0,0,'2016-02-15 15:12:49'); +INSERT INTO `uniqueiddata` VALUES (1000143,'NOT_SET',0,0,'2016-02-15 15:12:49'); +INSERT INTO `uniqueiddata` VALUES (1000144,'NOT_SET',0,0,'2016-02-15 15:14:34'); +INSERT INTO `uniqueiddata` VALUES (1000145,'NOT_SET',0,0,'2016-02-15 15:14:34'); +INSERT INTO `uniqueiddata` VALUES (1000146,'NOT_SET',0,0,'2016-02-15 15:14:38'); +INSERT INTO `uniqueiddata` VALUES (1000147,'NOT_SET',0,0,'2016-02-15 15:14:38'); +INSERT INTO `uniqueiddata` VALUES (1000148,'NOT_SET',0,0,'2016-02-15 15:14:41'); +INSERT INTO `uniqueiddata` VALUES (1000149,'NOT_SET',0,0,'2016-02-15 15:14:41'); +INSERT INTO `uniqueiddata` VALUES (1000150,'NOT_SET',0,0,'2016-02-15 15:14:42'); +INSERT INTO `uniqueiddata` VALUES (1000151,'NOT_SET',0,0,'2016-02-15 15:14:42'); +INSERT INTO `uniqueiddata` VALUES (1000152,'NOT_SET',0,0,'2016-02-15 15:18:32'); +INSERT INTO `uniqueiddata` VALUES (1000153,'NOT_SET',0,0,'2016-02-15 15:18:32'); +INSERT INTO `uniqueiddata` VALUES (1000154,'NOT_SET',0,0,'2016-02-15 15:18:47'); +INSERT INTO `uniqueiddata` VALUES (1000155,'NOT_SET',0,0,'2016-02-15 15:18:47'); +INSERT INTO `uniqueiddata` VALUES (1000156,'NOT_SET',0,0,'2016-02-15 15:18:59'); +INSERT INTO `uniqueiddata` VALUES (1000157,'NOT_SET',0,0,'2016-02-15 15:18:59'); +INSERT INTO `uniqueiddata` VALUES (1000158,'NOT_SET',0,0,'2016-02-15 15:19:09'); +INSERT INTO `uniqueiddata` VALUES (1000159,'NOT_SET',0,0,'2016-02-15 15:19:09'); +INSERT INTO `uniqueiddata` VALUES (1000160,'NOT_SET',0,0,'2016-02-15 15:19:10'); +INSERT INTO `uniqueiddata` VALUES (1000161,'NOT_SET',0,0,'2016-02-15 15:19:10'); +INSERT INTO `uniqueiddata` VALUES (1000162,'NOT_SET',0,0,'2016-02-15 15:20:33'); +INSERT INTO `uniqueiddata` VALUES (1000163,'NOT_SET',0,0,'2016-02-15 15:20:33'); +INSERT INTO `uniqueiddata` VALUES (1000164,'NOT_SET',0,0,'2016-02-15 15:21:20'); +INSERT INTO `uniqueiddata` VALUES (1000165,'NOT_SET',0,0,'2016-02-15 15:21:20'); +INSERT INTO `uniqueiddata` VALUES (1000166,'NOT_SET',0,0,'2016-02-15 15:21:46'); +INSERT INTO `uniqueiddata` VALUES (1000167,'NOT_SET',0,0,'2016-02-15 15:21:46'); +INSERT INTO `uniqueiddata` VALUES (1000168,'NOT_SET',0,0,'2016-02-15 15:23:36'); +INSERT INTO `uniqueiddata` VALUES (1000169,'NOT_SET',0,0,'2016-02-15 15:23:36'); +INSERT INTO `uniqueiddata` VALUES (1000170,'NOT_SET',0,0,'2016-02-15 15:23:51'); +INSERT INTO `uniqueiddata` VALUES (1000171,'NOT_SET',0,0,'2016-02-15 15:23:51'); +INSERT INTO `uniqueiddata` VALUES (1000172,'NOT_SET',0,0,'2016-02-15 15:29:44'); +INSERT INTO `uniqueiddata` VALUES (1000173,'NOT_SET',0,0,'2016-02-15 15:29:44'); +INSERT INTO `uniqueiddata` VALUES (1000174,'NOT_SET',0,0,'2016-02-15 15:29:44'); +INSERT INTO `uniqueiddata` VALUES (1000175,'NOT_SET',0,0,'2016-02-15 15:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000176,'NOT_SET',0,0,'2016-02-15 15:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000177,'NOT_SET',0,0,'2016-02-15 15:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000178,'NOT_SET',0,0,'2016-02-15 15:32:24'); +INSERT INTO `uniqueiddata` VALUES (1000179,'NOT_SET',0,0,'2016-02-15 15:32:24'); +INSERT INTO `uniqueiddata` VALUES (1000180,'NOT_SET',0,0,'2016-02-15 15:32:24'); +INSERT INTO `uniqueiddata` VALUES (1000181,'NOT_SET',0,0,'2016-02-15 15:34:55'); +INSERT INTO `uniqueiddata` VALUES (1000182,'NOT_SET',0,0,'2016-02-15 15:34:55'); +INSERT INTO `uniqueiddata` VALUES (1000183,'NOT_SET',0,0,'2016-02-15 15:34:55'); +INSERT INTO `uniqueiddata` VALUES (1000184,'NOT_SET',0,0,'2016-02-15 16:12:30'); +INSERT INTO `uniqueiddata` VALUES (1000185,'NOT_SET',0,0,'2016-02-15 16:12:30'); +INSERT INTO `uniqueiddata` VALUES (1000186,'NOT_SET',0,0,'2016-02-15 16:12:30'); +INSERT INTO `uniqueiddata` VALUES (1000187,'NOT_SET',0,0,'2016-02-15 16:12:44'); +INSERT INTO `uniqueiddata` VALUES (1000188,'NOT_SET',0,0,'2016-02-15 16:12:44'); +INSERT INTO `uniqueiddata` VALUES (1000189,'NOT_SET',0,0,'2016-02-15 16:12:44'); +INSERT INTO `uniqueiddata` VALUES (1000190,'NOT_SET',0,0,'2016-02-15 16:12:45'); +INSERT INTO `uniqueiddata` VALUES (1000191,'NOT_SET',0,0,'2016-02-15 16:12:45'); +INSERT INTO `uniqueiddata` VALUES (1000192,'NOT_SET',0,0,'2016-02-15 16:12:45'); +INSERT INTO `uniqueiddata` VALUES (1000193,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000194,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000195,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000196,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000197,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000198,'NOT_SET',0,0,'2016-02-15 16:12:46'); +INSERT INTO `uniqueiddata` VALUES (1000199,'NOT_SET',0,0,'2016-02-15 16:14:50'); +INSERT INTO `uniqueiddata` VALUES (1000200,'NOT_SET',0,0,'2016-02-15 16:14:50'); +INSERT INTO `uniqueiddata` VALUES (1000201,'NOT_SET',0,0,'2016-02-15 16:14:50'); +INSERT INTO `uniqueiddata` VALUES (1000202,'NOT_SET',0,0,'2016-02-15 16:15:03'); +INSERT INTO `uniqueiddata` VALUES (1000203,'NOT_SET',0,0,'2016-02-15 16:15:03'); +INSERT INTO `uniqueiddata` VALUES (1000204,'NOT_SET',0,0,'2016-02-15 16:15:03'); +INSERT INTO `uniqueiddata` VALUES (1000205,'NOT_SET',0,0,'2016-02-15 16:15:11'); +INSERT INTO `uniqueiddata` VALUES (1000206,'NOT_SET',0,0,'2016-02-15 16:15:11'); +INSERT INTO `uniqueiddata` VALUES (1000207,'NOT_SET',0,0,'2016-02-15 16:15:11'); +INSERT INTO `uniqueiddata` VALUES (1000208,'NOT_SET',0,0,'2016-02-15 22:32:45'); +INSERT INTO `uniqueiddata` VALUES (1000209,'NOT_SET',0,0,'2016-02-15 22:32:45'); +INSERT INTO `uniqueiddata` VALUES (1000210,'NOT_SET',0,0,'2016-02-15 22:32:45'); +INSERT INTO `uniqueiddata` VALUES (1000211,'NOT_SET',0,0,'2016-02-15 22:32:45'); +INSERT INTO `uniqueiddata` VALUES (1000212,'NOT_SET',0,0,'2016-02-15 22:32:45'); +INSERT INTO `uniqueiddata` VALUES (1000213,'NOT_SET',0,0,'2016-02-15 22:34:23'); +INSERT INTO `uniqueiddata` VALUES (1000214,'NOT_SET',0,0,'2016-02-15 23:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000215,'NOT_SET',0,0,'2016-02-15 23:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000216,'NOT_SET',0,0,'2016-02-15 23:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000217,'NOT_SET',0,0,'2016-02-15 23:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000218,'NOT_SET',0,0,'2016-02-15 23:11:20'); +INSERT INTO `uniqueiddata` VALUES (1000219,'NOT_SET',0,0,'2016-02-15 23:16:58'); +INSERT INTO `uniqueiddata` VALUES (1000220,'NOT_SET',0,0,'2016-02-16 19:00:04'); +INSERT INTO `uniqueiddata` VALUES (1000221,'NOT_SET',0,0,'2016-02-16 19:03:23'); +INSERT INTO `uniqueiddata` VALUES (1000222,'NOT_SET',0,0,'2016-02-16 19:06:00'); +INSERT INTO `uniqueiddata` VALUES (1000223,'NOT_SET',0,0,'2016-02-16 19:11:54'); +INSERT INTO `uniqueiddata` VALUES (1000224,'NOT_SET',0,0,'2016-02-16 19:14:18'); +INSERT INTO `uniqueiddata` VALUES (1000225,'NOT_SET',0,0,'2016-02-16 19:22:28'); +INSERT INTO `uniqueiddata` VALUES (1000226,'NOT_SET',0,0,'2016-02-16 21:43:08'); +INSERT INTO `uniqueiddata` VALUES (1000227,'NOT_SET',0,0,'2016-02-16 21:44:28'); +INSERT INTO `uniqueiddata` VALUES (1000228,'NOT_SET',0,0,'2016-02-16 21:46:22'); +INSERT INTO `uniqueiddata` VALUES (1000229,'NOT_SET',0,0,'2016-02-16 21:46:52'); +INSERT INTO `uniqueiddata` VALUES (1000230,'NOT_SET',0,0,'2016-02-16 21:47:53'); +INSERT INTO `uniqueiddata` VALUES (1000231,'NOT_SET',0,0,'2016-02-16 21:48:12'); +INSERT INTO `uniqueiddata` VALUES (1000232,'NOT_SET',0,0,'2016-02-16 21:48:59'); +INSERT INTO `uniqueiddata` VALUES (1000233,'NOT_SET',0,0,'2016-02-16 21:49:38'); +INSERT INTO `uniqueiddata` VALUES (1000234,'NOT_SET',0,0,'2016-02-16 21:51:07'); +INSERT INTO `uniqueiddata` VALUES (1000235,'NOT_SET',0,0,'2016-02-16 21:51:19'); +INSERT INTO `uniqueiddata` VALUES (1000236,'NOT_SET',0,0,'2016-02-16 21:57:09'); +INSERT INTO `uniqueiddata` VALUES (1000237,'NOT_SET',0,0,'2016-02-16 22:12:34'); +INSERT INTO `uniqueiddata` VALUES (1000238,'NOT_SET',0,0,'2016-02-16 22:12:45'); +INSERT INTO `uniqueiddata` VALUES (1000239,'NOT_SET',0,0,'2016-02-16 22:13:01'); +INSERT INTO `uniqueiddata` VALUES (1000240,'NOT_SET',0,0,'2016-02-16 22:13:08'); +INSERT INTO `uniqueiddata` VALUES (1000241,'NOT_SET',0,0,'2016-02-17 23:58:31'); +INSERT INTO `uniqueiddata` VALUES (1000242,'NOT_SET',0,0,'2016-02-17 23:58:38'); +INSERT INTO `uniqueiddata` VALUES (1000243,'NOT_SET',0,0,'2016-02-17 23:58:46'); +INSERT INTO `uniqueiddata` VALUES (1000244,'NOT_SET',0,0,'2016-02-18 00:00:21'); +INSERT INTO `uniqueiddata` VALUES (1000245,'NOT_SET',0,0,'2016-02-18 00:02:48'); +INSERT INTO `uniqueiddata` VALUES (1000246,'NOT_SET',0,0,'2016-02-18 00:05:42'); +INSERT INTO `uniqueiddata` VALUES (1000247,'NOT_SET',0,0,'2016-02-18 00:06:33'); +INSERT INTO `uniqueiddata` VALUES (1000248,'NOT_SET',0,0,'2016-02-18 00:11:51'); +INSERT INTO `uniqueiddata` VALUES (1000249,'NOT_SET',0,0,'2016-02-18 00:15:13'); +INSERT INTO `uniqueiddata` VALUES (1000250,'NOT_SET',0,0,'2016-02-18 00:20:42'); +INSERT INTO `uniqueiddata` VALUES (1000251,'NOT_SET',0,0,'2016-02-18 00:22:35'); +INSERT INTO `uniqueiddata` VALUES (1000252,'NOT_SET',0,0,'2016-02-18 00:29:39'); +INSERT INTO `uniqueiddata` VALUES (1000253,'NOT_SET',0,0,'2016-02-18 00:34:22'); +INSERT INTO `uniqueiddata` VALUES (1000254,'NOT_SET',0,0,'2016-02-18 00:35:05'); +INSERT INTO `uniqueiddata` VALUES (1000255,'NOT_SET',0,0,'2016-02-18 00:35:10'); +INSERT INTO `uniqueiddata` VALUES (1000256,'NOT_SET',0,0,'2016-02-18 00:35:14'); +INSERT INTO `uniqueiddata` VALUES (1000257,'NOT_SET',0,0,'2016-02-18 00:35:17'); +INSERT INTO `uniqueiddata` VALUES (1000258,'NOT_SET',0,0,'2016-02-18 00:35:22'); +INSERT INTO `uniqueiddata` VALUES (1000259,'NOT_SET',0,0,'2016-02-18 00:35:27'); +INSERT INTO `uniqueiddata` VALUES (1000260,'NOT_SET',0,0,'2016-02-18 00:35:37'); +INSERT INTO `uniqueiddata` VALUES (1000261,'NOT_SET',0,0,'2016-02-18 00:36:37'); +INSERT INTO `uniqueiddata` VALUES (1000262,'NOT_SET',0,0,'2016-02-18 00:36:48'); +INSERT INTO `uniqueiddata` VALUES (1000263,'NOT_SET',0,0,'2016-02-18 00:36:53'); +INSERT INTO `uniqueiddata` VALUES (1000264,'NOT_SET',0,0,'2016-02-18 00:36:57'); +INSERT INTO `uniqueiddata` VALUES (1000265,'NOT_SET',0,0,'2016-02-18 00:37:01'); +INSERT INTO `uniqueiddata` VALUES (1000266,'NOT_SET',0,0,'2016-02-18 00:37:05'); +INSERT INTO `uniqueiddata` VALUES (1000267,'NOT_SET',0,0,'2016-02-18 00:37:07'); +INSERT INTO `uniqueiddata` VALUES (1000268,'NOT_SET',0,0,'2016-02-18 00:37:20'); +INSERT INTO `uniqueiddata` VALUES (1000269,'NOT_SET',0,0,'2016-02-18 00:41:21'); +INSERT INTO `uniqueiddata` VALUES (1000270,'NOT_SET',0,0,'2016-02-18 00:41:55'); +INSERT INTO `uniqueiddata` VALUES (1000271,'NOT_SET',0,0,'2016-02-18 00:42:00'); +INSERT INTO `uniqueiddata` VALUES (1000272,'NOT_SET',0,0,'2016-02-18 00:42:03'); +INSERT INTO `uniqueiddata` VALUES (1000273,'NOT_SET',0,0,'2016-02-18 00:42:09'); +INSERT INTO `uniqueiddata` VALUES (1000274,'NOT_SET',0,0,'2016-02-18 00:43:13'); +INSERT INTO `uniqueiddata` VALUES (1000275,'NOT_SET',0,0,'2016-02-18 00:43:18'); +INSERT INTO `uniqueiddata` VALUES (1000276,'NOT_SET',0,0,'2016-02-18 00:44:32'); +INSERT INTO `uniqueiddata` VALUES (1000277,'NOT_SET',0,0,'2016-02-18 00:45:24'); +INSERT INTO `uniqueiddata` VALUES (1000278,'NOT_SET',0,0,'2016-02-18 17:38:42'); +INSERT INTO `uniqueiddata` VALUES (1000279,'NOT_SET',0,0,'2016-02-18 17:38:50'); +INSERT INTO `uniqueiddata` VALUES (1000280,'NOT_SET',0,0,'2016-02-18 17:38:59'); +INSERT INTO `uniqueiddata` VALUES (1000281,'NOT_SET',0,0,'2016-02-18 17:39:04'); +INSERT INTO `uniqueiddata` VALUES (1000282,'NOT_SET',0,0,'2016-02-18 17:39:12'); +INSERT INTO `uniqueiddata` VALUES (1000283,'NOT_SET',0,0,'2016-02-18 17:39:21'); +INSERT INTO `uniqueiddata` VALUES (1000284,'NOT_SET',0,0,'2016-02-18 17:39:37'); +INSERT INTO `uniqueiddata` VALUES (1000285,'NOT_SET',0,0,'2016-02-18 17:39:55'); +INSERT INTO `uniqueiddata` VALUES (1000286,'NOT_SET',0,0,'2016-02-18 17:40:03'); +INSERT INTO `uniqueiddata` VALUES (1000287,'NOT_SET',0,0,'2016-02-18 17:40:10'); +INSERT INTO `uniqueiddata` VALUES (1000288,'NOT_SET',0,0,'2016-02-18 17:40:24'); +INSERT INTO `uniqueiddata` VALUES (1000289,'NOT_SET',0,0,'2016-02-18 17:40:29'); +INSERT INTO `uniqueiddata` VALUES (1000290,'NOT_SET',0,0,'2016-02-18 17:40:32'); +INSERT INTO `uniqueiddata` VALUES (1000291,'NOT_SET',0,0,'2016-02-18 17:40:46'); +INSERT INTO `uniqueiddata` VALUES (1000292,'NOT_SET',0,0,'2016-02-18 17:41:00'); +INSERT INTO `uniqueiddata` VALUES (1000293,'NOT_SET',0,0,'2016-02-18 17:41:09'); +INSERT INTO `uniqueiddata` VALUES (1000294,'NOT_SET',0,0,'2016-02-18 17:41:12'); +INSERT INTO `uniqueiddata` VALUES (1000295,'NOT_SET',0,0,'2016-02-18 17:41:16'); +INSERT INTO `uniqueiddata` VALUES (1000296,'NOT_SET',0,0,'2016-02-18 17:41:44'); +INSERT INTO `uniqueiddata` VALUES (1000297,'NOT_SET',0,0,'2016-02-18 17:41:50'); +INSERT INTO `uniqueiddata` VALUES (1000298,'NOT_SET',0,0,'2016-02-18 17:41:54'); +INSERT INTO `uniqueiddata` VALUES (1000299,'NOT_SET',0,0,'2016-02-18 17:41:58'); +INSERT INTO `uniqueiddata` VALUES (1000300,'NOT_SET',0,0,'2016-02-18 17:42:02'); +INSERT INTO `uniqueiddata` VALUES (1000301,'NOT_SET',0,0,'2016-02-18 17:42:06'); +INSERT INTO `uniqueiddata` VALUES (1000302,'NOT_SET',0,0,'2016-02-18 17:42:11'); +INSERT INTO `uniqueiddata` VALUES (1000303,'NOT_SET',0,0,'2016-02-18 17:42:48'); +INSERT INTO `uniqueiddata` VALUES (1000304,'NOT_SET',0,0,'2016-02-18 17:42:52'); +INSERT INTO `uniqueiddata` VALUES (1000305,'NOT_SET',0,0,'2016-02-18 17:43:05'); +INSERT INTO `uniqueiddata` VALUES (1000306,'NOT_SET',0,0,'2016-02-18 17:43:11'); +INSERT INTO `uniqueiddata` VALUES (1000307,'NOT_SET',0,0,'2016-02-18 17:43:28'); +INSERT INTO `uniqueiddata` VALUES (1000308,'NOT_SET',0,0,'2016-02-18 17:43:38'); +INSERT INTO `uniqueiddata` VALUES (1000309,'NOT_SET',0,0,'2016-02-18 17:43:45'); +INSERT INTO `uniqueiddata` VALUES (1000310,'NOT_SET',0,0,'2016-02-18 17:43:47'); +INSERT INTO `uniqueiddata` VALUES (1000311,'NOT_SET',0,0,'2016-02-18 17:43:52'); +INSERT INTO `uniqueiddata` VALUES (1000312,'NOT_SET',0,0,'2016-02-18 17:43:57'); +INSERT INTO `uniqueiddata` VALUES (1000313,'NOT_SET',0,0,'2016-02-18 17:44:03'); +INSERT INTO `uniqueiddata` VALUES (1000314,'NOT_SET',0,0,'2016-02-18 17:44:10'); +INSERT INTO `uniqueiddata` VALUES (1000315,'NOT_SET',0,0,'2016-02-18 17:44:13'); +INSERT INTO `uniqueiddata` VALUES (1000316,'NOT_SET',0,0,'2016-02-18 17:44:17'); +INSERT INTO `uniqueiddata` VALUES (1000317,'NOT_SET',0,0,'2016-02-18 17:44:20'); +INSERT INTO `uniqueiddata` VALUES (1000318,'NOT_SET',0,0,'2016-02-18 17:44:29'); +INSERT INTO `uniqueiddata` VALUES (1000319,'NOT_SET',0,0,'2016-02-18 17:46:36'); +INSERT INTO `uniqueiddata` VALUES (1000320,'NOT_SET',0,0,'2016-02-18 17:46:46'); +INSERT INTO `uniqueiddata` VALUES (1000321,'NOT_SET',0,0,'2016-02-18 17:50:52'); +INSERT INTO `uniqueiddata` VALUES (1000322,'NOT_SET',0,0,'2016-02-18 17:50:59'); +INSERT INTO `uniqueiddata` VALUES (1000323,'NOT_SET',0,0,'2016-02-18 20:27:42'); +INSERT INTO `uniqueiddata` VALUES (1000324,'NOT_SET',0,0,'2016-02-18 20:28:24'); +INSERT INTO `uniqueiddata` VALUES (1000325,'NOT_SET',0,0,'2016-02-18 20:28:41'); +INSERT INTO `uniqueiddata` VALUES (1000326,'NOT_SET',0,0,'2016-02-18 20:30:27'); +INSERT INTO `uniqueiddata` VALUES (1000327,'NOT_SET',0,0,'2016-02-18 22:45:38'); +INSERT INTO `uniqueiddata` VALUES (1000328,'NOT_SET',0,0,'2016-02-18 22:46:11'); +INSERT INTO `uniqueiddata` VALUES (1000329,'NOT_SET',0,0,'2016-02-18 22:47:38'); +INSERT INTO `uniqueiddata` VALUES (1000330,'NOT_SET',0,0,'2016-02-18 22:47:50'); +INSERT INTO `uniqueiddata` VALUES (1000331,'NOT_SET',0,0,'2016-06-02 21:29:07'); +INSERT INTO `uniqueiddata` VALUES (1000332,'NOT_SET',0,0,'2016-06-02 21:29:07'); +INSERT INTO `uniqueiddata` VALUES (1000333,'NOT_SET',0,0,'2016-06-02 21:29:07'); +INSERT INTO `uniqueiddata` VALUES (1000334,'NOT_SET',0,0,'2016-06-02 21:29:07'); +INSERT INTO `uniqueiddata` VALUES (1000335,'NOT_SET',0,0,'2016-06-02 21:29:07'); +INSERT INTO `uniqueiddata` VALUES (1000336,'NOT_SET',0,0,'2016-06-02 21:31:00'); +INSERT INTO `uniqueiddata` VALUES (1000337,'NOT_SET',0,0,'2016-06-07 09:43:05'); +INSERT INTO `uniqueiddata` VALUES (1000338,'NOT_SET',0,0,'2016-06-07 09:44:38'); +INSERT INTO `uniqueiddata` VALUES (1000339,'NOT_SET',0,0,'2016-06-07 09:46:36'); +INSERT INTO `uniqueiddata` VALUES (1000340,'NOT_SET',0,0,'2016-06-07 10:04:57'); +INSERT INTO `uniqueiddata` VALUES (1000341,'NOT_SET',0,0,'2016-06-12 12:38:23'); +INSERT INTO `uniqueiddata` VALUES (1000342,'NOT_SET',0,0,'2016-06-12 12:38:23'); +INSERT INTO `uniqueiddata` VALUES (1000343,'NOT_SET',0,0,'2016-06-12 12:38:23'); +INSERT INTO `uniqueiddata` VALUES (1000344,'NOT_SET',0,0,'2016-06-12 12:38:23'); +INSERT INTO `uniqueiddata` VALUES (1000345,'NOT_SET',0,0,'2016-06-12 12:38:23'); +INSERT INTO `uniqueiddata` VALUES (1000346,'NOT_SET',0,0,'2016-06-12 14:09:36'); +INSERT INTO `uniqueiddata` VALUES (1000347,'NOT_SET',0,0,'2016-06-12 14:09:36'); +INSERT INTO `uniqueiddata` VALUES (1000348,'NOT_SET',0,0,'2016-06-12 14:09:36'); +INSERT INTO `uniqueiddata` VALUES (1000349,'NOT_SET',0,0,'2016-06-12 14:09:36'); +INSERT INTO `uniqueiddata` VALUES (1000350,'NOT_SET',0,0,'2016-06-12 14:09:36'); +INSERT INTO `uniqueiddata` VALUES (1000351,'NOT_SET',0,0,'2016-06-12 14:14:55'); +INSERT INTO `uniqueiddata` VALUES (1000352,'NOT_SET',0,0,'2016-06-14 10:55:22'); +INSERT INTO `uniqueiddata` VALUES (1000353,'NOT_SET',0,0,'2016-06-14 10:55:22'); +INSERT INTO `uniqueiddata` VALUES (1000354,'NOT_SET',0,0,'2016-06-14 10:55:22'); +INSERT INTO `uniqueiddata` VALUES (1000355,'NOT_SET',0,0,'2016-06-14 10:55:22'); +INSERT INTO `uniqueiddata` VALUES (1000356,'NOT_SET',0,0,'2016-06-14 10:55:22'); +INSERT INTO `uniqueiddata` VALUES (1000357,'NOT_SET',0,0,'2016-06-14 10:57:31'); +INSERT INTO `uniqueiddata` VALUES (1000358,'NOT_SET',0,0,'2016-06-24 17:11:46'); +INSERT INTO `uniqueiddata` VALUES (1000359,'NOT_SET',0,0,'2016-06-24 17:11:46'); +INSERT INTO `uniqueiddata` VALUES (1000360,'NOT_SET',0,0,'2016-06-24 17:11:46'); +INSERT INTO `uniqueiddata` VALUES (1000361,'NOT_SET',0,0,'2016-06-24 17:11:46'); +INSERT INTO `uniqueiddata` VALUES (1000362,'NOT_SET',0,0,'2016-06-24 17:11:46'); +INSERT INTO `uniqueiddata` VALUES (1000363,'NOT_SET',0,0,'2016-07-03 23:42:26'); +INSERT INTO `uniqueiddata` VALUES (1000364,'NOT_SET',0,0,'2016-07-03 23:42:26'); +INSERT INTO `uniqueiddata` VALUES (1000365,'NOT_SET',0,0,'2016-07-03 23:42:26'); +INSERT INTO `uniqueiddata` VALUES (1000366,'NOT_SET',0,0,'2016-07-03 23:42:26'); +INSERT INTO `uniqueiddata` VALUES (1000367,'NOT_SET',0,0,'2016-07-03 23:42:26'); +INSERT INTO `uniqueiddata` VALUES (1000368,'NOT_SET',0,0,'2016-07-03 23:44:12'); +INSERT INTO `uniqueiddata` VALUES (1000369,'NOT_SET',0,0,'2016-07-03 23:50:02'); +INSERT INTO `uniqueiddata` VALUES (1000370,'NOT_SET',0,0,'2016-07-03 23:50:02'); +INSERT INTO `uniqueiddata` VALUES (1000371,'NOT_SET',0,0,'2016-07-03 23:50:02'); +INSERT INTO `uniqueiddata` VALUES (1000372,'NOT_SET',0,0,'2016-07-03 23:50:02'); +INSERT INTO `uniqueiddata` VALUES (1000373,'NOT_SET',0,0,'2016-07-03 23:50:02'); +INSERT INTO `uniqueiddata` VALUES (1000374,'NOT_SET',0,0,'2016-07-05 18:50:51'); +INSERT INTO `uniqueiddata` VALUES (1000375,'NOT_SET',0,0,'2016-07-05 23:11:55'); +INSERT INTO `uniqueiddata` VALUES (1000376,'NOT_SET',0,0,'2016-07-05 23:11:55'); +INSERT INTO `uniqueiddata` VALUES (1000377,'NOT_SET',0,0,'2016-07-05 23:11:55'); +INSERT INTO `uniqueiddata` VALUES (1000378,'NOT_SET',0,0,'2016-07-05 23:11:55'); +INSERT INTO `uniqueiddata` VALUES (1000379,'NOT_SET',0,0,'2016-07-05 23:11:55'); +INSERT INTO `uniqueiddata` VALUES (1000380,'NOT_SET',0,0,'2016-07-05 23:15:14'); +INSERT INTO `uniqueiddata` VALUES (1000381,'NOT_SET',0,0,'2016-07-06 08:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000382,'NOT_SET',0,0,'2016-07-06 08:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000383,'NOT_SET',0,0,'2016-07-06 08:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000384,'NOT_SET',0,0,'2016-07-06 08:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000385,'NOT_SET',0,0,'2016-07-06 08:57:30'); +INSERT INTO `uniqueiddata` VALUES (1000386,'NOT_SET',0,0,'2016-07-06 08:58:55'); +INSERT INTO `uniqueiddata` VALUES (1000387,'NOT_SET',0,0,'2016-07-06 14:24:09'); +INSERT INTO `uniqueiddata` VALUES (1000388,'NOT_SET',0,0,'2016-07-06 14:24:09'); +INSERT INTO `uniqueiddata` VALUES (1000389,'NOT_SET',0,0,'2016-07-06 14:24:09'); +INSERT INTO `uniqueiddata` VALUES (1000390,'NOT_SET',0,0,'2016-07-06 14:24:09'); +INSERT INTO `uniqueiddata` VALUES (1000391,'NOT_SET',0,0,'2016-07-06 14:24:09'); +INSERT INTO `uniqueiddata` VALUES (1000392,'NOT_SET',0,0,'2016-07-06 14:25:57'); +INSERT INTO `uniqueiddata` VALUES (1000393,'NOT_SET',0,0,'2016-07-07 01:23:56'); +INSERT INTO `uniqueiddata` VALUES (1000394,'NOT_SET',0,0,'2016-07-07 01:23:56'); +INSERT INTO `uniqueiddata` VALUES (1000395,'NOT_SET',0,0,'2016-07-07 01:23:56'); +INSERT INTO `uniqueiddata` VALUES (1000396,'NOT_SET',0,0,'2016-07-07 01:23:56'); +INSERT INTO `uniqueiddata` VALUES (1000397,'NOT_SET',0,0,'2016-07-07 01:23:56'); +INSERT INTO `uniqueiddata` VALUES (1000398,'NOT_SET',0,0,'2016-07-07 01:45:10'); +INSERT INTO `uniqueiddata` VALUES (1000399,'NOT_SET',0,0,'2016-07-07 01:51:59'); +INSERT INTO `uniqueiddata` VALUES (1000400,'NOT_SET',0,0,'2016-07-07 01:51:59'); +INSERT INTO `uniqueiddata` VALUES (1000401,'NOT_SET',0,0,'2016-07-07 01:51:59'); +INSERT INTO `uniqueiddata` VALUES (1000402,'NOT_SET',0,0,'2016-07-07 01:51:59'); +INSERT INTO `uniqueiddata` VALUES (1000403,'NOT_SET',0,0,'2016-07-07 01:51:59'); +INSERT INTO `uniqueiddata` VALUES (1000404,'NOT_SET',0,0,'2016-07-07 01:53:28'); +INSERT INTO `uniqueiddata` VALUES (1000405,'NOT_SET',0,0,'2016-07-08 01:21:37'); +INSERT INTO `uniqueiddata` VALUES (1000406,'NOT_SET',0,0,'2016-07-08 01:21:37'); +INSERT INTO `uniqueiddata` VALUES (1000407,'NOT_SET',0,0,'2016-07-08 01:21:37'); +INSERT INTO `uniqueiddata` VALUES (1000408,'NOT_SET',0,0,'2016-07-08 01:21:37'); +INSERT INTO `uniqueiddata` VALUES (1000409,'NOT_SET',0,0,'2016-07-08 01:21:37'); +INSERT INTO `uniqueiddata` VALUES (1000410,'NOT_SET',0,0,'2016-07-08 01:23:17'); +INSERT INTO `uniqueiddata` VALUES (1000411,'NOT_SET',0,0,'2016-07-14 10:19:45'); +INSERT INTO `uniqueiddata` VALUES (1000412,'NOT_SET',0,0,'2016-07-14 10:19:45'); +INSERT INTO `uniqueiddata` VALUES (1000413,'NOT_SET',0,0,'2016-07-14 10:19:45'); +INSERT INTO `uniqueiddata` VALUES (1000414,'NOT_SET',0,0,'2016-07-14 10:19:45'); +INSERT INTO `uniqueiddata` VALUES (1000415,'NOT_SET',0,0,'2016-07-14 10:19:45'); +INSERT INTO `uniqueiddata` VALUES (1000416,'NOT_SET',0,0,'2016-07-14 10:21:22'); +INSERT INTO `uniqueiddata` VALUES (1000417,'NOT_SET',0,0,'2016-07-14 13:19:59'); +INSERT INTO `uniqueiddata` VALUES (1000418,'NOT_SET',0,0,'2016-07-14 13:19:59'); +INSERT INTO `uniqueiddata` VALUES (1000419,'NOT_SET',0,0,'2016-07-14 13:19:59'); +INSERT INTO `uniqueiddata` VALUES (1000420,'NOT_SET',0,0,'2016-07-14 13:19:59'); +INSERT INTO `uniqueiddata` VALUES (1000421,'NOT_SET',0,0,'2016-07-14 13:19:59'); +INSERT INTO `uniqueiddata` VALUES (1000422,'NOT_SET',0,0,'2016-07-14 13:22:36'); +INSERT INTO `uniqueiddata` VALUES (1000423,'NOT_SET',0,0,'2016-07-14 13:27:51'); +INSERT INTO `uniqueiddata` VALUES (1000424,'NOT_SET',0,0,'2016-07-14 13:27:51'); +INSERT INTO `uniqueiddata` VALUES (1000425,'NOT_SET',0,0,'2016-07-14 13:27:51'); +INSERT INTO `uniqueiddata` VALUES (1000426,'NOT_SET',0,0,'2016-07-14 13:27:51'); +INSERT INTO `uniqueiddata` VALUES (1000427,'NOT_SET',0,0,'2016-07-14 13:27:51'); +INSERT INTO `uniqueiddata` VALUES (1000428,'NOT_SET',0,0,'2016-07-14 13:29:27'); +INSERT INTO `uniqueiddata` VALUES (1000429,'NOT_SET',0,0,'2016-07-20 16:28:04'); +INSERT INTO `uniqueiddata` VALUES (1000430,'NOT_SET',0,0,'2016-07-20 16:28:04'); +INSERT INTO `uniqueiddata` VALUES (1000431,'NOT_SET',0,0,'2016-07-20 16:28:04'); +INSERT INTO `uniqueiddata` VALUES (1000432,'NOT_SET',0,0,'2016-07-20 16:28:04'); +INSERT INTO `uniqueiddata` VALUES (1000433,'NOT_SET',0,0,'2016-07-20 16:28:04'); +INSERT INTO `uniqueiddata` VALUES (1000434,'NOT_SET',0,0,'2016-07-20 17:16:05'); +INSERT INTO `uniqueiddata` VALUES (1000435,'NOT_SET',0,0,'2016-07-23 20:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000436,'NOT_SET',0,0,'2016-07-23 20:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000437,'NOT_SET',0,0,'2016-07-23 20:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000438,'NOT_SET',0,0,'2016-07-23 20:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000439,'NOT_SET',0,0,'2016-07-23 20:32:11'); +INSERT INTO `uniqueiddata` VALUES (1000440,'NOT_SET',0,0,'2016-07-23 20:54:37'); +INSERT INTO `uniqueiddata` VALUES (1000441,'NOT_SET',0,0,'2016-07-28 20:18:23'); +INSERT INTO `uniqueiddata` VALUES (1000442,'NOT_SET',0,0,'2016-07-28 20:18:23'); +INSERT INTO `uniqueiddata` VALUES (1000443,'NOT_SET',0,0,'2016-07-28 20:18:23'); +INSERT INTO `uniqueiddata` VALUES (1000444,'NOT_SET',0,0,'2016-07-28 20:18:23'); +INSERT INTO `uniqueiddata` VALUES (1000445,'NOT_SET',0,0,'2016-07-28 20:18:23'); +INSERT INTO `uniqueiddata` VALUES (1000446,'NOT_SET',0,0,'2016-07-28 20:20:56'); +/*!40000 ALTER TABLE `uniqueiddata` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:16 diff --git a/sql/update.sql b/sql/update.sql new file mode 100644 index 00000000..3f748514 --- /dev/null +++ b/sql/update.sql @@ -0,0 +1,27 @@ +RENAME TABLE `dbaccounts` TO `accounts`; +RENAME TABLE `dbbattlenpc` TO `battlenpc`; +RENAME TABLE `dbchara` TO `charabase`; +RENAME TABLE `dbcharapcclass` TO `characlass`; +RENAME TABLE `dbcharapc` TO `charadetail`; +RENAME TABLE `dbcharaitems` TO `charaglobalitem`; +RENAME TABLE `dbcharainfoblacklist` TO `charainfoblacklist`; +RENAME TABLE `dbcharainfofriendlist` TO `charainfofriendlist`; +RENAME TABLE `dbcharainfolinkshell` TO `charainfolinkshell`; +RENAME TABLE `dbcharainfoprofile` TO `charainfosearch`; +RENAME TABLE `dbcharaitemcrystal` TO `charaitemcrystal`; +RENAME TABLE `dbcharaitemcurrency` TO `charaitemcurrency`; +RENAME TABLE `dbcharaitemmannequin` TO `charaitemgearset`; +RENAME TABLE `dbcharaitembaggage` TO `charaiteminventory`; +RENAME TABLE `dbcharaquest` TO `charaquest`; +RENAME TABLE `dbdiscoveryref` TO `discoveryinfo`; +RENAME TABLE `dbinfolinkshell` TO `infolinkshell`; +RENAME TABLE `dbuniqueiddata` TO `uniqueiddata`; +RENAME TABLE `dbzonemapping` TO `zonemapping`; +RENAME TABLE `dbzonepositions` TO `zonepositions`; +RENAME TABLE `dbzoneservers` TO `zoneservers`; + +ALTER TABLE `charaiteminventory` ADD `container_25` INT(20) NOT NULL AFTER `container_24`, ADD `container_26` INT(20) NOT NULL AFTER `container_25`, ADD `container_27` INT(20) NOT NULL AFTER `container_26`, ADD `container_28` INT(20) NOT NULL AFTER `container_27`, ADD `container_29` INT(20) NOT NULL AFTER `container_28`, ADD `container_30` INT(20) NOT NULL AFTER `container_29`, ADD `container_31` INT(20) NOT NULL AFTER `container_30`, ADD `container_32` INT(20) NOT NULL AFTER `container_31`, ADD `container_33` INT(20) NOT NULL AFTER `container_32`, ADD `container_34` INT(20) NOT NULL AFTER `container_33`; + +ALTER TABLE `characlass` ADD `Lv_24` INT(5) NOT NULL DEFAULT '0' AFTER `Exp_23`, ADD `Exp_24` INT(10) NOT NULL DEFAULT '0' AFTER `Lv_24`, ADD `Lv_25` INT(5) NOT NULL DEFAULT '0' AFTER `Exp_24`, ADD `Exp_25` INT(19) NOT NULL DEFAULT '0' AFTER `Lv_25`; + +ALTER TABLE `charadetail` CHANGE `Aetheryte` `Aetheryte` BINARY(16) NULL DEFAULT NULL, CHANGE `HowTo` `HowTo` BINARY(33) NULL DEFAULT NULL, CHANGE `Minions` `Minions` BINARY(33) NULL DEFAULT NULL, CHANGE `Mounts` `Mounts` BINARY(13) NULL DEFAULT NULL; \ No newline at end of file diff --git a/sql/zonemapping.sql b/sql/zonemapping.sql new file mode 100644 index 00000000..34dfc581 --- /dev/null +++ b/sql/zonemapping.sql @@ -0,0 +1,525 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `zonemapping` +-- + +DROP TABLE IF EXISTS `zonemapping`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `zonemapping` ( + `serverid` int(3) NOT NULL, + `zoneid` int(3) NOT NULL, + `flags` int(19) DEFAULT NULL, + PRIMARY KEY (`zoneid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `zonemapping` +-- + +LOCK TABLES `zonemapping` WRITE; +/*!40000 ALTER TABLE `zonemapping` DISABLE KEYS */; +INSERT INTO `zonemapping` VALUES (100,1,0); +INSERT INTO `zonemapping` VALUES (100,2,0); +INSERT INTO `zonemapping` VALUES (100,3,0); +INSERT INTO `zonemapping` VALUES (100,4,0); +INSERT INTO `zonemapping` VALUES (100,5,0); +INSERT INTO `zonemapping` VALUES (100,6,0); +INSERT INTO `zonemapping` VALUES (100,7,0); +INSERT INTO `zonemapping` VALUES (100,8,0); +INSERT INTO `zonemapping` VALUES (100,9,0); +INSERT INTO `zonemapping` VALUES (100,10,0); +INSERT INTO `zonemapping` VALUES (100,11,0); +INSERT INTO `zonemapping` VALUES (100,13,0); +INSERT INTO `zonemapping` VALUES (100,14,0); +INSERT INTO `zonemapping` VALUES (100,15,0); +INSERT INTO `zonemapping` VALUES (100,16,0); +INSERT INTO `zonemapping` VALUES (100,17,0); +INSERT INTO `zonemapping` VALUES (100,18,0); +INSERT INTO `zonemapping` VALUES (100,19,0); +INSERT INTO `zonemapping` VALUES (100,20,0); +INSERT INTO `zonemapping` VALUES (100,21,0); +INSERT INTO `zonemapping` VALUES (100,22,0); +INSERT INTO `zonemapping` VALUES (100,24,0); +INSERT INTO `zonemapping` VALUES (100,25,0); +INSERT INTO `zonemapping` VALUES (100,26,0); +INSERT INTO `zonemapping` VALUES (100,27,0); +INSERT INTO `zonemapping` VALUES (100,28,0); +INSERT INTO `zonemapping` VALUES (100,128,0); +INSERT INTO `zonemapping` VALUES (100,129,0); +INSERT INTO `zonemapping` VALUES (100,130,0); +INSERT INTO `zonemapping` VALUES (100,131,0); +INSERT INTO `zonemapping` VALUES (100,132,0); +INSERT INTO `zonemapping` VALUES (100,133,0); +INSERT INTO `zonemapping` VALUES (100,134,0); +INSERT INTO `zonemapping` VALUES (100,135,0); +INSERT INTO `zonemapping` VALUES (100,136,0); +INSERT INTO `zonemapping` VALUES (100,137,0); +INSERT INTO `zonemapping` VALUES (100,138,0); +INSERT INTO `zonemapping` VALUES (100,139,0); +INSERT INTO `zonemapping` VALUES (100,140,0); +INSERT INTO `zonemapping` VALUES (100,141,0); +INSERT INTO `zonemapping` VALUES (100,142,0); +INSERT INTO `zonemapping` VALUES (100,143,0); +INSERT INTO `zonemapping` VALUES (100,144,0); +INSERT INTO `zonemapping` VALUES (100,145,0); +INSERT INTO `zonemapping` VALUES (100,146,0); +INSERT INTO `zonemapping` VALUES (100,147,0); +INSERT INTO `zonemapping` VALUES (100,148,0); +INSERT INTO `zonemapping` VALUES (100,149,0); +INSERT INTO `zonemapping` VALUES (100,150,0); +INSERT INTO `zonemapping` VALUES (100,151,0); +INSERT INTO `zonemapping` VALUES (100,152,0); +INSERT INTO `zonemapping` VALUES (100,153,0); +INSERT INTO `zonemapping` VALUES (100,154,0); +INSERT INTO `zonemapping` VALUES (100,155,0); +INSERT INTO `zonemapping` VALUES (100,156,0); +INSERT INTO `zonemapping` VALUES (100,157,0); +INSERT INTO `zonemapping` VALUES (100,158,0); +INSERT INTO `zonemapping` VALUES (100,159,0); +INSERT INTO `zonemapping` VALUES (100,160,0); +INSERT INTO `zonemapping` VALUES (100,161,0); +INSERT INTO `zonemapping` VALUES (100,162,0); +INSERT INTO `zonemapping` VALUES (100,163,0); +INSERT INTO `zonemapping` VALUES (100,164,0); +INSERT INTO `zonemapping` VALUES (100,166,0); +INSERT INTO `zonemapping` VALUES (100,167,0); +INSERT INTO `zonemapping` VALUES (100,168,0); +INSERT INTO `zonemapping` VALUES (100,169,0); +INSERT INTO `zonemapping` VALUES (100,170,0); +INSERT INTO `zonemapping` VALUES (100,171,0); +INSERT INTO `zonemapping` VALUES (100,172,0); +INSERT INTO `zonemapping` VALUES (100,174,0); +INSERT INTO `zonemapping` VALUES (100,175,0); +INSERT INTO `zonemapping` VALUES (100,176,0); +INSERT INTO `zonemapping` VALUES (100,177,0); +INSERT INTO `zonemapping` VALUES (100,178,0); +INSERT INTO `zonemapping` VALUES (100,179,0); +INSERT INTO `zonemapping` VALUES (100,180,0); +INSERT INTO `zonemapping` VALUES (100,181,0); +INSERT INTO `zonemapping` VALUES (100,182,0); +INSERT INTO `zonemapping` VALUES (100,183,0); +INSERT INTO `zonemapping` VALUES (100,184,0); +INSERT INTO `zonemapping` VALUES (100,185,0); +INSERT INTO `zonemapping` VALUES (100,186,0); +INSERT INTO `zonemapping` VALUES (100,187,0); +INSERT INTO `zonemapping` VALUES (100,188,0); +INSERT INTO `zonemapping` VALUES (100,189,0); +INSERT INTO `zonemapping` VALUES (100,190,0); +INSERT INTO `zonemapping` VALUES (100,191,0); +INSERT INTO `zonemapping` VALUES (100,192,0); +INSERT INTO `zonemapping` VALUES (100,193,0); +INSERT INTO `zonemapping` VALUES (100,194,0); +INSERT INTO `zonemapping` VALUES (100,195,0); +INSERT INTO `zonemapping` VALUES (100,196,0); +INSERT INTO `zonemapping` VALUES (100,197,0); +INSERT INTO `zonemapping` VALUES (100,198,0); +INSERT INTO `zonemapping` VALUES (100,199,0); +INSERT INTO `zonemapping` VALUES (100,200,0); +INSERT INTO `zonemapping` VALUES (100,201,0); +INSERT INTO `zonemapping` VALUES (100,202,0); +INSERT INTO `zonemapping` VALUES (100,203,0); +INSERT INTO `zonemapping` VALUES (100,204,0); +INSERT INTO `zonemapping` VALUES (100,205,0); +INSERT INTO `zonemapping` VALUES (100,206,0); +INSERT INTO `zonemapping` VALUES (100,207,0); +INSERT INTO `zonemapping` VALUES (100,208,0); +INSERT INTO `zonemapping` VALUES (100,209,0); +INSERT INTO `zonemapping` VALUES (100,210,0); +INSERT INTO `zonemapping` VALUES (100,211,0); +INSERT INTO `zonemapping` VALUES (100,212,0); +INSERT INTO `zonemapping` VALUES (100,213,0); +INSERT INTO `zonemapping` VALUES (100,214,0); +INSERT INTO `zonemapping` VALUES (100,215,0); +INSERT INTO `zonemapping` VALUES (100,216,0); +INSERT INTO `zonemapping` VALUES (100,217,0); +INSERT INTO `zonemapping` VALUES (100,218,0); +INSERT INTO `zonemapping` VALUES (100,219,0); +INSERT INTO `zonemapping` VALUES (100,220,0); +INSERT INTO `zonemapping` VALUES (100,221,0); +INSERT INTO `zonemapping` VALUES (100,222,0); +INSERT INTO `zonemapping` VALUES (100,223,0); +INSERT INTO `zonemapping` VALUES (100,224,0); +INSERT INTO `zonemapping` VALUES (100,225,0); +INSERT INTO `zonemapping` VALUES (100,226,0); +INSERT INTO `zonemapping` VALUES (100,227,0); +INSERT INTO `zonemapping` VALUES (100,228,0); +INSERT INTO `zonemapping` VALUES (100,229,0); +INSERT INTO `zonemapping` VALUES (100,230,0); +INSERT INTO `zonemapping` VALUES (100,231,0); +INSERT INTO `zonemapping` VALUES (100,232,0); +INSERT INTO `zonemapping` VALUES (100,233,0); +INSERT INTO `zonemapping` VALUES (100,234,0); +INSERT INTO `zonemapping` VALUES (100,235,0); +INSERT INTO `zonemapping` VALUES (100,236,0); +INSERT INTO `zonemapping` VALUES (100,237,0); +INSERT INTO `zonemapping` VALUES (100,238,0); +INSERT INTO `zonemapping` VALUES (100,239,0); +INSERT INTO `zonemapping` VALUES (100,240,0); +INSERT INTO `zonemapping` VALUES (100,241,0); +INSERT INTO `zonemapping` VALUES (100,242,0); +INSERT INTO `zonemapping` VALUES (100,243,0); +INSERT INTO `zonemapping` VALUES (100,244,0); +INSERT INTO `zonemapping` VALUES (100,245,0); +INSERT INTO `zonemapping` VALUES (100,246,0); +INSERT INTO `zonemapping` VALUES (100,247,0); +INSERT INTO `zonemapping` VALUES (100,248,0); +INSERT INTO `zonemapping` VALUES (100,249,0); +INSERT INTO `zonemapping` VALUES (100,250,0); +INSERT INTO `zonemapping` VALUES (100,251,0); +INSERT INTO `zonemapping` VALUES (100,252,0); +INSERT INTO `zonemapping` VALUES (100,253,0); +INSERT INTO `zonemapping` VALUES (100,254,0); +INSERT INTO `zonemapping` VALUES (100,255,0); +INSERT INTO `zonemapping` VALUES (100,256,0); +INSERT INTO `zonemapping` VALUES (100,257,0); +INSERT INTO `zonemapping` VALUES (100,258,0); +INSERT INTO `zonemapping` VALUES (100,259,0); +INSERT INTO `zonemapping` VALUES (100,260,0); +INSERT INTO `zonemapping` VALUES (100,261,0); +INSERT INTO `zonemapping` VALUES (100,262,0); +INSERT INTO `zonemapping` VALUES (100,263,0); +INSERT INTO `zonemapping` VALUES (100,264,0); +INSERT INTO `zonemapping` VALUES (100,265,0); +INSERT INTO `zonemapping` VALUES (100,266,0); +INSERT INTO `zonemapping` VALUES (100,267,0); +INSERT INTO `zonemapping` VALUES (100,268,0); +INSERT INTO `zonemapping` VALUES (100,269,0); +INSERT INTO `zonemapping` VALUES (100,270,0); +INSERT INTO `zonemapping` VALUES (100,271,0); +INSERT INTO `zonemapping` VALUES (100,272,0); +INSERT INTO `zonemapping` VALUES (100,273,0); +INSERT INTO `zonemapping` VALUES (100,274,0); +INSERT INTO `zonemapping` VALUES (100,275,0); +INSERT INTO `zonemapping` VALUES (100,276,0); +INSERT INTO `zonemapping` VALUES (100,277,0); +INSERT INTO `zonemapping` VALUES (100,278,0); +INSERT INTO `zonemapping` VALUES (100,279,0); +INSERT INTO `zonemapping` VALUES (100,280,0); +INSERT INTO `zonemapping` VALUES (100,281,0); +INSERT INTO `zonemapping` VALUES (100,282,0); +INSERT INTO `zonemapping` VALUES (100,283,0); +INSERT INTO `zonemapping` VALUES (100,284,0); +INSERT INTO `zonemapping` VALUES (100,285,0); +INSERT INTO `zonemapping` VALUES (100,286,0); +INSERT INTO `zonemapping` VALUES (100,287,0); +INSERT INTO `zonemapping` VALUES (100,288,0); +INSERT INTO `zonemapping` VALUES (100,289,0); +INSERT INTO `zonemapping` VALUES (100,290,0); +INSERT INTO `zonemapping` VALUES (100,291,0); +INSERT INTO `zonemapping` VALUES (100,292,0); +INSERT INTO `zonemapping` VALUES (100,293,0); +INSERT INTO `zonemapping` VALUES (100,294,0); +INSERT INTO `zonemapping` VALUES (100,295,0); +INSERT INTO `zonemapping` VALUES (100,296,0); +INSERT INTO `zonemapping` VALUES (100,297,0); +INSERT INTO `zonemapping` VALUES (100,298,0); +INSERT INTO `zonemapping` VALUES (100,299,0); +INSERT INTO `zonemapping` VALUES (100,300,0); +INSERT INTO `zonemapping` VALUES (100,301,0); +INSERT INTO `zonemapping` VALUES (100,302,0); +INSERT INTO `zonemapping` VALUES (100,303,0); +INSERT INTO `zonemapping` VALUES (100,304,0); +INSERT INTO `zonemapping` VALUES (100,305,0); +INSERT INTO `zonemapping` VALUES (100,306,0); +INSERT INTO `zonemapping` VALUES (100,307,0); +INSERT INTO `zonemapping` VALUES (100,308,0); +INSERT INTO `zonemapping` VALUES (100,309,0); +INSERT INTO `zonemapping` VALUES (100,310,0); +INSERT INTO `zonemapping` VALUES (100,311,0); +INSERT INTO `zonemapping` VALUES (100,312,0); +INSERT INTO `zonemapping` VALUES (100,313,0); +INSERT INTO `zonemapping` VALUES (100,314,0); +INSERT INTO `zonemapping` VALUES (100,315,0); +INSERT INTO `zonemapping` VALUES (100,316,0); +INSERT INTO `zonemapping` VALUES (100,317,0); +INSERT INTO `zonemapping` VALUES (100,318,0); +INSERT INTO `zonemapping` VALUES (100,319,0); +INSERT INTO `zonemapping` VALUES (100,320,0); +INSERT INTO `zonemapping` VALUES (100,321,0); +INSERT INTO `zonemapping` VALUES (100,322,0); +INSERT INTO `zonemapping` VALUES (100,323,0); +INSERT INTO `zonemapping` VALUES (100,324,0); +INSERT INTO `zonemapping` VALUES (100,325,0); +INSERT INTO `zonemapping` VALUES (100,326,0); +INSERT INTO `zonemapping` VALUES (100,327,0); +INSERT INTO `zonemapping` VALUES (100,328,0); +INSERT INTO `zonemapping` VALUES (100,329,0); +INSERT INTO `zonemapping` VALUES (100,330,0); +INSERT INTO `zonemapping` VALUES (100,331,0); +INSERT INTO `zonemapping` VALUES (100,332,0); +INSERT INTO `zonemapping` VALUES (100,333,0); +INSERT INTO `zonemapping` VALUES (100,334,0); +INSERT INTO `zonemapping` VALUES (100,335,0); +INSERT INTO `zonemapping` VALUES (100,336,0); +INSERT INTO `zonemapping` VALUES (100,337,0); +INSERT INTO `zonemapping` VALUES (100,338,0); +INSERT INTO `zonemapping` VALUES (100,339,0); +INSERT INTO `zonemapping` VALUES (100,340,0); +INSERT INTO `zonemapping` VALUES (100,341,0); +INSERT INTO `zonemapping` VALUES (100,342,0); +INSERT INTO `zonemapping` VALUES (100,343,0); +INSERT INTO `zonemapping` VALUES (100,344,0); +INSERT INTO `zonemapping` VALUES (100,345,0); +INSERT INTO `zonemapping` VALUES (100,346,0); +INSERT INTO `zonemapping` VALUES (100,347,0); +INSERT INTO `zonemapping` VALUES (100,348,0); +INSERT INTO `zonemapping` VALUES (100,349,0); +INSERT INTO `zonemapping` VALUES (100,350,0); +INSERT INTO `zonemapping` VALUES (100,351,0); +INSERT INTO `zonemapping` VALUES (100,352,0); +INSERT INTO `zonemapping` VALUES (100,353,0); +INSERT INTO `zonemapping` VALUES (100,354,0); +INSERT INTO `zonemapping` VALUES (100,355,0); +INSERT INTO `zonemapping` VALUES (100,356,0); +INSERT INTO `zonemapping` VALUES (100,357,0); +INSERT INTO `zonemapping` VALUES (100,358,0); +INSERT INTO `zonemapping` VALUES (100,359,0); +INSERT INTO `zonemapping` VALUES (100,360,0); +INSERT INTO `zonemapping` VALUES (100,361,0); +INSERT INTO `zonemapping` VALUES (100,362,0); +INSERT INTO `zonemapping` VALUES (100,363,0); +INSERT INTO `zonemapping` VALUES (100,364,0); +INSERT INTO `zonemapping` VALUES (100,365,0); +INSERT INTO `zonemapping` VALUES (100,366,0); +INSERT INTO `zonemapping` VALUES (100,367,0); +INSERT INTO `zonemapping` VALUES (100,368,0); +INSERT INTO `zonemapping` VALUES (100,369,0); +INSERT INTO `zonemapping` VALUES (100,370,0); +INSERT INTO `zonemapping` VALUES (100,371,0); +INSERT INTO `zonemapping` VALUES (100,372,0); +INSERT INTO `zonemapping` VALUES (100,373,0); +INSERT INTO `zonemapping` VALUES (100,374,0); +INSERT INTO `zonemapping` VALUES (100,375,0); +INSERT INTO `zonemapping` VALUES (100,376,0); +INSERT INTO `zonemapping` VALUES (100,377,0); +INSERT INTO `zonemapping` VALUES (100,378,0); +INSERT INTO `zonemapping` VALUES (100,379,0); +INSERT INTO `zonemapping` VALUES (100,380,0); +INSERT INTO `zonemapping` VALUES (100,381,0); +INSERT INTO `zonemapping` VALUES (100,382,0); +INSERT INTO `zonemapping` VALUES (100,383,0); +INSERT INTO `zonemapping` VALUES (100,384,0); +INSERT INTO `zonemapping` VALUES (100,385,0); +INSERT INTO `zonemapping` VALUES (100,386,0); +INSERT INTO `zonemapping` VALUES (100,387,0); +INSERT INTO `zonemapping` VALUES (100,388,0); +INSERT INTO `zonemapping` VALUES (100,389,0); +INSERT INTO `zonemapping` VALUES (100,390,0); +INSERT INTO `zonemapping` VALUES (100,391,0); +INSERT INTO `zonemapping` VALUES (100,392,0); +INSERT INTO `zonemapping` VALUES (100,393,0); +INSERT INTO `zonemapping` VALUES (100,394,0); +INSERT INTO `zonemapping` VALUES (100,395,0); +INSERT INTO `zonemapping` VALUES (100,396,0); +INSERT INTO `zonemapping` VALUES (100,397,0); +INSERT INTO `zonemapping` VALUES (100,398,0); +INSERT INTO `zonemapping` VALUES (100,399,0); +INSERT INTO `zonemapping` VALUES (100,400,0); +INSERT INTO `zonemapping` VALUES (100,401,0); +INSERT INTO `zonemapping` VALUES (100,402,0); +INSERT INTO `zonemapping` VALUES (100,404,0); +INSERT INTO `zonemapping` VALUES (100,405,0); +INSERT INTO `zonemapping` VALUES (100,406,0); +INSERT INTO `zonemapping` VALUES (100,407,0); +INSERT INTO `zonemapping` VALUES (100,408,0); +INSERT INTO `zonemapping` VALUES (100,409,0); +INSERT INTO `zonemapping` VALUES (100,410,0); +INSERT INTO `zonemapping` VALUES (100,411,0); +INSERT INTO `zonemapping` VALUES (100,412,0); +INSERT INTO `zonemapping` VALUES (100,413,0); +INSERT INTO `zonemapping` VALUES (100,414,0); +INSERT INTO `zonemapping` VALUES (100,415,0); +INSERT INTO `zonemapping` VALUES (100,416,0); +INSERT INTO `zonemapping` VALUES (100,417,0); +INSERT INTO `zonemapping` VALUES (100,418,0); +INSERT INTO `zonemapping` VALUES (100,419,0); +INSERT INTO `zonemapping` VALUES (100,420,0); +INSERT INTO `zonemapping` VALUES (100,421,0); +INSERT INTO `zonemapping` VALUES (100,422,0); +INSERT INTO `zonemapping` VALUES (100,423,0); +INSERT INTO `zonemapping` VALUES (100,424,0); +INSERT INTO `zonemapping` VALUES (100,425,0); +INSERT INTO `zonemapping` VALUES (100,426,0); +INSERT INTO `zonemapping` VALUES (100,427,0); +INSERT INTO `zonemapping` VALUES (100,428,0); +INSERT INTO `zonemapping` VALUES (100,429,0); +INSERT INTO `zonemapping` VALUES (100,430,0); +INSERT INTO `zonemapping` VALUES (100,431,0); +INSERT INTO `zonemapping` VALUES (100,432,0); +INSERT INTO `zonemapping` VALUES (100,433,0); +INSERT INTO `zonemapping` VALUES (100,434,0); +INSERT INTO `zonemapping` VALUES (100,435,0); +INSERT INTO `zonemapping` VALUES (100,436,0); +INSERT INTO `zonemapping` VALUES (100,437,0); +INSERT INTO `zonemapping` VALUES (100,438,0); +INSERT INTO `zonemapping` VALUES (100,439,0); +INSERT INTO `zonemapping` VALUES (100,440,0); +INSERT INTO `zonemapping` VALUES (100,441,0); +INSERT INTO `zonemapping` VALUES (100,442,0); +INSERT INTO `zonemapping` VALUES (100,443,0); +INSERT INTO `zonemapping` VALUES (100,444,0); +INSERT INTO `zonemapping` VALUES (100,445,0); +INSERT INTO `zonemapping` VALUES (100,446,0); +INSERT INTO `zonemapping` VALUES (100,447,0); +INSERT INTO `zonemapping` VALUES (100,448,0); +INSERT INTO `zonemapping` VALUES (100,449,0); +INSERT INTO `zonemapping` VALUES (100,450,0); +INSERT INTO `zonemapping` VALUES (100,451,0); +INSERT INTO `zonemapping` VALUES (100,452,0); +INSERT INTO `zonemapping` VALUES (100,453,0); +INSERT INTO `zonemapping` VALUES (100,454,0); +INSERT INTO `zonemapping` VALUES (100,455,0); +INSERT INTO `zonemapping` VALUES (100,456,0); +INSERT INTO `zonemapping` VALUES (100,457,0); +INSERT INTO `zonemapping` VALUES (100,458,0); +INSERT INTO `zonemapping` VALUES (100,459,0); +INSERT INTO `zonemapping` VALUES (100,460,0); +INSERT INTO `zonemapping` VALUES (100,461,0); +INSERT INTO `zonemapping` VALUES (100,462,0); +INSERT INTO `zonemapping` VALUES (100,463,0); +INSERT INTO `zonemapping` VALUES (100,464,0); +INSERT INTO `zonemapping` VALUES (100,465,0); +INSERT INTO `zonemapping` VALUES (100,466,0); +INSERT INTO `zonemapping` VALUES (100,467,0); +INSERT INTO `zonemapping` VALUES (100,468,0); +INSERT INTO `zonemapping` VALUES (100,469,0); +INSERT INTO `zonemapping` VALUES (100,470,0); +INSERT INTO `zonemapping` VALUES (100,471,0); +INSERT INTO `zonemapping` VALUES (100,472,0); +INSERT INTO `zonemapping` VALUES (100,473,0); +INSERT INTO `zonemapping` VALUES (100,474,0); +INSERT INTO `zonemapping` VALUES (100,475,0); +INSERT INTO `zonemapping` VALUES (100,476,0); +INSERT INTO `zonemapping` VALUES (100,477,0); +INSERT INTO `zonemapping` VALUES (100,478,0); +INSERT INTO `zonemapping` VALUES (100,479,0); +INSERT INTO `zonemapping` VALUES (100,480,0); +INSERT INTO `zonemapping` VALUES (100,481,0); +INSERT INTO `zonemapping` VALUES (100,482,0); +INSERT INTO `zonemapping` VALUES (100,483,0); +INSERT INTO `zonemapping` VALUES (100,484,0); +INSERT INTO `zonemapping` VALUES (100,485,0); +INSERT INTO `zonemapping` VALUES (100,486,0); +INSERT INTO `zonemapping` VALUES (100,487,0); +INSERT INTO `zonemapping` VALUES (100,488,0); +INSERT INTO `zonemapping` VALUES (100,489,0); +INSERT INTO `zonemapping` VALUES (100,490,0); +INSERT INTO `zonemapping` VALUES (100,491,0); +INSERT INTO `zonemapping` VALUES (100,492,0); +INSERT INTO `zonemapping` VALUES (100,493,0); +INSERT INTO `zonemapping` VALUES (100,494,0); +INSERT INTO `zonemapping` VALUES (100,495,0); +INSERT INTO `zonemapping` VALUES (100,496,0); +INSERT INTO `zonemapping` VALUES (100,497,0); +INSERT INTO `zonemapping` VALUES (100,498,0); +INSERT INTO `zonemapping` VALUES (100,499,0); +INSERT INTO `zonemapping` VALUES (100,500,0); +INSERT INTO `zonemapping` VALUES (100,501,0); +INSERT INTO `zonemapping` VALUES (100,502,0); +INSERT INTO `zonemapping` VALUES (100,503,0); +INSERT INTO `zonemapping` VALUES (100,504,0); +INSERT INTO `zonemapping` VALUES (100,505,0); +INSERT INTO `zonemapping` VALUES (100,506,0); +INSERT INTO `zonemapping` VALUES (100,507,0); +INSERT INTO `zonemapping` VALUES (100,508,0); +INSERT INTO `zonemapping` VALUES (100,509,0); +INSERT INTO `zonemapping` VALUES (100,510,0); +INSERT INTO `zonemapping` VALUES (100,511,0); +INSERT INTO `zonemapping` VALUES (100,512,0); +INSERT INTO `zonemapping` VALUES (100,513,0); +INSERT INTO `zonemapping` VALUES (100,514,0); +INSERT INTO `zonemapping` VALUES (100,515,0); +INSERT INTO `zonemapping` VALUES (100,516,0); +INSERT INTO `zonemapping` VALUES (100,517,0); +INSERT INTO `zonemapping` VALUES (100,518,0); +INSERT INTO `zonemapping` VALUES (100,519,0); +INSERT INTO `zonemapping` VALUES (100,520,0); +INSERT INTO `zonemapping` VALUES (100,521,0); +INSERT INTO `zonemapping` VALUES (100,522,0); +INSERT INTO `zonemapping` VALUES (100,523,0); +INSERT INTO `zonemapping` VALUES (100,524,0); +INSERT INTO `zonemapping` VALUES (100,525,0); +INSERT INTO `zonemapping` VALUES (100,526,0); +INSERT INTO `zonemapping` VALUES (100,527,0); +INSERT INTO `zonemapping` VALUES (100,528,0); +INSERT INTO `zonemapping` VALUES (100,529,0); +INSERT INTO `zonemapping` VALUES (100,530,0); +INSERT INTO `zonemapping` VALUES (100,531,0); +INSERT INTO `zonemapping` VALUES (100,532,0); +INSERT INTO `zonemapping` VALUES (100,533,0); +INSERT INTO `zonemapping` VALUES (100,534,0); +INSERT INTO `zonemapping` VALUES (100,535,0); +INSERT INTO `zonemapping` VALUES (100,536,0); +INSERT INTO `zonemapping` VALUES (100,537,0); +INSERT INTO `zonemapping` VALUES (100,538,0); +INSERT INTO `zonemapping` VALUES (100,539,0); +INSERT INTO `zonemapping` VALUES (100,540,0); +INSERT INTO `zonemapping` VALUES (100,541,0); +INSERT INTO `zonemapping` VALUES (100,542,0); +INSERT INTO `zonemapping` VALUES (100,543,0); +INSERT INTO `zonemapping` VALUES (100,544,0); +INSERT INTO `zonemapping` VALUES (100,545,0); +INSERT INTO `zonemapping` VALUES (100,546,0); +INSERT INTO `zonemapping` VALUES (100,547,0); +INSERT INTO `zonemapping` VALUES (100,548,0); +INSERT INTO `zonemapping` VALUES (100,549,0); +INSERT INTO `zonemapping` VALUES (100,550,0); +INSERT INTO `zonemapping` VALUES (100,551,0); +INSERT INTO `zonemapping` VALUES (100,552,0); +INSERT INTO `zonemapping` VALUES (100,553,0); +INSERT INTO `zonemapping` VALUES (100,554,0); +INSERT INTO `zonemapping` VALUES (100,555,0); +INSERT INTO `zonemapping` VALUES (100,556,0); +INSERT INTO `zonemapping` VALUES (100,557,0); +INSERT INTO `zonemapping` VALUES (100,558,0); +INSERT INTO `zonemapping` VALUES (100,559,0); +INSERT INTO `zonemapping` VALUES (100,560,0); +INSERT INTO `zonemapping` VALUES (100,561,0); +INSERT INTO `zonemapping` VALUES (100,562,0); +INSERT INTO `zonemapping` VALUES (100,563,0); +INSERT INTO `zonemapping` VALUES (100,564,0); +INSERT INTO `zonemapping` VALUES (100,565,0); +INSERT INTO `zonemapping` VALUES (100,566,0); +INSERT INTO `zonemapping` VALUES (100,567,0); +INSERT INTO `zonemapping` VALUES (100,568,0); +INSERT INTO `zonemapping` VALUES (100,569,0); +INSERT INTO `zonemapping` VALUES (100,570,0); +INSERT INTO `zonemapping` VALUES (100,571,0); +INSERT INTO `zonemapping` VALUES (100,572,0); +INSERT INTO `zonemapping` VALUES (100,573,0); +INSERT INTO `zonemapping` VALUES (100,574,0); +INSERT INTO `zonemapping` VALUES (100,575,0); +INSERT INTO `zonemapping` VALUES (100,576,0); +INSERT INTO `zonemapping` VALUES (100,577,0); +INSERT INTO `zonemapping` VALUES (100,578,0); +/*!40000 ALTER TABLE `zonemapping` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:16 diff --git a/sql/zonepositions.sql b/sql/zonepositions.sql new file mode 100644 index 00000000..8650cf59 --- /dev/null +++ b/sql/zonepositions.sql @@ -0,0 +1,165 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `zonepositions` +-- + +DROP TABLE IF EXISTS `zonepositions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `zonepositions` ( + `id` int(11) NOT NULL, + `target_zone_id` int(11) NOT NULL, + `pos_x` float NOT NULL, + `pos_y` float NOT NULL, + `pos_z` float NOT NULL, + `pos_o` float NOT NULL, + `radius` int(11) NOT NULL DEFAULT '2', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `zonepositions` +-- + +LOCK TABLES `zonepositions` WRITE; +/*!40000 ALTER TABLE `zonepositions` DISABLE KEYS */; +INSERT INTO `zonepositions` VALUES (1317556,133,16.3201,8.34547,-91.7458,2.48019,2); +INSERT INTO `zonepositions` VALUES (1317554,133,140.8,11.15,-22.6,2.5,2); +INSERT INTO `zonepositions` VALUES (1317535,132,10.2559,1.03707,-11.7784,-0.43944,2); +INSERT INTO `zonepositions` VALUES (1317558,133,-130.42,5.5,-36.36,-1.69019,2); +INSERT INTO `zonepositions` VALUES (1317552,148,129.341,26.9524,-311.06,-0.01836,2); +INSERT INTO `zonepositions` VALUES (1317623,132,149.42,-11.2378,157.749,-1.66604,2); +INSERT INTO `zonepositions` VALUES (1317628,152,-515.845,18.4252,271.573,2.83271,2); +INSERT INTO `zonepositions` VALUES (1320077,148,384,-3,-184,-1,2); +INSERT INTO `zonepositions` VALUES (1317540,132,-104,1,12.6,0.3,2); +INSERT INTO `zonepositions` VALUES (1320086,148,158,-24,546,-3,2); +INSERT INTO `zonepositions` VALUES (1317630,153,-366,29,-241,0.8,2); +INSERT INTO `zonepositions` VALUES (1332303,152,-165,6,450,-1.4,2); +INSERT INTO `zonepositions` VALUES (1320082,153,275.5,11.1,-258.7,-0.8,2); +INSERT INTO `zonepositions` VALUES (1317533,132,105,6,9,-0.6,2); +INSERT INTO `zonepositions` VALUES (1317542,154,452,-1,196,-1,2); +INSERT INTO `zonepositions` VALUES (1320072,133,-205,10,-96,1.6,2); +INSERT INTO `zonepositions` VALUES (1317633,154,14.8739,-55.3396,527.905,-2.44165,2); +INSERT INTO `zonepositions` VALUES (1359064,132,40.1767,1.19993,33.5491,-2.0572,2); +INSERT INTO `zonepositions` VALUES (1359244,152,-196.215,3.10004,291.934,1.0742,2); +INSERT INTO `zonepositions` VALUES (1359648,153,185.686,9.21862,-74.6898,-0.828473,2); +INSERT INTO `zonepositions` VALUES (1359652,154,-31.4935,-39.9006,238.436,-1.95462,2); +INSERT INTO `zonepositions` VALUES (3693843,129,-96.5371,18.5462,0.164331,1.55699,2); +INSERT INTO `zonepositions` VALUES (3693863,130,-144.305,-3.15489,-163.06,0.844608,2); +INSERT INTO `zonepositions` VALUES (3860373,135,156.876,14.0959,680.845,-3.01048,2); +INSERT INTO `zonepositions` VALUES (3961173,137,491.614,18.236,474.858,-2.71565,2); +INSERT INTO `zonepositions` VALUES (3965407,138,651.437,9.39925,507.082,-0.015805,2); +INSERT INTO `zonepositions` VALUES (4142002,139,437.484,4.21339,84.1072,0,2); +INSERT INTO `zonepositions` VALUES (4142062,180,-117.633,64.3225,-219.456,0,2); +INSERT INTO `zonepositions` VALUES (3965476,140,73.0305,45.9193,-232.058,-0.837415,2); +INSERT INTO `zonepositions` VALUES (3965498,145,-379.737,-59,142.563,-1.60992,2); +INSERT INTO `zonepositions` VALUES (3965549,146,-153.169,26.3166,-418.709,-0.966313,2); +INSERT INTO `zonepositions` VALUES (3965676,147,28.5353,6.97858,454.249,-1.59139,2); +INSERT INTO `zonepositions` VALUES (3965792,155,228.603,312,-238.728,-0.872663,2); +INSERT INTO `zonepositions` VALUES (3965896,156,47.7514,20.4912,-667.904,-1.5964,2); +INSERT INTO `zonepositions` VALUES (2563653,132,165.172,-2.53922,83.0344,2.28249,2); +INSERT INTO `zonepositions` VALUES (2563690,133,101.232,8.36029,-108.339,-1.72413,2); +INSERT INTO `zonepositions` VALUES (2563700,133,117.217,11.5772,-231.311,2.29494,2); +INSERT INTO `zonepositions` VALUES (2563702,133,-146.938,3.99984,-13.7873,-1.46085,2); +INSERT INTO `zonepositions` VALUES (2563740,133,-307.932,7.06028,-174.981,1.41482,2); +INSERT INTO `zonepositions` VALUES (2563748,133,-73.8652,6.99362,-136.568,1.13622,2); +INSERT INTO `zonepositions` VALUES (2563810,148,128.68,25.6247,-302.237,-0.407334,2); +INSERT INTO `zonepositions` VALUES (2563828,154,448.667,-0.881895,198.039,-0.81543,2); +INSERT INTO `zonepositions` VALUES (4205005,134,224,113.1,-261,0.71968,2); +INSERT INTO `zonepositions` VALUES (4205026,141,-16.1511,-1.87702,-163.139,3.13206,2); +INSERT INTO `zonepositions` VALUES (4265667,250,40.9851,5.6,-23.4832,0,2); +INSERT INTO `zonepositions` VALUES (3724283,148,-502.084,73.8739,-349.12,0.022136,2); +INSERT INTO `zonepositions` VALUES (1406089,155,7.46379,184.824,573.833,-2.9039,2); +INSERT INTO `zonepositions` VALUES (1406085,154,-366.571,-7.6982,194.777,0.759619,2); +INSERT INTO `zonepositions` VALUES (4176152,153,-282.699,-0.13973,692.715,2.57545,2); +INSERT INTO `zonepositions` VALUES (1320088,145,366.689,31.0121,-291.751,-0.526007,2); +INSERT INTO `zonepositions` VALUES (1406087,156,120.007,31.4998,-765.044,-0.804052,2); +INSERT INTO `zonepositions` VALUES (1418272,155,-228.277,218.179,698.528,-2.42958,2); +INSERT INTO `zonepositions` VALUES (1418277,147,-102.023,84.4271,-411.113,-0.874677,2); +INSERT INTO `zonepositions` VALUES (4295875,156,-421.317,-3.21682,-122.225,-2.80336,2); +INSERT INTO `zonepositions` VALUES (3876614,134,-36.4611,36.6508,150.243,1.98843,2); +INSERT INTO `zonepositions` VALUES (2464045,129,58.7886,20,-0.066879,-1.53495,2); +INSERT INTO `zonepositions` VALUES (2453662,134,194.511,65.2717,285.229,-1.59811,2); +INSERT INTO `zonepositions` VALUES (2464048,135,235.118,73.7873,-338.534,0.887104,2); +INSERT INTO `zonepositions` VALUES (2453729,134,-372.325,33.3472,-595.069,0.942594,2); +INSERT INTO `zonepositions` VALUES (2464054,138,810.028,49.9019,384.635,-2.54678,2); +INSERT INTO `zonepositions` VALUES (2210360,135,-46.1092,73.9411,116.089,1.54535,2); +INSERT INTO `zonepositions` VALUES (2443382,128,24.9766,44.5,175.56,-3.13474,2); +INSERT INTO `zonepositions` VALUES (4323017,135,596.704,61.6635,-112.685,-2.81539,2); +INSERT INTO `zonepositions` VALUES (2453713,135,571.106,96.3,-518.642,-0.05646,2); +INSERT INTO `zonepositions` VALUES (2453673,137,-132.425,69.3748,739.518,-3.13744,2); +INSERT INTO `zonepositions` VALUES (2453708,135,693.492,79.5221,-382.789,-0.232514,2); +INSERT INTO `zonepositions` VALUES (2453666,137,246.611,56.1687,831.572,2.89764,2); +INSERT INTO `zonepositions` VALUES (2372269,130,42.3246,4,-158.943,-0.273386,2); +INSERT INTO `zonepositions` VALUES (2377056,141,-114.159,18.3778,332.705,2.8655,2); +INSERT INTO `zonepositions` VALUES (2376310,130,91.1395,4,-111.101,-2.27906,2); +INSERT INTO `zonepositions` VALUES (2377064,131,94.3718,4,-108.09,0.815058,2); +INSERT INTO `zonepositions` VALUES (2376964,130,58.4986,8,-88.0199,-2.27798,2); +INSERT INTO `zonepositions` VALUES (2377068,131,67.2002,8,-80.4213,0.854852,2); +INSERT INTO `zonepositions` VALUES (2376969,130,-12.143,10,-44.8101,-2.89781,2); +INSERT INTO `zonepositions` VALUES (2377071,131,-7.5771,12.5628,-27.816,0.26209,2); +INSERT INTO `zonepositions` VALUES (2369965,130,-176.583,14,-14.6283,1.56838,2); +INSERT INTO `zonepositions` VALUES (2377075,140,465.194,96.6206,159.051,-1.73197,2); +INSERT INTO `zonepositions` VALUES (2379246,130,-123.162,9.99999,-8.84062,-1.56451,2); +INSERT INTO `zonepositions` VALUES (2377082,131,-107.435,6.98457,-9.0397,1.57633,2); +INSERT INTO `zonepositions` VALUES (2379249,130,-121.899,10.0722,9.43441,-1.5354,2); +INSERT INTO `zonepositions` VALUES (2377078,131,-106.993,6.98457,9.39492,1.58387,2); +INSERT INTO `zonepositions` VALUES (2372279,131,159.45,4,42.6079,-1.86339,2); +INSERT INTO `zonepositions` VALUES (2376287,141,20.1486,18.3778,565.384,1.34262,2); +INSERT INTO `zonepositions` VALUES (2210427,128,-3.02154,43,-27.8195,1.52636,2); +INSERT INTO `zonepositions` VALUES (2210364,129,-2.24011,20.0008,27.8738,1.54483,2); +INSERT INTO `zonepositions` VALUES (2210434,128,-92.7087,35.5,104.59,0.839544,2); +INSERT INTO `zonepositions` VALUES (2210376,129,-89.9423,20.6775,111.428,-3.09037,2); +INSERT INTO `zonepositions` VALUES (2210411,128,-70.0571,40.6609,-125.182,2.3762,2); +INSERT INTO `zonepositions` VALUES (2210368,129,-84.1969,18.0003,-22.3949,0.030137,2); +INSERT INTO `zonepositions` VALUES (2453691,134,-166.683,35.0913,-726.536,-0.302407,2); +INSERT INTO `zonepositions` VALUES (2464051,137,-108.773,70.3399,46.5696,1.79447,2); +INSERT INTO `zonepositions` VALUES (2453742,137,80.4418,80.0177,-115.679,0.063873,2); +INSERT INTO `zonepositions` VALUES (2453717,139,717.879,0.468218,208.285,-3.11069,2); +INSERT INTO `zonepositions` VALUES (2453747,138,408.384,27.5189,-5.33734,-0.320773,2); +INSERT INTO `zonepositions` VALUES (2453733,139,-472.983,1.43406,283.031,2.36451,2); +INSERT INTO `zonepositions` VALUES (4057217,139,-350.797,47.4884,-14.5283,-1.14213,2); +INSERT INTO `zonepositions` VALUES (4056858,148,-326.559,51.2799,-87.2374,-2.82522,2); +INSERT INTO `zonepositions` VALUES (4057229,139,289.163,41.1628,-198.013,0.64875,2); +INSERT INTO `zonepositions` VALUES (4056861,148,238.76,54.7158,-252.767,-1.7284,2); +INSERT INTO `zonepositions` VALUES (2376981,141,-398.28,-0.789985,99.3511,1.96518,2); +INSERT INTO `zonepositions` VALUES (2372291,140,258.5,52.6883,-4.64944,-0.456935,2); +INSERT INTO `zonepositions` VALUES (2377124,141,226.726,2.7533,669.653,-2.22354,2); +INSERT INTO `zonepositions` VALUES (2372323,146,-423.482,12.8616,-422.811,0.697403,2); +INSERT INTO `zonepositions` VALUES (2377115,141,446.556,-17.9999,-174.403,-0.73727,2); +INSERT INTO `zonepositions` VALUES (2372300,145,-559.838,-19.777,335.605,2.10368,2); +INSERT INTO `zonepositions` VALUES (2377133,141,-26.9884,33,-486.807,0.127408,2); +INSERT INTO `zonepositions` VALUES (2372337,147,36.6025,5.93622,506.673,3.10036,2); +INSERT INTO `zonepositions` VALUES (2377127,145,-173.756,-45.2898,483.95,-2.64246,2); +INSERT INTO `zonepositions` VALUES (2377118,146,-27.6321,16.1257,-760.456,-0.049568,2); +INSERT INTO `zonepositions` VALUES (1359242,148,5.5,-1.2,39,2,2); +/*!40000 ALTER TABLE `zonepositions` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:16 diff --git a/sql/zoneservers.sql b/sql/zoneservers.sql new file mode 100644 index 00000000..1e9f8789 --- /dev/null +++ b/sql/zoneservers.sql @@ -0,0 +1,51 @@ +-- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) +-- +-- Host: localhost Database: sapphire +-- ------------------------------------------------------ +-- Server version 5.7.13-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `zoneservers` +-- + +DROP TABLE IF EXISTS `zoneservers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `zoneservers` ( + `id` int(5) NOT NULL, + `ip` varchar(50) DEFAULT NULL, + `port` int(5) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `zoneservers` +-- + +LOCK TABLES `zoneservers` WRITE; +/*!40000 ALTER TABLE `zoneservers` DISABLE KEYS */; +INSERT INTO `zoneservers` VALUES (100,'127.0.0.1',54995); +/*!40000 ALTER TABLE `zoneservers` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-12-09 17:37:17 diff --git a/src/servers/CMakeLists.txt b/src/servers/CMakeLists.txt new file mode 100644 index 00000000..80fd3109 --- /dev/null +++ b/src/servers/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.6) +project (Sapphire_root) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) +set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) + +include_directories("${PROJECT_INCLUDE_DIR}") +include_directories("${PROJECT_SOURCE_DIR}") + + +add_subdirectory(${PROJECT_SOURCE_DIR}/Server_Common) +add_subdirectory(${PROJECT_SOURCE_DIR}/Server_Lobby) +add_subdirectory(${PROJECT_SOURCE_DIR}/Server_REST) +add_subdirectory(${PROJECT_SOURCE_DIR}/Server_Zone) diff --git a/src/servers/Server_Common/Acceptor.cpp b/src/servers/Server_Common/Acceptor.cpp new file mode 100644 index 00000000..2720d904 --- /dev/null +++ b/src/servers/Server_Common/Acceptor.cpp @@ -0,0 +1,130 @@ +#include "Hive.h" +#include "Acceptor.h" +#include "Connection.h" +#include +#include + +namespace Core +{ + namespace Network + { + + //----------------------------------------------------------------------------- + + Acceptor::Acceptor( HivePtr hive ) + : m_hive( hive ), + m_acceptor( hive->GetService() ), + m_io_strand( hive->GetService() ), + m_error_state( 0 ) + { + } + + Acceptor::~Acceptor() + { + } + + + bool Acceptor::OnAccept( ConnectionPtr connection, const std::string & host, uint16_t port ) + { + return true; + } + + void Acceptor::OnError( const boost::system::error_code & error ) + { + + } + + + void Acceptor::StartError( const boost::system::error_code & error ) + { + if( boost::interprocess::ipcdetail::atomic_cas32( &m_error_state, 1, 0 ) == 0 ) + { + boost::system::error_code ec; + m_acceptor.cancel( ec ); + m_acceptor.close( ec ); + OnError( error ); + } + } + + void Acceptor::DispatchAccept( ConnectionPtr connection ) + { + m_acceptor.async_accept( connection->GetSocket(), + connection->GetStrand().wrap( boost::bind( &Acceptor::HandleAccept, + shared_from_this(), + _1, + connection ) ) ); + } + + void Acceptor::HandleAccept( const boost::system::error_code & error, ConnectionPtr connection ) + { + if( error || HasError() || m_hive->HasStopped() ) + { + connection->StartError( error ); + } + else + { + if( connection->GetSocket().is_open() ) + { + if( OnAccept( connection, + connection->GetSocket().remote_endpoint().address().to_string(), + connection->GetSocket().remote_endpoint().port() ) ) + { + connection->OnAccept( m_acceptor.local_endpoint().address().to_string(), m_acceptor.local_endpoint().port() ); + connection->Recv(); + } + } + else + { + connection->StartError( error ); + } + } + } + + void Acceptor::Stop() + { + + } + + void Acceptor::Accept( ConnectionPtr connection ) + { + m_io_strand.post( boost::bind( &Acceptor::DispatchAccept, shared_from_this(), connection ) ); + } + + void Acceptor::Listen( const std::string & host, const uint16_t & port ) + { + try + { + boost::asio::ip::tcp::resolver resolver( m_hive->GetService() ); + boost::asio::ip::tcp::resolver::query query( host, std::to_string( port ) ); + boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve( query ); + + m_acceptor.open( endpoint.protocol() ); + m_acceptor.set_option( boost::asio::ip::tcp::acceptor::reuse_address( false ) ); + m_acceptor.bind( endpoint ); + m_acceptor.listen( boost::asio::socket_base::max_connections ); + } + catch( ... ) + { + // this should not happen + assert( true ); + } + + } + + HivePtr Acceptor::GetHive() + { + return m_hive; + } + + boost::asio::ip::tcp::acceptor & Acceptor::GetAcceptor() + { + return m_acceptor; + } + + bool Acceptor::HasError() + { + return ( boost::interprocess::ipcdetail::atomic_cas32( &m_error_state, 1, 1 ) == 1 ); + } + + } +} \ No newline at end of file diff --git a/src/servers/Server_Common/Acceptor.h b/src/servers/Server_Common/Acceptor.h new file mode 100644 index 00000000..638ea7b8 --- /dev/null +++ b/src/servers/Server_Common/Acceptor.h @@ -0,0 +1,78 @@ +#ifndef ACCEPTOR_H_ +#define ACCEPTOR_H_ + +#include +#include +#include +#include +#include "Forwards.h" + +namespace Core +{ + namespace Network + { + + class Connection; + + class Acceptor : public boost::enable_shared_from_this< Acceptor > + { + friend class Hive; + + private: + HivePtr m_hive; + boost::asio::ip::tcp::acceptor m_acceptor; + boost::asio::strand m_io_strand; + volatile uint32_t m_error_state; + + private: + Acceptor( const Acceptor & rhs ); + Acceptor & operator =( const Acceptor & rhs ); + void StartError( const boost::system::error_code & error ); + void DispatchAccept( ConnectionPtr connection ); + void HandleAccept( const boost::system::error_code & error, ConnectionPtr connection ); + + private: + // Called when a connection has connected to the server. This function + // should return true to invoke the connection's OnAccept function if the + // connection will be kept. If the connection will not be kept, the + // connection's Disconnect function should be called and the function + // should return false. + virtual bool OnAccept( ConnectionPtr connection, const std::string & host, uint16_t port ); + + // Called when an error is encountered. Most typically, this is when the + // acceptor is being closed via the Stop function or if the Listen is + // called on an address that is not available. + virtual void OnError( const boost::system::error_code & error ); + + public: + Acceptor( HivePtr hive ); + virtual ~Acceptor(); + + // Returns the Hive object. + HivePtr GetHive(); + + // Returns the acceptor object. + boost::asio::ip::tcp::acceptor & GetAcceptor(); + + // Returns the strand object. + boost::asio::strand & GetStrand(); + + // Returns true if this object has an error associated with it. + bool HasError(); + + public: + // Begin listening on the specific network interface. + void Listen( const std::string & host, const uint16_t & port ); + + // Posts the connection to the listening interface. The next client that + // connections will be given this connection. If multiple calls to Accept + // are called at a time, then they are accepted in a FIFO order. + void Accept( ConnectionPtr connection ); + + // Stop the Acceptor from listening. + void Stop(); + }; + + } +} +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/CMakeLists.txt b/src/servers/Server_Common/CMakeLists.txt new file mode 100644 index 00000000..04e0ea48 --- /dev/null +++ b/src/servers/Server_Common/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 2.6) +project(Sapphire) + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader/") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/ChaiScript-6.0.0/include/") + +if(UNIX) + include_directories("/usr/include/mysql/") + message(STATUS "Setting GCC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32") + + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + else() + if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib) + else() + message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!") + endif() + endif() +else() + add_definitions(-D_WIN32_WINNT=0x601) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL/") + message(STATUS "Setting MSVC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + message(STATUS "Using boost in /libraries/external") + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + else() + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR})) + set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR}) + set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR}) + else() + message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!") + endif() + endif() +endif() + +set(Boost_USE_STATIC_LIBS ON) + + +file(GLOB UTILS_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB UTILS_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.cpp") + +find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) +include_directories(${Boost_INCLUDE_DIR}) +link_directories(${BOOST_LIBRARYDIR}) +link_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader/") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +add_library(Common ${UTILS_PUBLIC_INCLUDE_FILES} ${UTILS_SOURCE_FILES}) + +set_target_properties(Common PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" +) + +if (UNIX) + target_link_libraries(Common xivdat mysqlclient) +else() + target_link_libraries(Common xivdat libmysql) +endif() diff --git a/src/servers/Server_Common/ChaiscriptStdLib.cpp b/src/servers/Server_Common/ChaiscriptStdLib.cpp new file mode 100644 index 00000000..282d4ecb --- /dev/null +++ b/src/servers/Server_Common/ChaiscriptStdLib.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "ChaiscriptStdLib.h" +#include + +std::shared_ptr< chaiscript::Module > Core::Scripting::create_chaiscript_stdlib() +{ + return chaiscript::Std_Lib::library(); +} + +boost::shared_ptr< chaiscript::ChaiScript > Core::Scripting::create_chaiscript() +{ + auto chai = boost::make_shared< chaiscript::ChaiScript >(); + //create_chaiscript_bindings( chai ); + return chai; +} \ No newline at end of file diff --git a/src/servers/Server_Common/ChaiscriptStdLib.h b/src/servers/Server_Common/ChaiscriptStdLib.h new file mode 100644 index 00000000..f3e346d8 --- /dev/null +++ b/src/servers/Server_Common/ChaiscriptStdLib.h @@ -0,0 +1,19 @@ +#ifndef CHAISCRIPT_STDLIB +#define CHAISCRIPT_STDLIB + +#include + +namespace chaiscript +{ + class Module; + class ChaiScript; +} + +namespace Core { namespace Scripting { + +std::shared_ptr create_chaiscript_stdlib(); +boost::shared_ptr< chaiscript::ChaiScript > create_chaiscript(); + +} } + +#endif diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h new file mode 100644 index 00000000..60e6cb43 --- /dev/null +++ b/src/servers/Server_Common/Common.h @@ -0,0 +1,958 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include + +#include + +#include + +// +--------------------------------------------------------------------------- +// The following enumerations are structures to require their type be included. +// They are also defined within the Core::Common namespace to avoid collisions. +// +--------------------------------------------------------------------------- +namespace Core { + namespace Common { + + // 99 is the last spawn id that seems to spawn any actor + const uint8_t MAX_DISPLAYED_ACTORS = 99; + + const int32_t INVALID_GAME_OBJECT_ID = 0xE0000000; + + struct FFXIVARR_POSITION3 + { + float x; + float y; + float z; + }; + + enum EquipSlot : uint8_t + { + MainHand = 0, + OffHand = 1, + Head = 2, + Body = 3, + Hands = 4, + Waist = 5, + Legs = 6, + Feet = 7, + Neck = 8, + Ear = 9, + Wrist = 10, + Ring1 = 11, + Ring2 = 12, + SoulCrystal = 13, + }; + + enum InventoryType : uint16_t + { + Bag0 = 0, + Bag1 = 1, + Bag2 = 2, + Bag3 = 3, + + GearSet0 = 1000, + GearSet1 = 1001, + + Currency = 2000, + Crystal = 2001, + //UNKNOWN_0 = 2003, + KeyItem = 2004, + DamagedGear = 2007, + //UNKNOWN_1 = 2008, + + ArmoryOff = 3200, + ArmoryHead = 3201, + ArmoryBody = 3202, + ArmoryHand = 3203, + ArmoryWaist = 3204, + ArmoryLegs = 3205, + ArmoryFeet = 3206, + ArmotyNeck = 3207, + ArmoryEar = 3208, + ArmoryWrist = 3209, + ArmoryRing = 3300, + + ArmorySoulCrystal = 3400, + ArmoryMain = 3500, + + RetainerBag0 = 10000, + RetainerBag1 = 10001, + RetainerBag2 = 10002, + RetainerBag3 = 10003, + RetainerBag4 = 10004, + RetainerBag5 = 10005, + RetainerBag6 = 10006, + RetainerEquippedGear = 11000, + RetainerGil = 12000, + RetainerCrystal = 12001, + RetainerMarket = 12002, + + FreeCompanyBag0 = 20000, + FreeCompanyBag1 = 20001, + FreeCompanyBag2 = 20002, + FreeCompanyGil = 22000, + FreeCompanyCrystal = 22001 + }; + + enum Race : uint8_t + { + Hyur = 1, + Elezen = 2, + Lalafell = 3, + Miqote = 4, + Roegadyn = 5, + AuRa = 6, + }; + + enum struct ZoneingType : uint8_t + { + None = 1, + Teleport = 2, + Return = 3, + ReturnDead = 4, + FadeIn = 5, + }; + + enum Tribe : uint8_t + { + Midlander = 1, + Highlander = 2, + Wildwood = 3, + Duskwight = 4, + Plainsfolk = 5, + Dunesfolk = 6, + SeekerOfTheSun = 7, + KeeperOfTheMoon = 8, + SeaWolf = 9, + Hellsguard = 10, + Raen = 11, + Xaela = 12, + }; + + enum Gender : uint8_t + { + Male = 0, + Female = 1, + }; + + enum GuardianDeity : uint8_t + { + Halone = 1, // the Fury + Menphina = 2, // the Lover + Thaliak = 3, // the Scholar + Nymeia = 4, // the Spinner + Llymlaen = 5, // the Navigator + Oschon = 6, // the Wanderer + Byregot = 7, // the Builder + Rhalgr = 8, // the Destroyer + Azeyma = 9, // the Warden + NaldThal = 10, // the Traders + Nophica = 11, // the Matron + Althyk = 12, // the Keeper + }; + + enum struct GrandCompany : uint8_t + { + None = 0, + Maelstrom = 1, + TwinAdder = 2, + ImmortalFlames = 3, + }; + + enum struct GCRank : uint8_t + { + None = 0, + PrivateThirdClass = 1, + PrivateSecondClass = 2, + PrivateFirstClass = 3, + Corporal = 4, + SergeantThirdClass = 5, + SergeantSecondClass = 6, + SergeantFirstClass = 7, + ChiefSergeant = 8, + SecondLieutenant = 9, + FirstLieutenant = 10, + Captain = 11, + SecondCommander = 12, + FirstCommander = 13, + HighCommander = 14, + RearMarshal = 15, + ViceMarshal = 16, + Marshal = 17, + GrandMarshal = 18, + Champion = 19, + }; + + enum struct Weather : uint8_t + { + None, + Clear, + Fair, + Overcast, + Fog, + Wind, + Gales, + Rain, + Showers, + Thunder, + Thunderstorms, + DustStorms, + Sandstorms, + HotSpells, + HeatWave, + Snow, + Blizzards, + Gloom, + Aurora, + Darkness, + Hopelessness, + Overcast1, + StormClouds, + Torrential, + Torrential1, + Lour, + HeatWave1, + Gloom1, + Gales1, + Eruptions, + Fair1 + }; + + enum Town : uint8_t + { + LimsaLominsa = 1, + Gridania = 2, + Uldah = 3, + Ishgard = 4, + }; + + enum ClassJob : uint8_t + { + CLASS_ADV = 0, // purely internal + CLASS_GLADIATOR = 1, // gladiator + CLASS_PUGILIST = 2, // pugilist + CLASS_MARAUDER = 3, // marauder + CLASS_LANCER = 4, // lancer + CLASS_ARCHER = 5, // archer + CLASS_CONJURER = 6, // conjurer + CLASS_THAUMATURGE = 7, // thaumaturge + CLASS_WOODWORKER = 8, // carpenter + CLASS_BLACKSMIT = 9, // blacksmith + CLASS_ARMOURER = 10, // armorer + CLASS_GOLDSMITH = 11, // goldsmith + CLASS_TANNER = 12, // leatherworker + CLASS_WEAVER = 13, // weaver + CLASS_ALCHEMIST = 14, // alchemist + CLASS_CULINARIAN = 15, // culinarian + CLASS_MINER = 16, // miner + CLASS_HARVESTER = 17, // botanist + CLASS_FISHERMAN = 18, // fisher + JOB_KNIGHT = 19, // paladin + JOB_MONK = 20, // monk + JOB_WARRIOR = 21, // warrior + JOB_DRAGON = 22, // dragoon + JOB_BARD = 23, // bard + JOB_WHITE = 24, // white mage + JOB_BLACK = 25, // black mage + CLASS_ARCANIST = 26, // arcanist + JOB_SUMMONER = 27, // summoner + JOB_SCHOLAR = 28, // scholar + CLASS_ROGUE = 29, + JOB_NINJA = 30, + JOB_MACHINIST = 31, // machinist + JOB_DARKKNIGHT = 32, // darknight + JOB_ASTROLOGIAN = 33, // astro + JOB_SAMURAI = 34, // sam + JOB_REDMAGE = 35, // red mage + + }; + + enum PlayerSyncFlags : uint32_t + { + None = 0x00000000, + Position = 0x00000001, // x,y,z,zone + Status = 0x00000002, // hp,mp,tp,class + Look = 0x00000004, // models to display + ExpLevel = 0x00000008, // exp,level + Quests = 0x00000010, // quest status + Achievements = 0x00000020, // achievements + Discovery = 0x00000040, // Discovered places + Aetherytes = 0x00000080, // Attuned aetherytes + HomePoint = 0x00000100, // Current homepoint + HowTo = 0x00000200, + FirstLogin = 0x00000400, + HpMp = 0x00000800, + QuestTracker = 0x00001000, + NewGame = 0x00002000, + + Unlocks = 0x00008000, + PlayTime = 0x00010000, + NewAdventurer = 0x00020000, + SearchInfo = 0x00040000, + GC = 0x00080000, + + All = 0xFFFFFFFF, + }; + + /** + * Structural representation of the packet sent by the server + * Send the entire StatusEffect list + */ + struct StatusEffect + { + uint16_t effect_id; + uint16_t unknown1; + float duration; + uint32_t sourceActorId; + }; + + enum struct ItemCategory : uint8_t + { + not_set = 0, + PugWep = 1, + GlaWep = 2, + MrdWep = 3, + ArcWep = 4, + LncWep = 5, + ThmWep = 6, + Thm2Wep = 7, + CnjWep = 8, + Cnj2Wep = 9, + ArnWep = 10, + Shield = 11, + CrpPri = 12, + BlmPri = 13, + ArmPri = 14, + GldPri = 15, + LtwPri = 16, + WvrPri = 17, + AlcPri = 18, + ClnPri = 19, + MinPri = 20, + BotPri = 21, + FshPri = 22, + CrpSec = 23, + BsmSec = 24, + ArmSec = 25, + GldSec = 26, + LtwSec = 27, + WvrSec = 28, + AlcSec = 29, + ClnSec = 30, + MinSec = 31, + BotSec = 32, + FshSec = 33, + Throw = 34, + Head = 35, + Body = 36, + Hands = 37, + Waist = 38, + Legs = 39, + Feet = 40, + UndShirt = 41, + UndGarm = 42, + Earring = 43, + Necklace = 44, + Bracelet = 45, + Ring = 46, + Crystal = 47, + Materia = 48, + Material = 49, + Food = 50, + Enhancement = 51, + Curative = 52, + PLaceholder = 53, + Enfeeb = 54, + Medecine = 55, + }; + + + enum RegionType : uint8_t + { + normal, + instance, + }; + + enum CharaLook : uint8_t + { + Race = 0x00, + Gender = 0x01, + Tribe = 0x04, + Height = 0x03, + ModelType = 0x02, // Au Ra: changes horns/tails, everything else: seems to drastically change appearance (flip between two sets, odd/even numbers). sometimes retains hairstyle and other features + FaceType = 0x05, + HairStyle = 0x06, + HasHighlights = 0x07, // negative to enable, positive to disable + SkinColor = 0x08, + EyeColor = 0x09, // color of character's right eye + HairColor = 0x0A, // main color + HairColor2 = 0x0B, // highlights color + FaceFeatures = 0x0C, // seems to be a toggle, (-odd and +even for large face covering), opposite for small + FaceFeaturesColor = 0x0D, + Eyebrows = 0x0E, + EyeColor2 = 0x0F, // color of character's left eye + EyeShape = 0x10, + NoseShape = 0x11, + JawShape = 0x12, + LipStyle = 0x13, // lip colour depth and shape (negative values around -120 darker/more noticeable, positive no colour) + LipColor = 0x14, + RaceFeatureSize = 0x15, + RaceFeatureType = 0x16, // negative or out of range tail shapes for race result in no tail (e.g. Au Ra has max of 4 tail shapes), incorrect value can crash client + BustSize = 0x17, // char creator allows up to max of 100, i set to 127 cause who wouldnt but no visible difference + Facepaint = 0x18, + FacepaintColor = 0x19, + + }; + + enum MoveType : uint16_t + { + Run = 0x00, + Walk = 0x02, + Strafe = 0x04, + Jump = 0x100, + Fall = 0x400, + Land = 0x200, + }; + + enum UserLevel : uint8_t + { + all = 0xff, + player = 0x01, + gm = 0x02, + dev = 0x04, + admin = 0x08 + }; + + struct QuestActive + { + QuestActive() + { + c.questId = 0; + c.sequence = 0; + c.flags = 0; + c.UI8A = 0; + c.UI8B = 0; + c.UI8C = 0; + c.UI8D = 0; + c.UI8E = 0; + c.UI8F = 0; + c.padding = 0; + } + + + union + { + struct + { + uint16_t questId; + uint8_t sequence; + uint8_t flags; + uint8_t padding; + uint8_t BitFlag48; + uint8_t BitFlag40; + uint8_t BitFlag32; + uint8_t BitFlag24; + uint8_t BitFlag16; + uint8_t BitFlag8; + uint8_t padding1; + } a; + + struct + { + uint16_t questId; + uint8_t sequence; + uint8_t flags; + uint8_t padding; + uint8_t UI8AL : 4; + uint8_t UI8AH : 4; + uint8_t UI8BL : 4; + uint8_t UI8BH : 4; + uint8_t UI8CL : 4; + uint8_t UI8CH : 4; + uint8_t UI8DL : 4; + uint8_t UI8DH : 4; + uint8_t UI8EL : 4; + uint8_t UI8EH : 4; + uint8_t UI8FL : 4; + uint8_t UI8FH : 4; + uint8_t padding1; + } b; + + struct + { + uint16_t questId; + uint8_t sequence; + uint8_t flags; + uint8_t padding; + uint8_t UI8A; + uint8_t UI8B; + uint8_t UI8C; + uint8_t UI8D; + uint8_t UI8E; + uint8_t UI8F; + uint8_t padding1; + } c; + + //struct + //{ + // uint16_t questId; + // uint8_t sequence; + // uint8_t flags; + // uint8_t padding; + // uint16_t UI16A; + // uint16_t UI16B; + // uint16_t UI16C; + // uint8_t padding1; + //} d; + + //struct + //{ + // uint8_t padding; + // uint32_t UI32A; + // uint16_t padding2; + //} e; + }; + + + }; + + enum EventType : uint16_t + { + Quest = 0x0001, + ChocoRent = 0x0002, + Shop = 0x0004, + Aetheryte = 0x0005, + GuildLeveAssign = 0x0006, + DefaultTalk = 0x0009, + CustomTalk = 0x000B, + CraftLeve = 0x000E, + ChocoPort = 0x0012, + Opening = 0x0013, + GCShop = 0x0016, + GuildOrderGuide = 0x0017, + GuildOrderOfficer = 0x0018, + Stories = 0x001A, + FcTalk = 0x001F, + }; + + enum ActionType : uint8_t + { + + Event, + Spell, + Teleport + + }; + + enum struct PlayerStateFlag : uint8_t + { + NoFlag = 0, + NoCombat, + Combat, + Casting, + StatusAffliction, + StatusAffliction1, + Occupied, + Occupied1, + + Occupied2, + Occupied3, + BoundByDuty, + Occupied4, + NewAdventurer, + TradeOpen, + BrowsingBazaar, + HandlingItems, + + Crafting, + PreparingToCraft, + Gathering, + Fishing, + BeingRaised, + BetweenAreas, + Stealthed, + InnRoom, + + Jumping, + AutoRun, + Occupied5, + BetweenAreas1, + SystemError, + LoggingOut, + InvalidLocation, + WaitingForDuty, + + BoundByDuty1, + Mounting, + WatchingCutscene, + WaitingForDutyFinder, + CreatingCharacter, + Jumping1, + PvpDisplay, + StatusAfflication2, + + Mounting1, + CarryingItem, + UsingPartyFinder, + HousingFunctions, + Transformed, + FreeTrail, + BeingMoved, + Mounting2, + + StatusAffliction3, + StatusAffliction4, + RegisteringRaceOrMatch, + WaitingForRaceOrMatch, + WaitingForTripleTriadMatch, + InFlight, + WatchingCutscene1 + + }; + + enum struct FateStatus : uint8_t + { + Active = 2, + Inactive = 4, + Preparing = 7, + Completed = 8, + }; + + enum struct OnlineStatus : uint8_t + { + None = 0, + Producer, + GameMaster, + GameMaster1, + GameMaster2, + Disconnected, + WaitFriendlistApproval, + WaitLinkshellApproval, + WaitFCApproval, + NotFound, + Offline, + CameraMode, + Mentor, + Busy, + PvP, + TripleTriad, + Cutscene, + ChocoPorter, + Afk, + LfRepairs, + LfRepair, + LfMeld, + RolePlaying, + LfParty, + SwordForHire, + WaitDutyFinder, + RecPartyMembers, + Mentor1, + PvEMentor, + TradeMentor, + PvPMentor, + Returner, + NewAdventurer, + AllianceLead, + AlliancePartyLead, + AllianceMember, + PartyLeader, + PartyMember, + PartyLeaderCross, + PartyMemberCross, + AnotherWorld, + SharingDuty, + SimliarDuty, + InDuty, + TrialAdventurer, + FreeCompany, + GrandCompany, + Online + + }; + + enum ActorControlType : uint16_t + { + ToggleWeapon = 0x01, + SetStatus = 0x02, + CastStart = 0x03, + ToggleAggro = 0x04, + ClassJobChange = 0x05, + DefeatMsg = 0x06, + GainExpMsg = 0x07, + + LevelUpEffect = 0x0A, + + ExpChainMsg = 0x0C, + HpSetStat = 0x0D, + DeathAnimation = 0x0E, + CastInterrupt = 0x0F, + + StatusEffectGain = 0x14, + StatusEffectLose = 0x15, + + UpdateRestedExp = 0x018, + Unk2 = 0x19, + + Flee = 0x1B, + + Unk3 = 0x20, + + CombatIndicationShow = 0x22, + + SpawnEffect = 0x25, + ToggleInvisible = 0x26, + + ToggleActionUnlock = 0x29, + + UpdateUiExp = 0x2B, + DmgTakenMsg = 0x2D, + + SetTarget = 0x32, + ToggleNameHidden = 0x36, + + LimitbreakStart = 0x47, + LimitbreakPartyStart = 0x48, + BubbleText = 0x49, + + DamageEffect = 0x50, + RaiseAnimation = 0x51, + TreasureScreenMsg = 0x57, + ItemRepairMsg = 0x5C, + + LeveStartAnim = 0x66, + Unk4 = 0x67, + PlayerNameGrayout = 0x6A, + + ItemObtainMsg = 0x75, + DutyQuestScreenMsg = 0x7B, + + ItemObtainIcon = 0x84, + FateItemFailMsg = 0x85, + ItemFailMsg = 0x86, + ActionLearnMsg1 = 0x87, + + FreeEventPos = 0x8A, + + UnlockAetherCurrentMsg = 0xA4, + + RemoveName = 0xA8, + + ScreenFadeOut = 0xAA, + + ZoneIn = 0xC8, + ZoneInDefaultPos = 0xC9, + + TeleportStart = 0xCB, + + TeleportDone = 0xCD, + TeleportDoneFadeOut = 0xCE, + DespawnZoneScreenMsg = 0xCF, + + InstanceSelectDlg = 0xD2, + ActorDespawnEffect = 0xD4, + + CompanionUnlock = 0xFD, + ObtainBarding = 0xFE, + EquipBarding = 0xFF, + + CompanionMsg1 = 0x102, + CompanionMsg2 = 0x103, + ShowPetHotbar = 0x104, + + ActionLearnMsg = 0x109, + ActorFadeOut = 0x10A, + ActorFadeIn = 0x10B, + WithdrawMsg = 0x10C, + OrderMinion = 0x10D, + ToggleMinion = 0x10E, + LearnMinion = 0x10F, + ActorFateOut1 = 0x110, + + Emote = 0x122, + + SetPose = 0x127, + + CraftingUnk = 0x12C, + + GatheringSenseMsg = 0x130, + PartyMsg = 0x131, + GatheringSenseMsg1 = 0x132, + + GatheringSenseMsg2 = 0x138, + + FishingMsg = 0x140, + + FishingBaitMsg = 0x145, + + FishingReachMsg = 0x147, + FishingFailMsg = 0x148, + + MateriaConvertMsg = 0x15E, + MeldSuccessMsg = 0x15F, + MeldFailMsg = 0x160, + MeldModeToggle = 0x161, + + AetherRestoreMsg = 0x163, + + DyeMsg = 0x168, + + ToggleCrestMsg = 0x16A, + ToggleBulkCrestMsg = 0x16B, + MateriaRemoveMsg = 0x16C, + GlamourCastMsg = 0x16D, + GlamourRemoveMsg = 0x16E, + + RelicInfuseMsg = 0x179, + + AetherReductionDlg = 0x17D, + + Unk6 = 0x19C, + + SetTitle = 0x1F4, + + SetStatusIcon = 0x1F8, + + SetHomepoint = 0x1FB, + SetFavorite = 0x1FC, + LearnTeleport = 0x1FD, + + ArmoryErrorMsg = 0x201, + + AchievementPopup = 0x203, + + Unk7 = 0x205, + AchievementMsg = 0x206, + + SetItemLevel = 0x209, + + ChallengeEntryCompleteMsg = 0x20B, + ChallengeEntryUnlockMsg = 0x20C, + + GilTrailMsg = 0x211, + + SetMaxGearSets = 0x230, + + ToggleDisplayHeadAndWeapon = 0x260, + + GearSetEquipMsg = 0x321 + }; + + enum struct ChatType : uint32_t + { + LogKindError, + ServerDebug, + ServerUrgent, + ServerNotice, + Unused4, + Unused5, + Unused6, + Unused7, + Unused8, + Unused9, + Say, + Shout, + Tell, + TellReceive, + Party, + Alliance, + LS1, + LS2, + LS3, + LS4, + LS5, + LS6, + LS7, + LS8, + FreeCompany, + Unused25, + Unused26, + NoviceNetwork, + CustomEmote, + StandardEmote, + Yell, + Unknown31, + PartyUnk2, + Unused33, + Unused34, + Unused35, + Unused36, + Unused37, + Unused38, + Unused39, + Unused40, + BattleDamage, + BattleFailed, + BattleActions, + BattleItems, + BattleHealing, + BattleBeneficial, + BattleDetrimental, + BattleUnk48, + BattleUnk49, + Unused50, + Unused51, + Unused52, + Unused53, + Unused54, + Unused55, + Echo, + SystemMessage, + SystemErrorMessage, + BattleSystem, + GatheringSystem, + NPCMessage, + LootMessage, + Unused63, + CharProgress, + Loot, + Crafting, + Gathering, + NPCAnnouncement, + FCAnnouncement, + FCLogin, + RetainerSale, + PartySearch, + PCSign, + DiceRoll, + NoviceNetworkNotice, + Unknown76, + Unused77, + Unused78, + Unused79, + GMTell, + GMSay, + GMShout, + GMYell, + GMParty, + GMFreeCompany, + GMLS1, + GMLS2, + GMLS3, + GMLS4, + GMLS5, + GMLS6, + GMLS7, + GMLS8, + GMNoviceNetwork, + Unused95, + Unused96, + Unused97, + Unused98, + Unused99, + Unused100 + }; + + struct ServerEntry + { + uint32_t serverId; + uint32_t flags; + }; + + typedef std::vector< PlayerStateFlag > PlayerStateFlagList; + + } /* Common */ +} /* Core */ + +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/CommonNetwork.h b/src/servers/Server_Common/CommonNetwork.h new file mode 100644 index 00000000..017035a6 --- /dev/null +++ b/src/servers/Server_Common/CommonNetwork.h @@ -0,0 +1,318 @@ +/** +* Structural definitions common to all FFXIV:ARR packets. +*/ +#ifndef _CORE_NETWORK_PACKETS_COMMON_H +#define _CORE_NETWORK_PACKETS_COMMON_H + +#include + +#include + +using namespace std; + +namespace Core { +namespace Network { +namespace Packets { + + /** + * Anticipated usage: + * ================== + * Set up a stream buffer to collect the bytes to be transmitted as a packet. + * Now, you can do the following (given you have the structs filled out already). + * + * FFXIVARR_PACKET_HEADER pkt_hdr = { . . . }; + * FFXIVARR_PACKET_SEGMENT_HEADER pkt_seg_hdr[n] = { . . . }; + * + * std::stringstream buf; + * buf << pkt_hdr; + * for (int i = 0; i < n; i++) + * { + * buf << pkt_seg_hdr[i]; + * buf << {pkt_seg_data[i]}; + * } + * + * The reverse can be done parsing a packet. Remember to validate the packet + * type before parsing the headers. + * + * Compression and Encryption: + * =========================== + * By using std::iostream's, you can support stream filters. Simply create a + * stream that performs the compression or encryption, and use that stream to + * read and write. + */ + + /** + * Structure representing the common header for all FFXIVARR packets. + * + * 0 4 8 12 14 16 + * +-------------------------------+---------------+-------+-------+ + * | timestamp | size | cType | count | + * +---+---+-------+---------------+---------------+-------+-------+ + * | ? |CMP| ? | ? | + * +---+---+-------+---------------+ + * (followed by /count/ FFXIVARR_PACKET_SEGMENTs) + */ + struct FFXIVARR_PACKET_HEADER + { + + uint64_t unknown_0; + uint64_t unknown_8; + /** + * Represents the number of milliseconds since epoch that the packet was + * sent. + */ + uint64_t timestamp; + /** The size of the packet header and its payload */ + uint32_t size; + /** The type of this connection - 1 zone, 2 chat*/ + uint16_t connectionType; + /** The number of packet segments that follow. */ + uint16_t count; + uint8_t unknown_20; + /** Indicates if the data segments of this packet are compressed. */ + uint8_t isCompressed; + uint32_t unknown_24; + }; + + inline ostream& operator<<(ostream& os, const FFXIVARR_PACKET_HEADER& hdr) + { + return os.write(reinterpret_cast(&hdr), sizeof hdr); + } + + inline istream& operator>>(istream& is, FFXIVARR_PACKET_HEADER& hdr) + { + return is.read(reinterpret_cast(&hdr), sizeof hdr); + } + + /** + * Structure representing the header portion of a packet segment. + * + * NOTE: If the main packet header indicated the packet is compressed, this + * header will be compressed as well! The header will NOT ever be encrypted. + * + * 0 4 8 12 16 + * +---------------+---------------+---------------+-------+-------+ + * | size | source_actor | target_actor | type | ? | + * +---------------+---------------+---------------+-------+-------+ + * | | + * : type-specific data of length, size, follows : + * | (NOTE: Some segments MAY be encrypted) | + * +---------------------------------------------------------------+ + */ + struct FFXIVARR_PACKET_SEGMENT_HEADER + { + /** The size of the segment header and its data. */ + uint32_t size; + /** The session ID this segment describes. */ + uint32_t source_actor; + /** The session ID this packet is being delivered to. */ + uint32_t target_actor; + /** The segment type. (1, 2, 3, 7, 8, 9, 10) */ + uint16_t type; + uint16_t _reserved_E; + }; + + inline ostream& operator<<(ostream& os, const FFXIVARR_PACKET_SEGMENT_HEADER& hdr) + { + return os.write(reinterpret_cast(&hdr), sizeof hdr); + } + + inline istream& operator>>(istream& is, FFXIVARR_PACKET_SEGMENT_HEADER& hdr) + { + return is.read(reinterpret_cast(&hdr), sizeof hdr); + } + + // TODO: Include structures for the individual packet segment types + + /** + * Server IPC Type Codes. + */ + enum ServerIpcType : uint16_t + { + LobbyError = 0x0002, + LobbyServiceAccountList = 0x000C, + LobbyCharList = 0x000D, + LobbyCharCreate = 0x000E, + LobbyEnterWorld = 0x000F, + LobbyServerList = 0x0015, + LobbyRetainerList = 0x0017, + + Ping = 0x0065, // updated for sb + Init = 0x0066, // updated for sb + Chat = 0x0067, // updated for sb + Logout = 0x0077, // updated for sb + Playtime = 0x00AF, // updated for sb + SocialRequestError = 0x00AD, + SocialRequestResponse = 0x11AF, + SocialList = 0x00B4, // updated for sb + UpdateSearchInfo = 0x00B2, + InitSearchInfo = 0x00B7, // updated for sb + ServerNotice = 0x00BC, // updated for sb + SetOnlineStatus = 0x00B9, + BlackList = 0x00CA, // updated for sb + LinkshellList = 0x00D1, // updated for sb + StatusEffectList = 0x00F0, // updated for sb + Effect = 0x00F1, // updated for sb + GCAffiliation = 0x00FC, + + ActorSetPos = 0x0114, // updated for sb + ActorCast = 0x0116, // updated for sb + PlayerSpawn = 0x0110, // updated for sb + NpcSpawn = 0x0111, // updated for sb + ActorMove = 0x0112, // updated for sb + HateList = 0x011A, // updated for sb borked + UpdateClassInfo = 0x0117, + InitUI = 0x011E, // updated for sb + PlayerStats = 0x011F, // updated for sb + ActorOwner = 0x0120, // updated for sb + PlayerStateFlags = 0x0121, // updated for sb + PlayerClassInfo = 0x0123, // updated for sb + ModelEquip = 0x0124, // updated for sb + ItemInfo = 0x0139, // updated for sb + ContainerInfo = 0x013A, // updated for sb + InventoryTransactionFinish = 0x013B, + InventoryTransaction = 0x012A, + CurrencyCrystalInfo = 0x013D, + InventoryActionAck = 0x0139, + UpdateInventorySlot = 0x0140, // updated for sb + AddStatusEffect = 0x0141, + ActorControl142 = 0x0142, // unchanged for sb + ActorControl143 = 0x0143, // unchanged for sb + ActorControl144 = 0x0144, // unchanged for sb + UpdateHpMpTp = 0x0145, // unchanged for sb + + CFNotify = 0x0078, + CFMemberStatus = 0x0079, + CFDutyInfo = 0x007A, + CFPlayerInNeed = 0x007F, + CFRegistered = 0x00B0, + CFAvailableContents = 0x01CF, + + EventPlay = 0x0154, // updated for sb + EventStart = 0x015D, // updated for sb + EventFinish = 0x015E, // updated for sb + QuestActiveList = 0x0171, // updated for sb + QuestUpdate = 0x0172, // updated for sb + QuestCompleteList = 0x0173, // updated for sb + QuestFinish = 0x0174, // updated for sb + QuestMessage = 0x0179, + QuestTracker = 0x0181, // updated for sb + ActorSpawn = 0x0190, // todo: split into playerspawn/actorspawn and use opcode 0x110/0x111 + ActorFreeSpawn = 0x0191, // unchanged for sb + InitZone = 0x019A, // unchanged for sb + WeatherChange = 0x01AF, // updated for sb + Discovery = 0x01B2, // updated for sb + + PrepareZoning = 0x0239, // updated for sb + + // Unknown IPC types that still need to be sent + // TODO: figure all these out properly + IPCTYPE_UNK_320 = 0x1FB, + IPCTYPE_UNK_322 = 0x1FD, + + }; + + // TODO: Include structures for the individual packet segment types + + /** + * Client IPC Type Codes. + */ + enum ClientIpcType : uint16_t + { + ReqCharList = 0x0003, + ReqEnterWorld = 0x0004, + ReqServiceAccountList = 0x0005, + + ReqCharDelete = 0x000A, + ReqCharCreate = 0x000B, + + PingHandler = 0x0065,// updated for sb + InitHandler = 0x0066,// updated for sb + ChatHandler = 0x0067,// updated for sb + + FinishLoadingHandler = 0x0069,// updated for sb + + PlayTimeHandler = 0x0073,// updated for sb + LogoutHandler = 0x0074,// updated for sb + + CFDutyInfoHandler = 0x0078, + + SocialReqSendHandler = 0x00A5, + SocialListHandler = 0x00AA,// updated for sb + SetSearchInfoHandler = 0x00AC, + + ReqSearchInfoHandler = 0x00AD, + + BlackListHandler = 0x00B7,// updated for sb + + LinkshellListHandler = 0x00BF,// updated for sb + + FcInfoReqHandler = 0x0100,// updated for sb + + ZoneLineHandler = 0x0107, // updated for sb + ActionHandler = 0x0108,// updated for sb + + DiscoveryHandler = 0x0109,// updated for sb + + SkillHandler = 0x010B, // updated for sb + GMCommand1 = 0x010D,// updated for sb + GMCommand2 = 0x010E,// updated for sb + UpdatePositionHandler = 0x010F, // updated for sb + + InventoryModifyHandler = 0x0116, // updated for sb + + TalkEventHandler = 0x011F, // updated for sb + EmoteEventHandler = 0x0120, // updated for sb + WithinRangeEventHandler = 0x0121, // updated for sb + OutOfRangeEventHandler = 0x0122, // updated for sb + EnterTeriEventHandler = 0x0123, // updated for sb + + ReturnEventHandler = 0x0128, + TradeReturnEventHandler = 0x0129, + }; + + + struct FFXIVARR_PACKET_RAW + { + FFXIVARR_PACKET_SEGMENT_HEADER segHdr; + std::vector data; + }; + + /** + * Structural representation of the common header for IPC packet segments. + * NOTE: This is packet segment type 3. + * + * 0 4 6 8 12 16 + * +-------+-------+------+----------+---------------+---------------+ + * | 14 00 | type | ?? | serverId | timestamp | ??? | + * +-------+-------+------+----------+---------------+---------------+ + * | | + * : data : + * | | + * +-----------------------------------------------------------------+ + */ + struct FFXIVARR_IPC_HEADER + { + uint16_t reserved; + ServerIpcType type; + uint16_t unknown_2; + uint16_t serverId; + uint32_t timestamp; + uint32_t unknown_C; + }; + + inline ostream& operator<<(ostream& os, const FFXIVARR_IPC_HEADER& hdr) + { + return os.write(reinterpret_cast(&hdr), sizeof hdr); + } + + inline istream& operator>>(istream& is, FFXIVARR_IPC_HEADER& hdr) + { + return is.read(reinterpret_cast(&hdr), sizeof hdr); + } + +} /* Packets */ +} /* Network */ +} /* Core */ + +#endif /*_CORE_NETWORK_PACKETS_COMMON_H*/ diff --git a/src/servers/Server_Common/Connection.cpp b/src/servers/Server_Common/Connection.cpp new file mode 100644 index 00000000..6e4040b2 --- /dev/null +++ b/src/servers/Server_Common/Connection.cpp @@ -0,0 +1,210 @@ +#include "Connection.h" +#include "Hive.h" +#include +#include + +namespace Core { + namespace Network { + //----------------------------------------------------------------------------- + + Connection::Connection( HivePtr hive ) + : m_hive( hive ), + m_socket( hive->GetService() ), + m_io_strand( hive->GetService() ), + m_receive_buffer_size( 32000 ), + m_error_state( 0 ) + { + } + + Connection::~Connection() + { + } + + void Connection::Bind( const std::string & ip, uint16_t port ) + { + boost::asio::ip::tcp::endpoint endpoint( boost::asio::ip::address::from_string( ip ), port ); + m_socket.open( endpoint.protocol() ); + m_socket.set_option( boost::asio::ip::tcp::acceptor::reuse_address( false ) ); + m_socket.bind( endpoint ); + } + + void Connection::StartSend() + { + if( !m_pending_sends.empty() ) + { + boost::asio::async_write( m_socket, + boost::asio::buffer( m_pending_sends.front() ), + m_io_strand.wrap( boost::bind( &Connection::HandleSend, + shared_from_this(), + boost::asio::placeholders::error, + m_pending_sends.begin() ) ) ); + } + } + + void Connection::StartRecv( int32_t total_bytes ) + { + if( total_bytes > 0 ) + { + m_recv_buffer.resize( total_bytes ); + boost::asio::async_read( m_socket, + boost::asio::buffer( m_recv_buffer ), + m_io_strand.wrap( boost::bind( &Connection::HandleRecv, + shared_from_this(), + _1, + _2 ) ) ); + } + else + { + m_recv_buffer.resize( m_receive_buffer_size ); + m_socket.async_read_some( boost::asio::buffer( m_recv_buffer ), + m_io_strand.wrap( boost::bind( &Connection::HandleRecv, + shared_from_this(), + _1, + _2 ) ) ); + } + } + + void Connection::StartError( const boost::system::error_code & error ) + { + if( boost::interprocess::ipcdetail::atomic_cas32( &m_error_state, 1, 0 ) == 0 ) + { + boost::system::error_code ec; + m_socket.shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); + m_socket.close( ec ); + OnError( error ); + } + } + + void Connection::HandleConnect( const boost::system::error_code & error ) + { + if( error || HasError() || m_hive->HasStopped() ) + { + StartError( error ); + } + else + { + if( m_socket.is_open() ) + { + OnConnect( m_socket.remote_endpoint().address().to_string(), m_socket.remote_endpoint().port() ); + Recv(); + } + else + { + StartError( error ); + } + } + } + + void Connection::HandleSend( const boost::system::error_code & error, std::list< std::vector< uint8_t > >::iterator itr ) + { + if( error || HasError() || m_hive->HasStopped() ) + { + StartError( error ); + } + else + { + OnSend( *itr ); + m_pending_sends.erase( itr ); + StartSend(); + } + } + + void Connection::HandleRecv( const boost::system::error_code & error, int32_t actual_bytes ) + { + if( error || HasError() || m_hive->HasStopped() ) + { + StartError( error ); + } + else + { + m_recv_buffer.resize( actual_bytes ); + OnRecv( m_recv_buffer ); + Recv(); + m_pending_recvs.pop_front(); + if( !m_pending_recvs.empty() ) + { + StartRecv( m_pending_recvs.front() ); + } + } + } + + void Connection::DispatchSend( std::vector< uint8_t > buffer ) + { + bool should_start_send = m_pending_sends.empty(); + m_pending_sends.push_back( buffer ); + if( should_start_send ) + { + StartSend(); + } + } + + void Connection::DispatchRecv( int32_t total_bytes ) + { + bool should_start_receive = m_pending_recvs.empty(); + m_pending_recvs.push_back( total_bytes ); + if( should_start_receive ) + { + StartRecv( total_bytes ); + } + } + + + void Connection::Connect( const std::string & host, uint16_t port) + { + boost::asio::ip::tcp::resolver resolver( m_hive->GetService() ); + boost::asio::ip::tcp::resolver::query query( host, std::to_string( port ) ); + boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve( query ); + m_socket.async_connect( *iterator, m_io_strand.wrap( boost::bind( &Connection::HandleConnect, shared_from_this(), _1 ) ) ); + + } + + void Connection::Disconnect() + { + OnDisconnect(); + m_socket.close(); + } + + void Connection::Recv( int32_t total_bytes ) + { + m_io_strand.post( boost::bind( &Connection::DispatchRecv, shared_from_this(), total_bytes ) ); + } + + void Connection::Send( const std::vector< uint8_t > & buffer ) + { + m_io_strand.post( boost::bind( &Connection::DispatchSend, shared_from_this(), buffer ) ); + } + + boost::asio::ip::tcp::socket & Connection::GetSocket() + { + return m_socket; + } + + boost::asio::strand & Connection::GetStrand() + { + return m_io_strand; + } + + HivePtr Connection::GetHive() + { + return m_hive; + } + + void Connection::SetReceiveBufferSize( int32_t size ) + { + m_receive_buffer_size = size; + } + + int32_t Connection::GetReceiveBufferSize() const + { + return m_receive_buffer_size; + } + + bool Connection::HasError() + { + return ( boost::interprocess::ipcdetail::atomic_cas32( &m_error_state, 1, 1 ) == 1 ); + } + + + } +} +//----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/servers/Server_Common/Connection.h b/src/servers/Server_Common/Connection.h new file mode 100644 index 00000000..14e229fc --- /dev/null +++ b/src/servers/Server_Common/Connection.h @@ -0,0 +1,144 @@ +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "Forwards.h" +#include "Acceptor.h" + + +namespace Core { + namespace Network { + //----------------------------------------------------------------------------- + + class Hive; + class Acceptor; + class Connection; + + //----------------------------------------------------------------------------- + + class Connection : public boost::enable_shared_from_this< Connection > + { + friend class Acceptor; + friend class Hive; + + protected: + HivePtr m_hive; + boost::asio::ip::tcp::socket m_socket; + boost::asio::strand m_io_strand; + std::vector< uint8_t > m_recv_buffer; + std::list< int32_t > m_pending_recvs; + std::list< std::vector< uint8_t > > m_pending_sends; + int32_t m_receive_buffer_size; + volatile uint32_t m_error_state; + + + Connection( HivePtr hive ); + virtual ~Connection(); + + private: + Connection( const Connection & rhs ); + Connection & operator =( const Connection & rhs ); + void StartSend(); + void StartRecv( int32_t total_bytes ); + void StartError( const boost::system::error_code & error ); + void DispatchSend( std::vector< uint8_t > buffer ); + void DispatchRecv( int32_t total_bytes ); + void HandleConnect( const boost::system::error_code & error ); + void HandleSend( const boost::system::error_code & error, std::list< std::vector< uint8_t > >::iterator itr ); + void HandleRecv( const boost::system::error_code & error, int32_t actual_bytes ); + + + private: + // Called when the connection has successfully connected to the local host. + virtual void OnAccept( const std::string & host, uint16_t port ) {}; + + // Called when the connection has successfully connected to the remote host. + virtual void OnConnect( const std::string & host, uint16_t port ) {}; + + // Called when data has been sent by the connection. + virtual void OnSend( const std::vector< uint8_t > & buffer ) {}; + + // Called when data has been received by the connection. + virtual void OnRecv( std::vector< uint8_t > & buffer ) {}; + + // Called when an error is encountered. + virtual void OnError( const boost::system::error_code & error ) {}; + + // Called when the connection has been disconnected + virtual void OnDisconnect() {}; + + public: + // Returns the Hive object. + HivePtr GetHive(); + + // Returns the socket object. + boost::asio::ip::tcp::socket & GetSocket(); + + // Returns the strand object. + boost::asio::strand & GetStrand(); + + // Sets the application specific receive buffer size used. For stream + // based protocols such as HTTP, you want this to be pretty large, like + // 64kb. For packet based protocols, then it will be much smaller, + // usually 512b - 8kb depending on the protocol. The default value is + // 4kb. + void SetReceiveBufferSize( int32_t size ); + + // Returns the size of the receive buffer size of the current object. + int32_t GetReceiveBufferSize() const; + + // Returns true if this object has an error associated with it. + bool HasError(); + + // Binds the socket to the specified interface. + void Bind( const std::string & ip, uint16_t port ); + + // Starts an a/synchronous connect. + void Connect( const std::string & host, uint16_t port ); + + // Posts data to be sent to the connection. + void Send( const std::vector< uint8_t > & buffer ); + + // Posts a recv for the connection to process. If total_bytes is 0, then + // as many bytes as possible up to GetReceiveBufferSize() will be + // waited for. If Recv is not 0, then the connection will wait for exactly + // total_bytes before invoking OnRecv. + void Recv( int32_t total_bytes = 0 ); + + // Posts an asynchronous disconnect event for the object to process. + void Disconnect(); + }; + + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + + template + boost::shared_ptr< T > addServerToHive( const std::string& listenIp, uint32_t port, HivePtr pHive ) + { + try + { + AcceptorPtr acceptor( new Acceptor( pHive ) ); + acceptor->Listen( listenIp, port ); + boost::shared_ptr< T > connection( new T( pHive, acceptor ) ); + acceptor->Accept( connection ); + return connection; + } + catch( std::runtime_error e ) + { + throw; + } + } + + } + + +} +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/Database.cpp b/src/servers/Server_Common/Database.cpp new file mode 100644 index 00000000..af9ba2ab --- /dev/null +++ b/src/servers/Server_Common/Database.cpp @@ -0,0 +1,411 @@ +#include "Database.h" + +#include +#include + +#include +#include +#include + +#include "Logger.h" + +extern Core::Logger g_log; + +namespace Core { +namespace Db { + +QueryResult::QueryResult( MYSQL_RES *res, uint32_t fields, uint32_t rows ) + : m_result( res ), + m_fieldCount( fields ), + m_rowCount( rows ) +{ + m_currentRow = new Field[fields]; +} + +QueryResult::~QueryResult() +{ + mysql_free_result( m_result ); + delete[] m_currentRow; +} + +bool QueryResult::nextRow() +{ + MYSQL_ROW row = mysql_fetch_row( m_result ); + if( row == NULL ) + { + return false; + } + + for( uint32_t i = 0; i < m_fieldCount; ++i ) + { + m_currentRow[i].setValue( row[i] ); + } + + return true; +} + +Database::Database() +{ + m_port = 0; + _counter = 0; + m_pConnections = nullptr; + m_connectionCount = -1; // Not connected. +} + +Database::~Database() +{ + for( int32_t i = 0; i < m_connectionCount; ++i ) + { + if( m_pConnections[i].conn != nullptr ) + { + mysql_close( m_pConnections[i].conn ); + } + } + + delete[] m_pConnections; +} + +bool Database::initialize( const DatabaseParams& params ) +{ + uint32_t i; + MYSQL * temp; + MYSQL * temp2; + my_bool my_true = true; + + g_log.Log( Core::LoggingSeverity::info, "Database: Connecting to " + params.hostname + ", database " + params.databaseName + "..." ); + + m_pConnections = new DatabaseConnection[params.connectionCount]; + for( i = 0; i < params.connectionCount; ++i ) + { + temp = mysql_init( NULL ); + if( mysql_options( temp, MYSQL_SET_CHARSET_NAME, "utf8" ) ) + { + g_log.Log( Core::LoggingSeverity::error, "Database: Could not set utf8 character set." ); + } + + if( mysql_options( temp, MYSQL_OPT_RECONNECT, &my_true ) ) + { + g_log.Log( Core::LoggingSeverity::error, "Database: MYSQL_OPT_RECONNECT could not be set, connection drops may occur but will be counteracted." ); + } + + temp2 = mysql_real_connect( temp, + params.hostname.c_str(), + params.username.c_str(), + params.password.c_str(), + params.databaseName.c_str(), + params.port, + NULL, + 0 ); + if( temp2 == NULL ) + { + g_log.Log( Core::LoggingSeverity::fatal, "Database: Connection failed due to: `%s`" + std::string( mysql_error( temp ) ) ); + return false; + } + + m_pConnections[i].conn = temp2; + } + + + return true; +} + +uint64_t Database::getNextUId() +{ + execute( "INSERT INTO uniqueiddata( IdName ) VALUES( 'NOT_SET' );" ); + auto res = query( "SELECT LAST_INSERT_ID();" ); + + if( !res ) + { + return 0; + } + + Db::Field *field = res->fetch(); + + return field[0].getUInt64(); +} + +DatabaseConnection * Database::getFreeConnection() +{ + uint32_t i = 0; + for( ;;) + { + DatabaseConnection * con = &m_pConnections[( ( i++ ) % m_connectionCount )]; + if( con->lock.try_lock() ) + { + return con; + } + + // sleep every 20 iterations, otherwise this can cause 100% cpu if the db link goes dead + if( !( i % 20 ) ) + { + std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) ); + } + } + + // shouldn't be reached + return NULL; +} + +boost::shared_ptr Database::query( const std::string& QueryString ) +{ + + // Send the query + boost::shared_ptr qResult( nullptr ); + DatabaseConnection * con = getFreeConnection(); + + if( _SendQuery( con, QueryString.c_str(), false ) ) + { + qResult = boost::shared_ptr( _StoreQueryResult( con ) ); + } + + con->lock.unlock(); + return qResult; +} + +QueryResult * Database::queryNA( const char* QueryString ) +{ + // Send the query + QueryResult * qResult = NULL; + DatabaseConnection * con = getFreeConnection(); + + if( _SendQuery( con, QueryString, false ) ) + { + qResult = _StoreQueryResult( con ); + } + + con->lock.unlock(); + return qResult; +} + +QueryResult * Database::fQuery( const char * QueryString, DatabaseConnection * con ) { + // Send the query + QueryResult * qResult = NULL; + if( _SendQuery( con, QueryString, false ) ) + { + qResult = _StoreQueryResult( con ); + } + + return qResult; +} + +void Database::fWaitExecute( const char * QueryString, DatabaseConnection * con ) +{ + // Send the query + _SendQuery( con, QueryString, false ); +} + + +bool Database::execute( const char* QueryString, ... ) { + char query[16384]; + + va_list vlist; + va_start( vlist, QueryString ); + vsnprintf( query, 16384, QueryString, vlist ); + va_end( vlist ); + + + return waitExecuteNA( query ); + +} + +bool Database::execute( const std::string& QueryString ) +{ + return waitExecuteNA( QueryString.c_str() ); +} + +bool Database::executeNA( const char* QueryString ) +{ + + return waitExecuteNA( QueryString ); + +} + +//this will wait for completion +bool Database::waitExecute( const char* QueryString, ... ) +{ + char sql[16384]; + va_list vlist; + va_start( vlist, QueryString ); + vsnprintf( sql, 16384, QueryString, vlist ); + va_end( vlist ); + + DatabaseConnection * con = getFreeConnection(); + bool Result = _SendQuery( con, sql, false ); + con->lock.unlock(); + return Result; +} + +bool Database::waitExecuteNA( const char* QueryString ) +{ + DatabaseConnection * con = getFreeConnection(); + bool Result = _SendQuery( con, QueryString, false ); + con->lock.unlock(); + return Result; +} + + +void Database::freeQueryResult( QueryResult * p ) +{ + delete p; +} + +std::string Database::escapeString( std::string Escape ) +{ + char a2[16384] = { 0 }; + + DatabaseConnection * con = getFreeConnection(); + const char * ret; + if( mysql_real_escape_string( con->conn, a2, Escape.c_str(), ( unsigned long ) Escape.length() ) == 0 ) + { + ret = Escape.c_str(); + } + else { + ret = a2; + } + + con->lock.unlock(); + return std::string( ret ); +} + +void Database::escapeLongString( const char * str, uint32_t len, std::stringstream& out ) +{ + char a2[65536 * 3] = { 0 }; + + DatabaseConnection * con = getFreeConnection(); + mysql_real_escape_string( con->conn, a2, str, ( unsigned long ) len ); + + out.write( a2, ( std::streamsize )strlen( a2 ) ); + con->lock.unlock(); +} + +std::string Database::escapeString( const char * esc, DatabaseConnection * con ) +{ + char a2[16384] = { 0 }; + const char * ret; + if( mysql_real_escape_string( con->conn, a2, ( char* ) esc, ( unsigned long ) strlen( esc ) ) == 0 ) + { + ret = esc; + } + else + { + ret = a2; + } + + return std::string( ret ); +} + +bool Database::_SendQuery( DatabaseConnection *con, const char* Sql, bool Self ) +{ + //dunno what it does ...leaving untouched + int result = mysql_query( con->conn, Sql ); + if( result > 0 ) + { + if( Self == false && _HandleError( con, mysql_errno( con->conn ) ) ) + { + // Re-send the query, the connection was successful. + // The true on the end will prevent an endless loop here, as it will + // stop after sending the query twice. + result = _SendQuery( con, Sql, true ); + } + else + { + g_log.Log( Core::LoggingSeverity::error, "Database: query failed " + std::string( mysql_error( con->conn ) ) ); + g_log.Log( Core::LoggingSeverity::error, "\t" + std::string( Sql ) ); + } + } + + return ( result == 0 ? true : false ); +} + +bool Database::_HandleError( DatabaseConnection * con, uint32_t ErrorNumber ) +{ + // Handle errors that should cause a reconnect to the CDatabase. + switch( ErrorNumber ) { + case 2006: // Mysql server has gone away + case 2008: // Client ran out of memory + case 2013: // Lost connection to sql server during query + case 2055: // Lost connection to sql server - system error + { + // Let's instruct a reconnect to the db when we encounter these errors. + return _Reconnect( con ); + }break; + } + + return false; +} + +QueryResult * Database::_StoreQueryResult( DatabaseConnection * con ) +{ + QueryResult *res; + MYSQL_RES * pRes = mysql_store_result( con->conn ); + uint32_t uRows = ( uint32_t ) mysql_affected_rows( con->conn ); + uint32_t uFields = ( uint32_t ) mysql_field_count( con->conn ); + + if( uRows == 0 || uFields == 0 || pRes == 0 ) + { + if( pRes != NULL ) + { + mysql_free_result( pRes ); + } + + return NULL; + } + + res = new QueryResult( pRes, uFields, uRows ); + res->nextRow(); + + return res; +} + +bool Database::_Reconnect( DatabaseConnection * conn ) +{ + MYSQL * temp; + MYSQL * temp2; + + temp = mysql_init( NULL ); + + temp2 = mysql_real_connect( temp, + m_hostname.c_str(), + m_username.c_str(), + m_password.c_str(), + m_databaseName.c_str(), + m_port, + NULL, + 0 ); + if( temp2 == NULL ) + { + g_log.Log( Core::LoggingSeverity::error, "Database: Could not reconnect to database because of " + std::string( mysql_error( temp ) ) ); + mysql_close( temp ); + return false; + } + + if( conn->conn != NULL ) + { + mysql_close( conn->conn ); + } + + conn->conn = temp; + return true; +} + +void Database::cleanupLibs() +{ + mysql_library_end(); +} + + + +void Database::shutdown() +{ + for( int32_t i = 0; i < m_connectionCount; ++i ) + { + if( m_pConnections[i].conn != NULL ) + { + mysql_close( m_pConnections[i].conn ); + m_pConnections[i].conn = NULL; + } + } +} + +} +} diff --git a/src/servers/Server_Common/Database.h b/src/servers/Server_Common/Database.h new file mode 100644 index 00000000..5cea1373 --- /dev/null +++ b/src/servers/Server_Common/Database.h @@ -0,0 +1,242 @@ +#ifndef _DATABASE_H +#define _DATABASE_H + +#include +#include + +#include + +#include +#include + +namespace Core { + namespace Db { + + // CField is used to access db-query resultsets + class Field + { + public: + + // set value + __inline void setValue( char* value ) + { + m_pValue = value; + } + + // return as string + __inline const char *getString() + { + + return m_pValue ? m_pValue : ""; + } + + // return as string + __inline void getBinary( char* dstBuf, uint16_t size ) + { + if( m_pValue ) + { + memcpy( dstBuf, m_pValue, size ); + } + else + { + dstBuf = NULL; + } + } + + // return as float + __inline float getFloat() + { + return m_pValue ? static_cast< float >( atof( m_pValue ) ) : 0; + } + + // return as bool + __inline bool getBool() + { + return m_pValue ? atoi( m_pValue ) > 0 : false; + } + + // return as unsigned 8 bit integer + __inline uint8_t getUInt8() + { + return m_pValue ? static_cast< uint8_t >( atol( m_pValue ) ) : 0; + } + + // return as signed 8 bit integer + __inline int8_t getInt8() + { + return m_pValue ? static_cast< int8_t >( atol( m_pValue ) ) : 0; + } + + // return as unsigned 16 bit integer + __inline uint16_t getUInt16() + { + return m_pValue ? static_cast< uint16_t >( atol( m_pValue ) ) : 0; + } + + // return as signed 16 bit integer + __inline int16_t getInt16() + { + return m_pValue ? static_cast< int16_t >( atol( m_pValue ) ) : 0; + } + + // return as unsigned 32 bit integer + __inline uint32_t getUInt32() + { + return m_pValue ? static_cast< uint32_t >( atol( m_pValue ) ) : 0; + } + + // return as signed 8 bit integer + __inline int32_t getInt32() + { + return m_pValue ? static_cast< int32_t >( atol( m_pValue ) ) : 0; + } + + // return as unsigned 64 bit integer + uint64_t getUInt64() + { + if( m_pValue ) + { + #ifdef _WIN32 + uint64_t value; + sscanf( m_pValue, "%I64d", &value ); + return value; + #else + uint64_t value; + sscanf( m_pValue, "%Lu", &value ); + return value; + + #endif + } + else + return 0; + } + + private: + char *m_pValue; + }; + + + class QueryResult + { + public: + QueryResult( MYSQL_RES *res, uint32_t fields, uint32_t rows ); + ~QueryResult(); + + bool nextRow(); + void Delete() + { + delete this; + } + + __inline Field* fetch() + { + return m_currentRow; + } + __inline uint32_t getFieldCount() const + { + return m_fieldCount; + } + __inline uint32_t getRowCount() const + { + return m_rowCount; + } + + protected: + uint32_t m_fieldCount; + uint32_t m_rowCount; + Field *m_currentRow; + MYSQL_RES *m_result; + }; + + struct DatabaseConnection + { + std::mutex lock; + MYSQL *conn; + }; + + struct DatabaseParams + { + std::string hostname; + std::string username; + std::string password; + std::string databaseName; + uint16_t port; + uint32_t bufferSize; + uint32_t connectionCount; + }; + + class Database + { + public: + Database(); + virtual ~Database(); + + /************************************************************************/ + /* Virtual Functions */ + /************************************************************************/ + bool initialize( const DatabaseParams& params ); + + void shutdown(); + + boost::shared_ptr query( const std::string& QueryString ); + QueryResult* queryNA( const char* QueryString ); + QueryResult* fQuery( const char * QueryString, DatabaseConnection *con ); + void fWaitExecute( const char * QueryString, DatabaseConnection *con ); + bool waitExecute( const char* QueryString, ... );//Wait For Request Completion + bool waitExecuteNA( const char* QueryString );//Wait For Request Completion + bool execute( const char* QueryString, ... ); + bool execute( const std::string& QueryString ); + bool executeNA( const char* QueryString ); + + __inline const std::string& getHostName() + { + return m_hostname; + } + + __inline const std::string& getDatabaseName() + { + return m_databaseName; + } + + std::string escapeString( std::string Escape ); + void escapeLongString( const char * str, uint32_t len, std::stringstream& out ); + std::string escapeString( const char * esc, DatabaseConnection *con ); + + void freeQueryResult( QueryResult * p ); + + DatabaseConnection *getFreeConnection(); + + void cleanupLibs(); + + /* database is killed off manually. */ + void onShutdown() {} + + uint64_t getNextUId(); + + protected: + + // actual query function + bool _SendQuery( DatabaseConnection *con, const char* Sql, bool Self ); + QueryResult * _StoreQueryResult( DatabaseConnection * con ); + bool _HandleError( DatabaseConnection *conn, uint32_t ErrorNumber ); + bool _Reconnect( DatabaseConnection *conn ); + + DatabaseConnection *m_pConnections; + + uint32_t _counter; + /////////////////////////////// + + int32_t m_connectionCount; + + // For reconnecting a broken connection + std::string m_hostname; + std::string m_username; + std::string m_password; + std::string m_databaseName; + uint32_t m_port; + + }; + + } +} +#endif diff --git a/src/servers/Server_Common/ExdData.cpp b/src/servers/Server_Common/ExdData.cpp new file mode 100644 index 00000000..8cc1e6e8 --- /dev/null +++ b/src/servers/Server_Common/ExdData.cpp @@ -0,0 +1,672 @@ +#include "ExdData.h" +#include + +#include + +/* TYPES !! + case DataType::boolean: 1 + case DataType::int8: 2 + case DataType::uint8: 3 + case DataType::int16: 4 + case DataType::uint16: 5 + case DataType::int32: 6 + case DataType::uint32: 7 + case DataType::float32: 8 + case DataType::uint64: 9 + */ + + + +Core::Data::ExdData::ExdData() +{ +} + +Core::Data::ExdData::~ExdData( void ) +{ +} + +xiv::exd::Exd Core::Data::ExdData::setupDatAccess( const std::string& name, xiv::exd::Language lang ) +{ + auto& cat = m_exd_data->get_category( name ); + return static_cast< xiv::exd::Exd >( cat.get_data_ln( lang ) ); +}; + +bool Core::Data::ExdData::init( const std::string& path ) +{ + try + { + m_data = boost::make_shared< xiv::dat::GameData >( path ); + m_exd_data = boost::make_shared< xiv::exd::ExdData >( *m_data ); + + m_questDat = setupDatAccess( "Quest", xiv::exd::Language::en ); + m_openingDat = setupDatAccess( "Opening", xiv::exd::Language::none ); + m_customTalkDat = setupDatAccess( "CustomTalk", xiv::exd::Language::en ); + m_aetheryteDat = setupDatAccess( "Aetheryte", xiv::exd::Language::en ); + m_levelDat = setupDatAccess( "Level", xiv::exd::Language::none ); + m_placeNameDat = setupDatAccess( "PlaceName", xiv::exd::Language::en ); + m_itemsDat = setupDatAccess( "Item", xiv::exd::Language::en ); + m_classJobCatDat = setupDatAccess( "ClassJobCategory", xiv::exd::Language::en ); + m_raceDat = setupDatAccess( "Race", xiv::exd::Language::en ); + m_eventItemDat = setupDatAccess( "EventItem", xiv::exd::Language::en ); + } + catch( std::runtime_error ) + { + return false; + } + + return true; +} + +bool Core::Data::ExdData::loadZoneInfo() +{ + auto territoryTypeData = setupDatAccess( "TerritoryType", xiv::exd::Language::none ); + auto placeNameData = setupDatAccess( "PlaceName", xiv::exd::Language::en ); + auto mapData = setupDatAccess( "Map", xiv::exd::Language::none ); + auto weatherRate = setupDatAccess( "WeatherRate", xiv::exd::Language::none ); + + auto territoryTypeDataRows = territoryTypeData.get_rows(); + + for( auto row : territoryTypeDataRows ) + { + auto& fields = row.second; + uint32_t id = row.first; + + uint16_t place_id = getField< uint16_t >( fields, 5 ); + uint16_t map_id = getField< uint16_t >( fields, 6 ); + std::string zoneStr = getField< std::string >( fields, 0 ); + + if( zoneStr.empty() ) + continue; + + auto placeNameDataFields = placeNameData.get_row( place_id ); + std::string zone_str = getField< std::string >( placeNameDataFields, 0 ); + + auto mapDataFields = mapData.get_row( map_id ); + int16_t map_index = getField< int16_t >( mapDataFields, 12 ); + bool is_two_bytes = getField< bool >( mapDataFields, 15 ); + + uint8_t weather_rate = getField< uint8_t >( fields, 10 ) > 75 ? 0 : getField< uint8_t >( fields, 10 ); + auto weatherRateFields = weatherRate.get_row( weather_rate ); + + int32_t aetheryte_index = getField< int32_t >( fields, 20 ); + + + ZoneInfo info{ 0 }; + info.id = id; + info.zone_name = zoneStr; + info.zone_str = zone_str; + info.layout_id = id; + info.discovery_index = map_index; + info.is_two_byte = is_two_bytes; + info.map_id = map_id; + info.weather_rate = weather_rate; // TODO: deal with weather groups + info.aetheryte_index = aetheryte_index; + + uint8_t sumPc = 0; + for( size_t i = 0; i < 16; ) + { + int32_t weatherId = getField< int32_t >( weatherRateFields, i ); + + if( weatherId == 0 ) + break; + + sumPc += getField< uint8_t >( weatherRateFields, i + 1 ); + info.weather_rate_map[sumPc] = weatherId; + + i += 2; + } + + m_zoneInfoMap[id] = info; + + } + + return true; +} + +bool Core::Data::ExdData::loadStatusEffectInfo() +{ + auto StatusDat = setupDatAccess( "Status", xiv::exd::Language::en ); + auto rows1 = StatusDat.get_rows(); + + for( auto row : rows1 ) + { + auto& fields = row.second; + uint32_t id = row.first; + + StatusEffectInfo info { 0 }; + info.id = id; + info.name = getField< std::string >( fields, 0 ); + m_statusEffectInfoMap[id] = info; + } + + return true; +} + +bool Core::Data::ExdData::loadAetheryteInfo() +{ + auto AetheryteDat = setupDatAccess( "Aetheryte", xiv::exd::Language::en ); + auto rows = AetheryteDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + auto info = boost::make_shared< AetheryteInfo >(); + info->id = row.first; + info->target_zone = getField< uint16_t >( fields, 10 ); + + info->isAetheryte = getField< bool >( fields, 15 ); + + uint16_t placename = getField< uint16_t >( fields, 8 ); + auto placeNameInfo = getPlaceNameInfo( placename ); + + if( placeNameInfo ) + { + info->placename = placeNameInfo->placename; + } + + + uint16_t placename_aethernet = getField< uint16_t >( fields, 9 ); + + auto placename_aethernetInfo = getPlaceNameInfo( placename_aethernet ); + + if( placename_aethernetInfo ) + { + info->placename_aethernet = placename_aethernetInfo->placename; + } + + info->levelId = getField< uint32_t >( fields, 11 ); + + info->map_coord_x = getField< int16_t >( fields, 20 ); + info->map_coord_y = getField< int16_t >( fields, 21 ); + m_aetheryteInfoMap.emplace( std::make_pair( info->id, info ) ); + } + return true; +} + +bool Core::Data::ExdData::loadClassJobInfo() +{ + auto ClassJobDat = setupDatAccess( "ClassJob", xiv::exd::Language::en ); + auto rows = ClassJobDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + ClassJobInfo info { 0 }; + + uint32_t id = row.first; + + if( id == 0 ) + continue; + + std::string name = getField< std::string >( fields, 0 ); + std::string short_name = getField< std::string >( fields, 1 ); + int8_t exp_idx = getField< int8_t >( fields, 4 ); + + + info.id = id; + info.name = name; + info.name_short = short_name; + info.exp_idx = exp_idx; + info.start_weapon_id = getField< int32_t >( fields, 28 ); + info.mod_hp = getField< uint16_t >( fields, 9 ); + info.mod_mpcpgp = getField< uint16_t >( fields, 10 ); + info.mod_str = getField< uint16_t >( fields, 11 ); + info.mod_vit = getField< uint16_t >( fields, 12 ); + info.mod_dex = getField< uint16_t >( fields, 13 ); + info.mod_int = getField< uint16_t >( fields, 14 ); + info.mod_mnd = getField< uint16_t >( fields, 15 ); + info.mod_pie = getField< uint16_t >( fields, 16 ); + + m_classJobInfoMap[id] = info; + } + + return true; +} + +bool Core::Data::ExdData::loadParamGrowInfo() +{ + auto ParamGrowDat = setupDatAccess( "ParamGrow", xiv::exd::Language::none ); + auto rows = ParamGrowDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + + ParamGrowthInfo info { 0 }; + uint32_t id = row.first; + info.level = id; + info.needed_exp = getField< int32_t >( fields, 0 ); + info.piety_scalar = getField< uint16_t >( fields, 3 ); // 3 + info.mp_const = getField< int32_t >( fields, 4 ); // 4 + info.base_secondary = getField< int32_t >( fields, 5 );// 5 + info.hp_mod = getField< uint16_t >( fields, 8 ); // 8 + info.quest_exp_mod = getField< uint8_t >( fields, 7 ); // 7 + + + m_paramGrowthInfoMap[id] = info; + } + + return true; +} + +bool Core::Data::ExdData::loadTribeInfo() +{ + auto tribeDat = setupDatAccess( "Tribe", xiv::exd::Language::en ); + auto rows = tribeDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + + TribeInfo info { 0 }; + uint32_t id = row.first; + info.id = id; + info.name = getField< std::string >( fields, 0 ); + info.mod_str = getField< int8_t >( fields, 4 ); + info.mod_vit = getField< int8_t >( fields, 5 ); + info.mod_dex = getField< int8_t >( fields, 6 ); + info.mod_int = getField< int8_t >( fields, 7 ); + info.mod_mnd = getField< int8_t >( fields, 8 ); + info.mod_pie = getField< int8_t >( fields, 9 ); + + m_tribeInfoMap[id] = info; + } + + return true; +} + +bool Core::Data::ExdData::loadEventActionInfo() +{ + auto EventActionDat = setupDatAccess( "EventAction", xiv::exd::Language::en ); + auto rows = EventActionDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + + EventActionInfo info { 0 }; + + uint32_t id = row.first; + + if( id == 0 ) + { + continue; + } + + std::string name = getField< std::string >( fields, 0 ); + uint8_t time = getField< uint8_t >( fields, 2 ); + + info.id = id; + info.name = name; + info.castTime = time * 1000; + + m_EventActionInfoMap[id] = info; + + } + + return true; +} + +bool Core::Data::ExdData::loadActionInfo() +{ + auto ActionDat = setupDatAccess( "Action", xiv::exd::Language::en ); + auto rows = ActionDat.get_rows(); + + for( auto row : rows ) + { + auto& fields = row.second; + + ActionInfo info{ 0 }; + + uint32_t id = row.first; + if( id == 0 ) + { + continue; + } + + std::string name = getField< std::string >( fields, 0 ); + uint8_t category = getField< uint8_t >( fields, 3 ); + + int8_t class_job = getField< int8_t >( fields, 10 );//9 + uint8_t unlock_level = getField< uint8_t >( fields, 11 );//10 + int8_t range = getField< int8_t >( fields, 13 );//11 + bool can_target_self = getField< bool >( fields, 14 );//12 + bool can_target_party = getField< bool>( fields, 15 );//13 + bool can_target_friendly = getField< bool >( fields, 16 );//14 + bool can_target_enemy = getField< bool >( fields, 17 );//15 + + bool is_aoe = getField< bool >( fields, 20 );//18 + + bool can_target_ko = getField< bool >( fields, 24 );//22 + + uint8_t aoe_type = getField< uint8_t >( fields, 26 );//24 + uint8_t radius = getField< uint8_t >( fields, 27 );//25 + + uint8_t points_type = getField< uint8_t >( fields, 30 );//28 + uint16_t points_cost = getField< uint16_t >( fields, 31 );//29 + + bool is_instant = getField< bool >( fields, 35 ); + uint16_t cast_time = getField< uint16_t >( fields, 36 ); + uint16_t recast_time = getField< uint16_t >( fields, 37 ); + + info.id = id; + info.name = name; + info.category = category; + + info.class_job = class_job; + info.unlock_level = unlock_level; + info.range = range; + info.can_target_self = can_target_self; + info.can_target_party = can_target_party; + info.can_target_friendly = can_target_friendly; + info.can_target_enemy = can_target_enemy; + + info.can_target_ko = can_target_ko; + + info.is_aoe = is_aoe; + + info.aoe_type = aoe_type; + info.radius = radius; + + info.points_type = points_type; + info.points_cost = points_cost; + + info.is_instant = is_instant; + info.cast_time = cast_time * 100; + info.recast_time = recast_time * 100; + + m_actionInfoMap[id] = info; + + } + + return true; +} + +boost::shared_ptr< Core::Data::PlaceNameInfo > + Core::Data::ExdData::getPlaceNameInfo( uint32_t placeNameId ) +{ + try + { + auto row = m_placeNameDat.get_row( placeNameId ); + auto info = boost::make_shared< PlaceNameInfo >(); + info->id = placeNameId; + info->placename = getField< std::string >( row, 0 ); + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; + +} + +boost::shared_ptr< Core::Data::ItemInfo > + Core::Data::ExdData::getItemInfo( uint32_t catalogId ) +{ + try + { + auto row = m_itemsDat.get_row( catalogId ); + auto info = boost::make_shared< ItemInfo >(); + info->id = catalogId; + info->name = getField< std::string >( row, 0 ); + info->item_level = getField< uint16_t >( row, 11 ); + info->required_level = getField< uint8_t >( row, 12 ); + info->ui_category = getField< uint8_t >( row, 17 ); + info->stack_size = getField< uint32_t >( row, 19 ); + info->is_hqable = getField< bool >( row, 20 ); + info->model_primary = getField< uint64_t >( row, 45 ); + info->model_secondary = getField< uint64_t >( row, 46 ); + info->delayMs = getField< uint16_t >( row, 51 ); + info->is_unique = getField< int16_t >( row, 64 ) != 0 ? true : false; + info->is_untradeable = getField< uint8_t >( row, 65 ) != 0 ? true : false; + + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; +} + +boost::shared_ptr< Core::Data::AetheryteInfo > + Core::Data::ExdData::getAetheryteInfo( uint32_t aetheryteId ) +{ + try + { + return m_aetheryteInfoMap[aetheryteId]; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; + +} + +boost::shared_ptr< Core::Data::CustomTalkInfo > + Core::Data::ExdData::getCustomTalkInfo( uint32_t customTalkId ) +{ + + try + { + auto row = m_customTalkDat.get_row( customTalkId ); + auto info = boost::make_shared< CustomTalkInfo >(); + info->id = customTalkId; + info->name_intern = getField< std::string >( row, 2 ); + + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; + +} + +boost::shared_ptr< Core::Data::QuestInfo > + Core::Data::ExdData::getQuestInfo( uint32_t questId ) +{ + + + try + { + auto row = m_questDat.get_row( questId ); + auto info = boost::make_shared< QuestInfo >(); + info->id = questId; + info->name = getField< std::string >( row, 0 ); + info->name_intern = getField< std::string >( row, 1 ); + + info->quest_level = getField< uint16_t >( row, 4 ); + + info->enpc_resident_start = getField< uint32_t >( row, 38 ); + info->enpc_resident_end = getField< uint32_t >( row, 40 ); + + info->reward_exp_factor = getField< uint16_t >( row, 1437 ); + info->reward_gil = getField< uint32_t >( row, 1438 ); + info->reward_gc_seals = getField< uint16_t >( row, 1440 ); + + info->reward_item_type = getField< uint8_t >( row, 1447 ); + + for( uint32_t i = 0; i < 6; i++ ) + { + uint32_t entry = getField< uint32_t >( row, i + 1448 ); + if( entry > 0 ) + info->reward_item.push_back( entry ); + + uint8_t entry1 = getField< uint8_t >( row, i + 1455 ); + if( entry1 > 0 ) + info->reward_item_count.push_back( entry1 ); + + uint8_t entry2 = getField< uint8_t >( row, i + 1462 ); + if( entry2 > 0 ) + info->reward_item_stain.push_back( entry2 ); + + } + + for( uint32_t i = 0; i < 5; i++ ) + { + uint32_t entry = getField< uint32_t >( row, i + 1469 ); + if( entry > 0 ) + info->reward_item_optional.push_back( entry ); + + uint8_t entry1 = getField< uint8_t >( row, i + 1474 ); + if( entry1 > 0 ) + info->reward_item_optional_count.push_back( entry1 ); + + uint8_t entry2 = getField< uint8_t >( row, i + 1484 ); + if( entry2 > 0 ) + info->reward_item_optional_stain.push_back( entry2 ); + } + + info->reward_emote = getField< uint8_t >( row, 1489 ); + info->reward_action = getField< uint16_t >( row, 1490 ); + info->reward_action_general1 = getField< uint8_t >( row, 1491 ); + info->reward_action_general2 = getField< uint8_t >( row, 1492 ); + info->reward_other = getField< uint8_t >( row, 1494 ); + + info->instanced_content_unlock = getField< uint32_t >( row, 1497 ); + + info->reward_tome_type = getField< uint8_t >( row, 1499 ); + info->reward_tome_count = getField< uint8_t >( row, 1500 ); + + info->reward_reputation = getField< uint8_t >( row, 1501 ); + + for( uint32_t i = 0; i < 50; i++ ) + { + std::string entry = getField< std::string >( row, i + 47 ); + if( entry.size() > 0 ) + { + info->script_entity.push_back( entry ); + uint32_t entry1 = getField< uint32_t >( row, i + 97 ); + info->script_value.push_back( entry1 ); + } + } + + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; + +} + +boost::shared_ptr< Core::Data::EventItemInfo > + Core::Data::ExdData::getEventItemInfo( uint32_t eventItemId ) +{ + + try + { + auto row = m_eventItemDat.get_row( eventItemId ); + auto info = boost::make_shared< EventItemInfo >(); + info->id = eventItemId; + info->name = getField< std::string >( row, 0 ); + info->eventId = getField< uint32_t >( row, 14 ); + info->castTime = getField< uint8_t >( row, 15 ) * 1000; + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; + +} + +boost::shared_ptr< Core::Data::LevelInfo > + Core::Data::ExdData::getLevelInfo( uint32_t levelId ) +{ + try + { + auto row = m_levelDat.get_row( levelId ); + auto info = boost::make_shared< LevelInfo >(); + info->id = levelId; + info->x = getField< float >( row, 0 ); + info->y = getField< float >( row, 1 ); + info->z = getField< float >( row, 2 ); + info->r = getField< float >( row, 3 ); + info->actor_id = getField< uint32_t >( row, 6 ); + info->zone_id = getField< uint16_t >( row, 9 ); + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; +} + +const std::vector > Core::Data::ExdData::getAetheryteInfoForZone( uint16_t zoneId ) +{ + std::vector< boost::shared_ptr< AetheryteInfo > > aetheryteList; + + for( auto& aetheryteInfo : m_aetheryteInfoMap ) + { + if( aetheryteInfo.second->target_zone == zoneId ) + { + aetheryteList.push_back( aetheryteInfo.second ); + } + } + return aetheryteList; +} + + +boost::shared_ptr< Core::Data::OpeningInfo > + Core::Data::ExdData::getOpeningInfo( uint32_t openingId ) +{ + try + { + auto row = m_openingDat.get_row( openingId ); + auto info = boost::make_shared< OpeningInfo >(); + info->id = openingId; + info->name = getField< std::string >( row, 0 ); + + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; +} + +boost::shared_ptr< Core::Data::RaceInfo > + Core::Data::ExdData::getRaceInfo( uint32_t raceId ) +{ + try + { + + auto row = m_raceDat.get_row( raceId ); + auto info = boost::make_shared< RaceInfo >(); + info->id = raceId; + info->name = getField< std::string >( row, 0 ); + info->male_body = getField< int32_t >( row, 2 ); + info->male_hands = getField< int32_t >( row, 3 ); + info->male_legs = getField< int32_t >( row, 4 ); + info->male_feet = getField< int32_t >( row, 5 ); + info->female_body = getField< int32_t >( row, 6 ); + info->female_hands = getField< int32_t >( row, 7 ); + info->female_legs = getField< int32_t >( row, 8 ); + info->female_feet = getField< int32_t >( row, 9 ); + + return info; + } + catch( ... ) + { + return nullptr; + } + + return nullptr; +} diff --git a/src/servers/Server_Common/ExdData.h b/src/servers/Server_Common/ExdData.h new file mode 100644 index 00000000..781b62e7 --- /dev/null +++ b/src/servers/Server_Common/ExdData.h @@ -0,0 +1,329 @@ +#ifndef _EXDDATA_H +#define _EXDDATA_H + +#include +#include +#include +#include +#include + +/* TYPES !! +case DataType::boolean: 1 +case DataType::int8: 2 +case DataType::uint8: 3 +case DataType::int16: 4 +case DataType::uint16: 5 +case DataType::int32: 6 +case DataType::uint32: 7 +case DataType::float32: 8 +case DataType::uint64: 9 +*/ + + +namespace Core { + + namespace Data { + + struct ZoneInfo + { + uint16_t id; + std::string zone_str; + std::string zone_name; + uint32_t layout_id; + uint16_t map_id; + int16_t discovery_index; + bool is_two_byte; + uint8_t weather_rate; + std::map< uint8_t, int32_t> weather_rate_map; + + int32_t aetheryte_index; + }; + + struct ClassJobInfo + { + uint8_t id; + std::string name; + std::string name_short; + uint8_t exp_idx; + uint32_t start_weapon_id; + uint16_t mod_hp; + uint16_t mod_mpcpgp; + uint16_t mod_str; + uint16_t mod_vit; + uint16_t mod_dex; + uint16_t mod_int; + uint16_t mod_mnd; + uint16_t mod_pie; + }; + + struct QuestInfo + { + uint32_t id; + std::string name; + std::string name_intern; + uint16_t quest_level; + uint32_t enpc_resident_start; + uint32_t enpc_resident_end; + uint16_t reward_exp_factor; + uint32_t reward_gil; + uint16_t reward_gc_seals; + uint8_t reward_item_type; + + std::vector< uint32_t > reward_item; + std::vector< uint8_t > reward_item_count; + std::vector< uint8_t > reward_item_stain; + + std::vector< uint32_t > reward_item_optional; + std::vector< uint8_t > reward_item_optional_count; + std::vector< uint8_t > reward_item_optional_stain; + + uint8_t reward_emote; + uint16_t reward_action; + uint8_t reward_action_general1; + uint8_t reward_action_general2; + + uint8_t reward_other; + + uint32_t instanced_content_unlock; + + uint8_t reward_tome_type; + uint8_t reward_tome_count; + + uint8_t reward_reputation; + + + + std::vector< std::string > script_entity; + std::vector< uint32_t > script_value; + }; + + struct EventActionInfo + { + uint32_t id; + std::string name; + uint32_t castTime; + }; + + struct OpeningInfo + { + uint32_t id; + std::string name; + }; + + struct ParamGrowthInfo + { + uint32_t level; + uint32_t needed_exp; + int16_t hp_mod; + int32_t mp_const; + int16_t piety_scalar; + int32_t base_secondary; + uint16_t quest_exp_mod; + }; + + struct CustomTalkInfo + { + uint32_t id; + std::string name_intern; + }; + + struct PlaceNameInfo + { + uint32_t id; + std::string placename; + }; + + struct AetheryteInfo + { + uint32_t id; + std::string placename; + std::string placename_aethernet; + int32_t target_zone; + int32_t levelId; + bool isAetheryte; + + int16_t map_coord_x; + int16_t map_coord_y; + }; + + struct RaceInfo + { + uint32_t id; + std::string name; + uint32_t male_body; + uint32_t male_hands; + uint32_t male_legs; + uint32_t male_feet; + uint32_t female_body; + uint32_t female_hands; + uint32_t female_legs; + uint32_t female_feet; + + int8_t mod_str; + int8_t mod_dex; + int8_t mod_vit; + int8_t mod_int; + int8_t mod_mnd; + int8_t mod_pie; + }; + + struct TribeInfo + { + uint32_t id; + std::string name; + + int8_t mod_str; + int8_t mod_dex; + int8_t mod_vit; + int8_t mod_int; + int8_t mod_mnd; + int8_t mod_pie; + }; + + struct LevelInfo + { + uint32_t id; + float x; + float y; + float z; + float r; + uint32_t actor_id; + uint32_t zone_id; + }; + + struct ClassJobCategoryInfo + { + uint32_t id; + // should they add new jobs, this needs to be changed + bool can_equip[0x33]; + }; + + struct ItemInfo + { + uint32_t id; + std::string name; //0 + uint16_t item_level;//11 + uint8_t required_level;//12 + uint32_t stack_size;//19 + uint16_t unknown_category;//15 + uint16_t ui_category;//17 + bool is_hqable;//20 + uint64_t model_primary;//28 + uint64_t model_secondary;//29 + uint32_t class_job_requirement;//58 + uint16_t delayMs; //59 + bool is_unique;//72 + bool is_untradeable;//73 + uint32_t class_job_index;//86 + }; + + struct ActionInfo + { + uint32_t id; + std::string name; //0 + uint16_t category;//3 + + int8_t class_job;//9 + uint8_t unlock_level;//10 + int8_t range;//11 + bool can_target_self;//12 + bool can_target_party;//13 + bool can_target_friendly;//14 + bool can_target_enemy;//15 + + bool is_aoe;//18 + + bool can_target_ko;//22 + + uint8_t aoe_type;//24 + uint8_t radius;//25 + + uint8_t points_type;//28 + uint16_t points_cost;//29 + + bool is_instant;//33 + uint32_t cast_time;//34 + uint32_t recast_time;//35 + }; + + struct EventItemInfo + { + uint32_t id; + std::string name; //0 + uint32_t eventId; + uint32_t castTime; + }; + + struct StatusEffectInfo + { + uint32_t id; + std::string name; //0 + }; + + class ExdData + { + public: + ExdData(); + ~ExdData(void); + + bool init( const std::string& path ); + + xiv::exd::Exd setupDatAccess( const std::string& name, xiv::exd::Language lang ); + + template< class T > + T getField( std::vector< xiv::exd::Field >& fields, uint32_t index ) + { + return *boost::get< T >( &fields.at( index ) ); + } + + boost::shared_ptr m_data; + boost::shared_ptr m_exd_data; + + xiv::exd::Exd m_questDat; + xiv::exd::Exd m_openingDat; + xiv::exd::Exd m_customTalkDat; + xiv::exd::Exd m_aetheryteDat; + xiv::exd::Exd m_levelDat; + xiv::exd::Exd m_placeNameDat; + xiv::exd::Exd m_itemsDat; + xiv::exd::Exd m_classJobCatDat; + xiv::exd::Exd m_raceDat; + xiv::exd::Exd m_eventItemDat; + + std::map m_zoneInfoMap; + std::map m_classJobInfoMap; + std::map m_paramGrowthInfoMap; + std::map m_EventActionInfoMap; + std::map m_actionInfoMap; + std::map m_statusEffectInfoMap; + std::map > m_aetheryteInfoMap; + std::map m_tribeInfoMap; + + bool loadZoneInfo(); + bool loadClassJobInfo(); + bool loadParamGrowInfo(); + bool loadEventActionInfo(); + bool loadActionInfo(); + bool loadStatusEffectInfo(); + bool loadAetheryteInfo(); + bool loadTribeInfo(); + + boost::shared_ptr< QuestInfo > getQuestInfo( uint32_t questId ); + boost::shared_ptr< OpeningInfo > getOpeningInfo( uint32_t openingId ); + boost::shared_ptr< CustomTalkInfo > getCustomTalkInfo( uint32_t customTalkId ); + boost::shared_ptr< AetheryteInfo > getAetheryteInfo( uint32_t aetheryteId ); + boost::shared_ptr< PlaceNameInfo > getPlaceNameInfo( uint32_t placeNameId ); + boost::shared_ptr< ItemInfo > getItemInfo( uint32_t catalogId ); + boost::shared_ptr< RaceInfo > getRaceInfo( uint32_t raceId ); + boost::shared_ptr< EventItemInfo > getEventItemInfo( uint32_t eventItemId ); + boost::shared_ptr< LevelInfo > getLevelInfo( uint32_t levelId ); + + const std::vector< boost::shared_ptr< AetheryteInfo > > getAetheryteInfoForZone( uint16_t zoneId ); + + }; + + } +} + +#endif + diff --git a/src/servers/Server_Common/Forwards.h b/src/servers/Server_Common/Forwards.h new file mode 100644 index 00000000..515bfe0e --- /dev/null +++ b/src/servers/Server_Common/Forwards.h @@ -0,0 +1,38 @@ +#ifndef _COMMON_FORWARDS_H +#define _COMMON_FORWARDS_H + +#include + +namespace Core +{ + + class XMLConfig; + + typedef boost::shared_ptr XMLConfigPtr; + + + namespace Network + { + class Hive; + class Acceptor; + class Connection; + + typedef boost::shared_ptr HivePtr; + typedef boost::shared_ptr AcceptorPtr; + typedef boost::shared_ptr ConnectionPtr; + + namespace Packets + { + class GamePacket; + + typedef boost::shared_ptr GamePacketPtr; + } + + } + + + +} + + +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/GamePacket.cpp b/src/servers/Server_Common/GamePacket.cpp new file mode 100644 index 00000000..a7084c72 --- /dev/null +++ b/src/servers/Server_Common/GamePacket.cpp @@ -0,0 +1,111 @@ + +#include +#include "GamePacket.h" + +#include +#include +#include + +Core::Network::Packets::GamePacket::GamePacket( uint16_t subType, uint16_t size, uint32_t id1, uint32_t id2, uint16_t type ) +{ + m_dataBuf = std::vector( size ); + memset( &m_segHdr, 0, sizeof( m_segHdr ) ); + + setHeader( size, type, id1, id2, subType, 0x00 ); + +} + +void Core::Network::Packets::GamePacket::setHeader( uint16_t size, uint16_t type, uint32_t id1, uint32_t id2, uint16_t subType, uint32_t unknown ) +{ + + m_segHdr.size = size; + m_segHdr.type = type; + m_segHdr.source_actor = id1; + m_segHdr.target_actor = id2; + m_segHdr._reserved_E = 0x00; + m_subType = subType; + + m_timeStamp = static_cast< uint32_t >( time( nullptr ) ); + if( size > 0 ) + { + memcpy( &m_dataBuf[0], &m_segHdr, sizeof( m_segHdr ) ); + m_dataBuf[0x10] = 0x14; + *reinterpret_cast< uint16_t* >( &m_dataBuf[0] + 0x12 ) = m_subType; + } + if( size > 0x18 ) + *reinterpret_cast< uint32_t* >( &m_dataBuf[0] + 0x18 ) = m_timeStamp; +} + +Core::Network::Packets::GamePacket::GamePacket( char * pData, uint16_t size, bool bWriteStamp ) +{ + + m_dataBuf = std::vector( size ); + memcpy( &m_dataBuf[0], pData, size ); + m_unknown2 = 0; + + if( bWriteStamp && size > 0x18 ) + { + m_timeStamp = static_cast< uint32_t >( time( nullptr ) ); + *reinterpret_cast< uint32_t* >( &m_dataBuf[0] + 0x18 ) = m_timeStamp; + } + + m_segHdr._reserved_E = 0; + m_segHdr.size = *reinterpret_cast< uint32_t* >( &m_dataBuf[0] ); + m_segHdr.type = *reinterpret_cast< uint16_t* >( &m_dataBuf[0] + 0x0C ); + m_subType = *reinterpret_cast< uint16_t* >( &m_dataBuf[0] + 0x12 ); + + m_segHdr.source_actor = *reinterpret_cast< uint32_t* >( &m_dataBuf[0] + 0x04 ); + m_segHdr.target_actor = *reinterpret_cast< uint32_t* >( &m_dataBuf[0] + 0x08 ); + +} + +Core::Network::Packets::GamePacket::GamePacket( const Packets::FFXIVARR_PACKET_RAW& packetData ) +{ + + m_segHdr = packetData.segHdr; + m_dataBuf = std::vector( m_segHdr.size ); + + memcpy( &m_dataBuf[0] + sizeof( Packets::FFXIVARR_PACKET_SEGMENT_HEADER ), &packetData.data[0], m_segHdr.size - sizeof( Packets::FFXIVARR_PACKET_SEGMENT_HEADER ) ); + memcpy( &m_dataBuf[0], &m_segHdr, sizeof( Packets::FFXIVARR_PACKET_SEGMENT_HEADER ) ); + + m_subType = *reinterpret_cast< uint16_t* >( &m_dataBuf[0] + 0x12 ); + + if( m_segHdr.size > 0x18 ) + m_timeStamp = *reinterpret_cast< uint32_t* >( &m_dataBuf[0] + 0x18 ); +} + +Core::Network::Packets::GamePacket::GamePacket() +{ +} + + +Core::Network::Packets::GamePacket::~GamePacket() +{ +} + +void Core::Network::Packets::GamePacket::savePacket() +{ + char filename[20]; + sprintf( filename, "dump_0x%x.dat", m_subType ); + FILE * fp = nullptr; + fp = fopen( filename, "wb" ); + fwrite( &m_dataBuf[0], 1, m_segHdr.size, fp ); + fclose( fp ); +} + + +std::string Core::Network::Packets::GamePacket::toString() +{ + + std::string str = "\n"; + for( unsigned int i = 0; i < getSize(); i++ ) + { + str += boost::str( boost::format( "%|02X|" ) % ( int ) ( m_dataBuf[i] & 0xFF ) ) + " "; + + if( ( i + 1 ) % 16 == 0 ) + str += "\n"; + } + str += "\n"; + + return str; +} diff --git a/src/servers/Server_Common/GamePacket.h b/src/servers/Server_Common/GamePacket.h new file mode 100644 index 00000000..bee8675a --- /dev/null +++ b/src/servers/Server_Common/GamePacket.h @@ -0,0 +1,92 @@ +#ifndef _GAMEPACKET_H_ +#define _GAMEPACKET_H_ + +#include "CommonNetwork.h" +#include "Forwards.h" +#include +#include + +namespace Core { +namespace Network { +namespace Packets { + +class GamePacket { +public: + GamePacket( uint16_t subType, uint16_t size, uint32_t id1, uint32_t id2, uint16_t type = 0x03 ); + GamePacket( char * pData, uint16_t size, bool bWriteStamp = true ); + + GamePacket( const Packets::FFXIVARR_PACKET_RAW& packetData ); + + GamePacket( void ); + ~GamePacket( void ); + + uint16_t getSize() + { + return m_segHdr.size; + } + + uint16_t getType() + { + return m_segHdr.type; + } + + uint16_t getSubType() + { + return m_subType; + } + + template + void setValAt( uint16_t pos, T value ) + { + memcpy( reinterpret_cast< unsigned char* >( &m_dataBuf[0] + pos ), &value, sizeof( T ) ); + } + + template + T getValAt( uint16_t pos ) + { + return *reinterpret_cast< T* >( &m_dataBuf[0] + pos ); + } + + void setBytesAt( uint16_t offset, unsigned char * bytes, uint16_t length ) + { + memcpy( reinterpret_cast< unsigned char* >( &m_dataBuf[0] + offset ), bytes, length ); + } + + char * getStringAt( uint16_t pos ) + { + return reinterpret_cast< char* >( &m_dataBuf[0] + pos ); + } + + void setStringAt( uint16_t pos, const std::string& str ) + { + memcpy( reinterpret_cast< unsigned char* >( &m_dataBuf[0] + pos ), str.c_str(), str.length() ); + } + + unsigned char * getData() + { + return reinterpret_cast< unsigned char* >( &m_dataBuf[0] ); + } + + void setHeader( uint16_t size, uint16_t type, uint32_t id1, uint32_t id2, uint16_t subType, uint32_t unknown = 0xFED2E000 ); + + std::string toString(); + + void savePacket(); + + FFXIVARR_PACKET_SEGMENT_HEADER m_segHdr; + +protected: + + uint16_t m_unknown2; + uint16_t m_subType; + uint32_t m_timeStamp; + std::vector m_dataBuf; + +}; + + + +} +} +} +#endif diff --git a/src/servers/Server_Common/GamePacketNew.h b/src/servers/Server_Common/GamePacketNew.h new file mode 100644 index 00000000..7fd9a8d9 --- /dev/null +++ b/src/servers/Server_Common/GamePacketNew.h @@ -0,0 +1,234 @@ +#ifndef _GAMEPACKETNEW_H +#define _GAMEPACKETNEW_H + +#include +#include + +#include "GamePacket.h" + +#include +#include + +namespace Core { +namespace Network { +namespace Packets { + +// Must forward define these in order to enable the compiler to produce the +// correct template functions. + +template +class GamePacketNew; + +template +std::ostream& operator<< ( std::ostream& os, const GamePacketNew& packet ); + +/** +* The base implementation of a game packet. Needed for parsing packets. +*/ +class GamePacketNewBase +{ +public: + virtual ~GamePacketNewBase() = default; + /** + * @brief Gets the IPC type of this packet. (Useful for determining the + * type of a parsed packet.) + */ + virtual ServerIpcType ipcType( void ) = 0; +}; + +/** +* A game packet, or IPC packet, object is a template class for constructing +* the data to be sent or parsed. The template works by accepting a structure +* type that represents just the IPC data portion (the bytes after the initial +* 32 byte header information.) +*/ +template +class GamePacketNew : public GamePacketNewBase +{ +public: + /** + * @brief Constructs a new game packet with the specified actors. + * @param sourceActorId The source actor id. + * @param targetActorId The target actor id. + */ + GamePacketNew( uint32_t sourceActorId, uint32_t targetActorId ) + { + initialize(); + m_segHdr.source_actor = sourceActorId; + m_segHdr.target_actor = targetActorId; + }; + + /** + * @brief Constructs a new game packet with the specified actors. + * @param sourceActorId The source and target actor id. + */ + GamePacketNew( uint32_t bothActorId ) + { + initialize(); + m_segHdr.source_actor = bothActorId; + m_segHdr.target_actor = bothActorId; + }; + +protected: + /** Initializes the fields of the header structures */ + virtual void initialize( void ) + { + // Zero out the structures. + memset( &m_segHdr, 0, sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ) ); + memset( &m_ipcHdr, 0, sizeof( FFXIVARR_IPC_HEADER ) ); + memset( &m_data, 0, sizeof( T ) ); + + // Set the values of static fields. + // The size must be the sum of the segment header, the ipc header, and + // the IPC data itself. + m_segHdr.size = sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ) + sizeof( FFXIVARR_IPC_HEADER ) + sizeof( T ); + // Game packets (IPC) are type 3. + m_segHdr.type = 3; + // The IPC type itself. + m_ipcHdr.type = static_cast< ServerIpcType >( m_data._ServerIpcType ); + }; + +public: + virtual ServerIpcType ipcType( void ) + { + return static_cast< ServerIpcType >( m_data._ServerIpcType ); + }; + + /** Gets a reference to the underlying IPC data structure. */ + T& data( void ) { return m_data; }; + + /** + * @brief Sets the source actor id for this IPC packet. + * @param actorId The source actor id. + * @return This IPC packet object (can be used for chaining). + */ + GamePacketNew sourceActor( uint32_t actorId ) + { + m_segHdr.source_actor = actorId; + return this; + }; + + /** + * @brief Gets the source actor id for this IPC packet. + * @return The source actor id. + */ + uint32_t sourceActor( void ) const + { + return m_segHdr.source_actor; + }; + + /** + * @brief Sets the target actor id for this IPC packet. + * @param actorId The target actor id. + * @return This IPC packet object (can be used for chaining). + */ + GamePacketNew targetActor( uint32_t actorId ) + { + m_segHdr.target_actor = actorId; + return this; + }; + + /** + * @brief Gets the target actor id for this IPC packet. + * @return The target actor id. + */ + uint32_t targetActor( void ) const + { + return m_segHdr.target_actor; + }; + + friend std::ostream& operator<< <> ( std::ostream& os, const GamePacketNew& packet ); + + friend class GamePacketFactory; + + /** + * @brief Adapts the new-style packet into the old style. + */ + operator GamePacket* ( ) const + { + std::ostringstream buf; + serialize( buf ); + // NOTE: This should be ok because CGamePacket's constructor will + // copy the contents of the buffer. + GamePacket* pOldStyle = new GamePacket( const_cast< char* >( buf.str().c_str() ), m_segHdr.size, false ); + return pOldStyle; + }; + + operator GamePacketPtr () const + { + std::ostringstream buf; + serialize( buf ); + // NOTE: This should be ok because CGamePacket's constructor will + // copy the contents of the buffer. + GamePacketPtr pOldStyle( new GamePacket( const_cast< char* >( buf.str().c_str() ), m_segHdr.size, true ) ); + return pOldStyle; + }; + +protected: + // TODO: Is this a waste of storage memory? + /** The segment header */ + FFXIVARR_PACKET_SEGMENT_HEADER m_segHdr; + /** The IPC packet header */ + FFXIVARR_IPC_HEADER m_ipcHdr; + /** The underlying data portion of the packet as a structure */ + T m_data; + +private: + std::ostream& serialize( std::ostream& os ) const + { + // Since the packet itself is constant, we need to make a copy of the IPC + // header in order to set the timestamp. + FFXIVARR_IPC_HEADER ipcHdr; + memcpy( &ipcHdr, &m_ipcHdr, sizeof( ipcHdr ) ); + + // TODO: Fixed timestamp? Can we use a manipulator on the stream to assign + // a fixed timestamp value. This might be useful if several packets must + // be sent having the exact same timestamp. (Maybe this doesn't really + // need to happen though...) + ipcHdr.timestamp = static_cast< uint32_t >( time( nullptr ) ); + + // TODO: What about encryption? compression? + // Ideally, these could come directly from the stream using manipulators. + // We could check the stream's flags, and perform the appropriate + // operations here. The snag is encryption, which does not occur for + // segment headers, but may occur for IPC headers, and their data. + // Compression occurs for the entire segment header down. + os << m_segHdr << ipcHdr; + return os.write( reinterpret_cast< const char* >( &m_data ), sizeof( T ) ); + }; +}; + +template +std::ostream& operator<<( std::ostream& os, const GamePacketNew& packet ) +{ +#if 0 + // Since the packet itself is constant, we need to make a copy of the IPC + // header in order to set the timestamp. + FFXIVARR_IPC_HEADER ipcHdr; + memcpy( &ipcHdr, &packet.m_ipcHdr, sizeof( ipcHdr ) ); + + // TODO: Fixed timestamp? Can we use a manipulator on the stream to assign + // a fixed timestamp value. This might be useful if several packets must + // be sent having the exact same timestamp. (Maybe this doesn't really + // need to happen though...) + ipcHdr.timestamp = time( NULL ); + + // TODO: What about encryption? compression? + // Ideally, these could come directly from the stream using manipulators. + // We could check the stream's flags, and perform the appropriate + // operations here. The snag is encryption, which does not occur for + // segment headers, but may occur for IPC headers, and their data. + // Compression occurs for the entire segment header down. + os << packet.m_segHdr << ipcHdr; + return os.write( + reinterpret_cast< const char* >( &packet.m_data ), sizeof( T ) ); +#else + return packet.serialize( os ); +#endif +} + +} /* Packets */ +} /* Network */ +} /* Core */ + +#endif /*_CORE_NETWORK_PACKETS_CGAMEPACKETNEW_H*/ \ No newline at end of file diff --git a/src/servers/Server_Common/Hive.cpp b/src/servers/Server_Common/Hive.cpp new file mode 100644 index 00000000..41cf0e25 --- /dev/null +++ b/src/servers/Server_Common/Hive.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include "Hive.h" + +namespace Core { + namespace Network { + + //----------------------------------------------------------------------------- + + Hive::Hive() + : m_work_ptr( new boost::asio::io_service::work( m_io_service ) ), + m_shutdown( 0 ) + { + } + + Hive::~Hive() + { + } + + boost::asio::io_service & Hive::GetService() + { + return m_io_service; + } + + bool Hive::HasStopped() + { + return ( boost::interprocess::ipcdetail::atomic_cas32( &m_shutdown, 1, 1 ) == 1 ); + } + + void Hive::Poll() + { + m_io_service.poll(); + } + + void Hive::Run() + { + m_io_service.run(); + } + + void Hive::Stop() + { + if( boost::interprocess::ipcdetail::atomic_cas32( &m_shutdown, 1, 0 ) == 0 ) + { + m_work_ptr.reset(); + m_io_service.run(); + m_io_service.stop(); + } + } + + void Hive::Reset() + { + if( boost::interprocess::ipcdetail::atomic_cas32( &m_shutdown, 0, 1 ) == 1 ) + { + m_io_service.reset(); + m_work_ptr.reset( new boost::asio::io_service::work( m_io_service ) ); + } + } + + } +} \ No newline at end of file diff --git a/src/servers/Server_Common/Hive.h b/src/servers/Server_Common/Hive.h new file mode 100644 index 00000000..0448cfc8 --- /dev/null +++ b/src/servers/Server_Common/Hive.h @@ -0,0 +1,54 @@ +#ifndef HIVE_H_ +#define HIVE_H_ + +#include + +#include + +namespace Core { + namespace Network { + + class Hive : public boost::enable_shared_from_this< Hive > + { + private: + boost::asio::io_service m_io_service; + boost::shared_ptr< boost::asio::io_service::work > m_work_ptr; + volatile uint32_t m_shutdown; + + private: + Hive( const Hive & rhs ); + Hive & operator =( const Hive & rhs ); + + public: + Hive(); + virtual ~Hive(); + + // Returns the io_service of this object. + boost::asio::io_service & GetService(); + + // Returns true if the Stop function has been called. + bool HasStopped(); + + // Polls the networking subsystem once from the current thread and + // returns. + void Poll(); + + // Runs the networking system on the current thread. This function blocks + // until the networking system is stopped, so do not call on a single + // threaded application with no other means of being able to call Stop + // unless you code in such logic. + void Run(); + + // Stops the networking system. All work is finished and no more + // networking interactions will be possible afterwards until Reset is called. + void Stop(); + + // Restarts the networking system after Stop as been called. A new work + // object is created ad the shutdown flag is cleared. + void Reset(); + }; + + } +} +//----------------------------------------------------------------------------- +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/LockedQueue.h b/src/servers/Server_Common/LockedQueue.h new file mode 100644 index 00000000..02cd24f9 --- /dev/null +++ b/src/servers/Server_Common/LockedQueue.h @@ -0,0 +1,124 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace Core { + + template + class LockedQueue + { + public: + LockedQueue(); + ~LockedQueue(); + + T pop(); + + //we can pass this in by reference, instead of copying + void push(const T object); + + //we can pass this in by reference + //this will push it onto the queue, and swap the object + // with a default-constructed T at the same time. + void push_swap(T& object); + void push_reset(T& object); + + + std::size_t size(); + + + protected: + std::queue m_queue; + std::mutex m_mutex; + + }; + + template + LockedQueue::LockedQueue() + { + + } + + template + std::size_t LockedQueue::size() + { + std::lock_guard lock( m_mutex); + return m_queue.size(); + } + + + template + LockedQueue::~LockedQueue() + { + + + } + + + template + T LockedQueue::pop() + { + std::lock_guard lock( m_mutex); + + if( m_queue.empty()) + { + return T(); + } + + T result = m_queue.front(); + + m_queue.pop(); + + return result; + } + + template + void LockedQueue::push(const T object) + { + std::lock_guard lock( m_mutex); + m_queue.push(object); + } + + template + void LockedQueue::push_swap(T& object) + { + std::lock_guard lock( m_mutex); + + m_queue.push(object); + + T default_ctored_object = T(); + //this is a special swap that will do a legit naive swap normally, + // except if there exists a function called T::swap(), which is + // specialized and possibly faster. + boost::swap(object, default_ctored_object); + + + + //default_ctored_object is now the value of object, and it will go out + // of scope here. In the case that T is a shared_ptr of some kind, + // this will allow that the object on the queue is the *last* shared_ptr + // in existance by the time this function returns. + + } + + template + void LockedQueue::push_reset(T& object) + { + std::lock_guard lock( m_mutex); + + m_queue.push(object); + + T default_ctored_object = T(); + + object.reset(); + + //default_ctored_object is now the value of object, and it will go out + // of scope here. In the case that T is a shared_ptr of some kind, + // this will allow that the object on the queue is the *last* shared_ptr + // in existance by the time this function returns. + + } + +} \ No newline at end of file diff --git a/src/servers/Server_Common/Logger.cpp b/src/servers/Server_Common/Logger.cpp new file mode 100644 index 00000000..6beb7a6e --- /dev/null +++ b/src/servers/Server_Common/Logger.cpp @@ -0,0 +1,88 @@ +#include "Logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Core { + + + Logger::Logger() + { + + } + + Logger::~Logger() + { + + } + + void Logger::setLogPath(const std::string& logPath) + { + m_logFile = logPath; + } + + void Logger::init() + { + + auto format = ( + boost::log::expressions::stream << + boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%H:%M:%S]") << + "[" << boost::log::trivial::severity << "] " << + boost::log::expressions::smessage + ); + + boost::log::add_file_log + ( + boost::log::keywords::file_name = m_logFile + "%Y-%m-%d.log", /*< file name pattern >*/ + boost::log::keywords::rotation_size = 10 * 1024 * 1024, /*< rotate files every 10 MiB... >*/ + boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), /*< ...or at midnight >*/ + boost::log::keywords::open_mode = std::ios::app, + boost::log::keywords::format = format, + boost::log::keywords::auto_flush = true + ); + + boost::log::add_console_log(std::cout, boost::log::keywords::format = format); + + boost::log::add_common_attributes(); + } + + + void Logger::Log(LoggingSeverity logSev, const std::string& text) + { + BOOST_LOG_SEV(m_lg, (boost::log::trivial::severity_level)logSev) << text; + } + + void Logger::error( const std::string& text ) + { + BOOST_LOG_SEV( m_lg, boost::log::trivial::severity_level::error ) << text; + } + + void Logger::info( const std::string& text ) + { + BOOST_LOG_SEV( m_lg, boost::log::trivial::severity_level::info ) << text; + } + + void Logger::debug( const std::string& text ) + { + BOOST_LOG_SEV( m_lg, boost::log::trivial::severity_level::debug ) << text; + } + + void Logger::fatal( const std::string& text ) + { + BOOST_LOG_SEV( m_lg, boost::log::trivial::severity_level::fatal ) << text; + } + + + +} \ No newline at end of file diff --git a/src/servers/Server_Common/Logger.h b/src/servers/Server_Common/Logger.h new file mode 100644 index 00000000..731ba5d0 --- /dev/null +++ b/src/servers/Server_Common/Logger.h @@ -0,0 +1,51 @@ +#ifndef _LOGGER_H +#define _LOGGER_H + +#include + +namespace Core { + + + enum struct LoggingSeverity : uint8_t + { + trace = 0, + debug = 1, + info = 2, + warning = 3, + error = 4, + fatal = 5 + }; + + class Logger + { + + private: + boost::log::sources::severity_logger_mt< boost::log::trivial::severity_level > m_lg; + + std::string m_logFile; + + public: + Logger(); + + ~Logger(); + + void init(); + + void Log(LoggingSeverity logSev, const std::string& text); + + void error( const std::string& text ); + void info( const std::string& text ); + void debug( const std::string& text ); + void fatal( const std::string& text ); + + void setLogPath(const std::string& logPath); + + }; + + + +} + + + +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/PacketContainer.cpp b/src/servers/Server_Common/PacketContainer.cpp new file mode 100644 index 00000000..956f548d --- /dev/null +++ b/src/servers/Server_Common/PacketContainer.cpp @@ -0,0 +1,79 @@ +#include "PacketContainer.h" +#include "GamePacket.h" +#include "Common.h" + +#include + +#include + + +Core::Network::Packets::PacketContainer::PacketContainer( void ) +{ + memset( &m_ipcHdr, 0, sizeof( FFXIVARR_PACKET_HEADER ) ); + m_ipcHdr.size = sizeof( FFXIVARR_PACKET_HEADER ); + m_ipcHdr.count = 0; +} + +Core::Network::Packets::PacketContainer::~PacketContainer( void ) +{ + m_entryList.clear(); +} + +void Core::Network::Packets::PacketContainer::addPacket( GamePacket pEntry ) +{ + m_entryList.push_back( pEntry ); + + m_ipcHdr.size += pEntry.getSize(); + m_ipcHdr.count++; +} + +void Core::Network::Packets::PacketContainer::fillSendBuffer( std::vector< uint8_t >& sendBuffer ) +{ + uint8_t* tempBuffer = new uint8_t[m_ipcHdr.size]; + memset( tempBuffer, 0, m_ipcHdr.size ); + + using namespace std::chrono; + auto ms = duration_cast< milliseconds >( system_clock::now().time_since_epoch() ); + uint64_t tick = ms.count(); + m_ipcHdr.timestamp = tick; + m_ipcHdr.unknown_20 = 1; + + memcpy( tempBuffer, &m_ipcHdr, sizeof( FFXIVARR_PACKET_HEADER ) ); + + auto it = m_entryList.begin(); + uint16_t offset = 0; + + if( m_entryList.size() > 1 ) + offset = 0; + + for( ; it != m_entryList.end(); ++it ) + { + memcpy( tempBuffer + sizeof( FFXIVARR_PACKET_HEADER ) + offset, it->getData(), it->m_segHdr.size ); + offset += it->m_segHdr.size; + } + + sendBuffer.assign( tempBuffer, tempBuffer + m_ipcHdr.size ); + + delete[] tempBuffer; + +} + +std::string Core::Network::Packets::PacketContainer::toString() +{ + std::vector< uint8_t > tmpBuffer; + + fillSendBuffer( tmpBuffer ); + + std::string str = "\n"; + for( unsigned int i = 0; i < m_ipcHdr.size; i++ ) + { + str += boost::str( boost::format( "%|02X|" ) % static_cast< int >( tmpBuffer[i] & 0xFF ) ) + " "; + + if( ( i + 1 ) % 16 == 0 ) + str += "\n"; + } + str += "\n"; + + return str; +} + diff --git a/src/servers/Server_Common/PacketContainer.h b/src/servers/Server_Common/PacketContainer.h new file mode 100644 index 00000000..a00e0b88 --- /dev/null +++ b/src/servers/Server_Common/PacketContainer.h @@ -0,0 +1,38 @@ +#ifndef _PacketContainer_H_ +#define _PacketContainer_H_ + +#include + +#include "Common.h" +#include "CommonNetwork.h" +#include "GamePacket.h" + +namespace Core { +namespace Network { +namespace Packets { + +class GamePacket; + +class PacketContainer +{ +public: + PacketContainer( void ); + ~PacketContainer( void ); + + void addPacket( GamePacket pEntry ); + + FFXIVARR_PACKET_HEADER m_ipcHdr; + + std::vector m_entryList; + + std::string toString(); + + void fillSendBuffer( std::vector< uint8_t >& sendBuffer ); + +}; + +} +} +} + +#endif diff --git a/src/servers/Server_Common/ServerPacketDef.h b/src/servers/Server_Common/ServerPacketDef.h new file mode 100644 index 00000000..b2984aca --- /dev/null +++ b/src/servers/Server_Common/ServerPacketDef.h @@ -0,0 +1,1365 @@ +/** +* IPC Packet Definitions (Server IPC Packets) +* Structural definitions for the various server IPC packet types. +* IMPORTANT NOTE: Do not use these for parsing IPC packets sent by the +* client. See the Client namespace for those types! +*/ +#ifndef _CORE_NETWORK_PACKETS_SERVER_IPC_H +#define _CORE_NETWORK_PACKETS_SERVER_IPC_H + +#include "Common.h" +#include "CommonNetwork.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +template struct FFXIVIpcBasePacket +{ + /** Creates a constant representing the IPC type */ + enum { _ServerIpcType = T }; +}; + +struct FFXIVIpcRetainerList : FFXIVIpcBasePacket +{ + uint8_t padding[0x210]; +}; + +/** +*/ +struct FFXIVIpcServiceIdInfo : FFXIVIpcBasePacket +{ + uint64_t seq; + uint8_t padding; + uint8_t numServiceAccounts; + uint8_t u1; + uint8_t u2; + uint32_t padding1; + + struct + { + uint32_t id; + uint32_t unknown; + uint32_t index; + char name[0x44]; + } serviceAccount[8]; +}; + + +struct FFXIVIpcServerList : FFXIVIpcBasePacket +{ + uint64_t seq; + uint16_t final; + uint16_t offset; + uint32_t numServers; + uint32_t padding; + uint32_t padding1; + + struct + { + uint16_t id; + uint16_t index; + uint32_t flags; // 0x02 = World not accepting new characters + uint32_t padding1; + uint32_t icon; // 2 = bonus XP star + uint32_t padding2; + char name[0x40]; + } server[6]; +}; + +struct FFXIVIpcCharList : FFXIVIpcBasePacket +{ + uint64_t seq; + uint8_t counter; // current packet count * 4, count * 4 +1 on last packet. + uint8_t numInPacket; // always 2?? + uint16_t padding; + uint8_t unknown1; + uint8_t unknown2; + uint8_t unknown3; + uint8_t unknown4; // 0x80 in case of last packet + uint32_t unknown5[7]; + uint8_t unknown6; // 0x80 in case of last packet + uint8_t veteranRank; + uint8_t unknown7; + uint8_t padding1; + uint32_t daysSubscribed; + uint32_t remainingDays; + uint32_t daysToNextRank; + uint16_t maxCharOnWorld; + uint16_t unknown8; + uint32_t entitledExpansion; + uint32_t padding2; + + struct CharaDetails + { + uint32_t uniqueId; + uint32_t padding; + uint64_t contentId; + uint32_t index; + uint32_t padding2; + uint16_t serverId; + char nameChara[32]; + char nameServer[32]; + char charDetailJson[1030]; + } charaDetails[2]; + +}; + +struct FFXIVIpcEnterWorld : FFXIVIpcBasePacket +{ + uint64_t seq; + uint32_t charId; + uint32_t padding; + uint64_t contentId; + uint32_t padding2; + char sid[66]; + uint16_t port; + char host[48]; + uint64_t padding3; + uint64_t padding4; +}; + +struct FFXIVIpcCharCreate : FFXIVIpcBasePacket +{ + uint64_t seq; + uint8_t unknown; + uint8_t unknown_2; + uint8_t type; + uint8_t padding; + uint32_t unknown_3; + uint32_t unknown_4; + uint32_t unknown_5; + uint64_t content_id; + uint16_t unknown_7; + uint16_t unknown_8; + uint32_t unknown_9; + uint16_t unknown_10; + char name[32]; + char world[32]; +}; + +struct FFXIVIpcLobbyError : FFXIVIpcBasePacket +{ + uint64_t seq; + uint32_t error_id; + uint32_t param; + uint16_t message_id; + char message[516]; +}; + +/** +* Structural representation of the packet sent by the server as response +* to a ping packet +*/ +struct FFXIVIpcPing : FFXIVIpcBasePacket +{ + /* 0000 */ uint64_t timeInMilliseconds; + /* 0008 */ uint8_t unknown_8[0x38]; +}; + +/** +* Structural representation of the packet sent by the server as response +* to a ping packet +*/ +struct FFXIVIpcInit : FFXIVIpcBasePacket +{ + uint64_t unknown; + uint32_t charId; + uint32_t unknown1; +}; + +/** +* Structural representation of the packet sent by the server +* carrying chat messages +*/ +struct FFXIVIpcChat : FFXIVIpcBasePacket +{ + /* 0000 */ Common::ChatType chatType; + uint8_t padding[16]; //Maybe this is SubCode, or some kind of talker ID... + char name[32]; + char msg[1012]; +}; + +/** +* Structural representation of the packet sent by the server +* carrying chat messages +*/ +struct FFXIVIpcLogout : FFXIVIpcBasePacket +{ + uint32_t flags1; + uint32_t flags2; +}; + +/** +* Structural representation of the packet sent by the server +* sent to show the play time +*/ +struct FFXIVIpcPlayTime : FFXIVIpcBasePacket +{ + uint32_t playTimeInMinutes; + uint32_t padding; +}; + + +/** +* Structural representation of the packet sent by the server +* with a list of players ( party list | friend list | search results ) +*/ +struct PlayerEntry { + uint64_t contentId; + char bytes[12]; + uint16_t zoneId; + uint16_t zoneId1; + char bytes1[8]; + uint64_t onlineStatusMask; + uint8_t classJob; + uint8_t padding; + uint8_t level; + uint8_t padding1; + uint16_t padding2; + uint8_t one; + char name[0x20]; + char fcTag[9]; +}; + +struct FFXIVIpcSocialList : FFXIVIpcBasePacket +{ + uint32_t padding; + uint32_t padding1; + uint32_t padding2; + uint8_t type; + uint8_t sequence; + uint16_t padding3; + + PlayerEntry entries[10]; +}; + + +struct FFXIVIpcSetSearchInfo : FFXIVIpcBasePacket +{ + uint64_t onlineStatusFlags; + uint64_t unknown; + uint32_t unknown1; + uint8_t padding; + uint8_t selectRegion; + char searchMessage[193]; + uint8_t padding2; +}; + +struct FFXIVIpcInitSearchInfo : FFXIVIpcBasePacket +{ + uint64_t onlineStatusFlags; + uint64_t unknown; + uint8_t unknown1; + uint8_t selectRegion; + char searchMessage[193]; + char padding[5]; +}; + +/** +* Structural representation of the packet sent by the server +* to display a server notice message +*/ +struct FFXIVIpcServerNotice : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t padding; + char message[307]; +}; + +struct FFXIVIpcSetOnlineStatus : FFXIVIpcBasePacket +{ + uint64_t onlineStatusFlags; +}; + +struct FFXIVIpcBlackList : FFXIVIpcBasePacket +{ + struct BlEntry + { + uint64_t contentId; + char name[32]; + } entry[20]; + uint8_t padding; + uint8_t padding1; + uint16_t sequence; + uint32_t padding2; +}; + +struct FFXIVIpcLinkshellList : FFXIVIpcBasePacket +{ + struct LsEntry + { + uint64_t lsId; + uint64_t unknownId; + uint8_t unknown; + uint8_t rank; + uint16_t padding; + uint8_t lsName[20]; + } entry[8]; +}; + +struct FFXIVIpcStatusEffectList : FFXIVIpcBasePacket +{ + uint8_t classId; + uint8_t classId1; + uint16_t level; + uint32_t current_hp; + uint32_t max_hp; + uint16_t current_mp; + uint16_t max_mp; + uint16_t currentTp; + uint16_t unknown1; + Common::StatusEffect effect[30]; + uint32_t padding; +}; + +struct FFXIVGCAffiliation : FFXIVIpcBasePacket +{ + uint8_t gcId; + uint8_t gcRank[3]; +}; + +/** +* Structural representation of the packet sent by the server +* add a status effect +*/ +struct FFXIVIpcAddStatusEffect : FFXIVIpcBasePacket +{ + uint32_t unknown; + uint32_t actor_id; + uint8_t unknown1; + uint8_t unknown2; + uint16_t padding1; + uint32_t current_hp; + uint16_t current_mp; + uint16_t current_tp; + uint32_t max_hp; + uint16_t max_mp; + uint16_t max_something; + uint8_t effect_index; // which position do i display this + uint8_t unknown3; + uint16_t effect_id; + uint32_t power; + float duration; + uint32_t actor_id1; + uint8_t unknown4[52]; +}; + +/** +* Structural representation of the packet sent by the server +* to update certain player details / status +*/ +struct FFXIVIpcActorControl142 : FFXIVIpcBasePacket +{ + /* 0000 */ uint16_t category; + /* 0002 */ uint16_t padding; + /* 0004 */ uint32_t param1; + /* 0008 */ uint32_t param2; + /* 000C */ uint32_t param3; + /* 0010 */ uint32_t param4; + /* 0014 */ uint32_t padding1; +}; + +/** +* Structural representation of the packet sent by the server +* to update certain player details / status +*/ +struct FFXIVIpcActorControl143 : FFXIVIpcBasePacket +{ + /* 0000 */ uint16_t category; + /* 0002 */ uint16_t padding; + /* 0004 */ uint32_t param1; + /* 0008 */ uint32_t param2; + /* 000C */ uint32_t param3; + /* 0010 */ uint32_t param4; + /* 0014 */ uint32_t param5; + /* 0018 */ uint32_t param6; + /* 0018 */ uint32_t padding1; +}; + +/** +* Structural representation of the packet sent by the server +* to update certain player details / status +*/ +struct FFXIVIpcActorControl144 : FFXIVIpcBasePacket +{ + /* 0000 */ uint16_t category; + /* 0002 */ uint16_t padding; + /* 0004 */ uint32_t param1; + /* 0008 */ uint32_t param2; + /* 000C */ uint32_t param3; + /* 0010 */ uint32_t param4; + /* 0014 */ uint32_t padding1; + /* 0018 */ uint64_t targetId; +}; + +/** +* Structural representation of the packet sent by the server +* to update HP / MP / TP +*/ +struct FFXIVIpcUpdateHpMpTp : FFXIVIpcBasePacket +{ + /* 0000 */ uint32_t hp; + /* 0004 */ uint16_t mp; + /* 0006 */ uint16_t tp; + /* 0008 */ uint32_t unknown_8; + /* 000C */ uint32_t unknown_12; +}; + + +/** +* Structural representation of the packet sent by the server +* to update HP / MP / TP +*/ +struct effectEntry +{ + uint8_t unknown_1; + uint8_t unknown_2; + uint8_t unknown_3; + int8_t bonusPercent; + int16_t param1; + uint8_t unknown_5; + uint8_t unknown_6; +}; + +struct FFXIVIpcEffect : FFXIVIpcBasePacket +{ + uint32_t targetId; + uint32_t unknown_1; + uint32_t actionAnimationId; + uint32_t unknown_2; + uint32_t unknown_5; + uint32_t unknown_6; + uint32_t effectTargetId; + uint16_t rotation; + uint16_t actionTextId; + uint8_t unknown_61; + uint8_t unknown_62; + uint8_t unknown_10; + uint8_t numEffects; + uint32_t u11; + effectEntry effects[8]; + uint32_t effectTarget; + uint64_t unknown_8; +}; + + +/** +* Structural representation of the packet sent by the server +* to spawn an actor +*/ +struct FFXIVIpcPlayerSpawn : FFXIVIpcBasePacket +{ + uint16_t title; + uint16_t u1b; + uint8_t gmRank; + uint8_t u2ab; + uint8_t u2b; + uint8_t onlineStatus; + + uint8_t pose; + uint8_t u3b; + uint8_t u3c; + uint8_t u3d; + + uint32_t u4; + + uint64_t targetId; + uint32_t u6; + uint32_t u7; + uint64_t mainWeaponModel; + uint64_t secWeaponModel; + uint64_t craftToolModel; + + uint32_t u14; + uint32_t u15; + uint32_t bNPCBase; + uint32_t bNPCName; + uint32_t u18; + uint32_t u19; + uint32_t u20; + uint32_t ownerId; + uint32_t u22; + uint32_t hPCurr; + uint32_t hPMax; + uint32_t displayFlags; + uint16_t fateID; + uint16_t mPCurr; + uint16_t tPCurr; + uint16_t mPMax; + uint16_t tPMax; + uint16_t modelChara; + uint16_t rotation; + uint16_t activeMinion; + uint8_t spawnIndex; + uint8_t state; + uint8_t persistantEmote; + uint8_t type; + uint8_t subtype; + uint8_t voice; + uint16_t u25c; + uint8_t enemyType; + uint8_t level; + uint8_t classJob; + uint8_t u26d; + uint16_t u27a; + uint8_t currentMount; + uint8_t mountHead; + uint8_t mountBody; + uint8_t mountFeet; + uint8_t mountColor; + uint8_t scale; + uint32_t u29b; + Common::StatusEffect effect[30]; + Common::FFXIVARR_POSITION3 pos; + uint32_t models[10]; + char name[32]; + uint8_t look[26]; + char fcTag[6]; + uint32_t unk30; +}; + +/** +* Structural representation of the packet sent by the server +* to spawn an actor +*/ +struct FFXIVIpcNpcSpawn : FFXIVIpcBasePacket +{ + uint16_t title; + uint16_t u1b; + uint16_t u2a; + uint16_t u2b; + + uint8_t pose; + uint8_t u3b; + uint8_t u3c; + uint8_t u3d; + + uint32_t u4; + + uint64_t targetId; + uint32_t u6; + uint32_t u7; + + uint64_t mainWeaponModel; + uint64_t secWeaponModel; + uint64_t craftToolModel; + + uint32_t u14; + uint32_t u15; + uint32_t bNPCBase; + uint32_t bNPCName; + uint32_t u18; + uint32_t u19; + uint32_t u20; + uint32_t u21; + uint32_t u22; + + uint32_t hPCurr; + uint32_t hPMax; + uint32_t displayFlags; + uint16_t fateID; + uint16_t mPCurr; + uint16_t tPCurr; + uint16_t mPMax; + + uint16_t unk21a; + uint16_t modelChara; + uint16_t rotation; + uint16_t unk22b; + uint8_t spawnIndex; + uint8_t state; // ActorState + uint8_t u24a; + uint8_t type; // 1 for player, 2 for NPC, else furniture; + uint8_t subtype; // 4 for players, 2 for pet, 3 for companion, 5 for mob, 7 for minion + uint8_t u25b; + uint16_t u25c; + + uint8_t enemyType; // 0 for friendly, anything else is an enemy + uint8_t level; + uint8_t classJob; + uint8_t u26d; + + uint16_t u27a; + + uint8_t currentMount; + uint8_t mountHead; + uint8_t mountBody; + uint8_t mountFeet; + uint16_t mountColor; + + uint32_t u29b; + Common::StatusEffect effect[30]; + Common::FFXIVARR_POSITION3 pos; + uint32_t models[10]; + char name[32]; + uint8_t look[26]; + char fcTag[6]; + uint32_t unk30; +}; + +struct FFXIVIpcActorSpawn : FFXIVIpcBasePacket +{ + uint32_t unknown_0; + uint32_t nameId; + uint32_t bnpcBaseId; + uint32_t unknown_C[2]; + uint32_t ownerId; + uint32_t unknown_E[2]; + uint64_t targetId; + uint32_t fateId; + uint8_t gmRank; + uint8_t spawnIndex; + uint8_t status; /** 1 = alive, 2 = dead, 3 = use pose*/ + uint8_t pose; /** sitting, dancing, etc */ + uint8_t mobAgressive; /** 0 passive, 2 agressive */ + uint8_t mobTypeIcon; /** normal, nm, etc */ + uint8_t type; /** 0 = nothing, 1 = player, 2 = npc*/ + uint8_t unknown_33; + uint32_t unknown_34; + uint8_t voice; + uint8_t scale; + uint8_t v1; + uint8_t v2; + uint32_t unknown_38_1; + int32_t unknown_89; + int32_t unknown_90; + uint64_t mainWeaponModel; + uint64_t secWeaponModel; + uint64_t craftToolModel; + uint16_t rotation; + uint16_t model; /** 0 if look array is being used */ + uint16_t title; + uint8_t typeFlags; /** has something to do with type 4 for mobs */ + uint8_t minion; + uint8_t unknown_60; /** for players 01 */ + uint8_t unknown_61; /** for players 09 */ + uint8_t level; + uint8_t classJob; + uint32_t hPCurr; + uint16_t mPCurr; + uint16_t tPCurr; + uint32_t hPMax; + uint16_t mPMax; + uint16_t tPMax; + Common::OnlineStatus statusIcon; + uint8_t unknown_B0[17]; + uint8_t persistantPose; + uint8_t unknown_C0; + uint32_t displayFlags; + Common::StatusEffect effect[30]; + uint8_t currentMount; + uint8_t mountHead; + uint8_t mountBody; + uint8_t mountFeet; + uint32_t mountColor; + uint32_t unknown_236; + char name[32]; + uint8_t look[28]; + uint32_t models[10]; + Common::FFXIVARR_POSITION3 pos; + char fcTag[6]; + uint8_t unknown_250[10]; +}; + + +/** +* Structural representation of the packet sent by the server +* to show player movement +*/ +struct FFXIVIpcActorFreeSpawn : FFXIVIpcBasePacket +{ + uint32_t spawnId; + uint32_t actorId; +}; + +/** +* Structural representation of the packet sent by the server +* to show player movement +*/ +struct FFXIVIpcActorMove : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t rotation; + /* 0001 */ uint8_t unknown_1; + /* 0002 */ uint8_t unknown_2; + /* 0003 */ uint8_t unknown_3; + /* 0004 */ uint16_t unknown_4; + /* 0006 */ uint16_t posX; + /* 0008 */ uint16_t posY; + /* 000a */ uint16_t posZ; + /* 000C */ uint32_t unknown_12; +}; + +/** +* Structural representation of the packet sent by the server +* to set an actors position +*/ +struct FFXIVIpcActorSetPos : FFXIVIpcBasePacket +{ + uint16_t r16; + uint8_t waitForLoad; + uint8_t unknown1; + uint32_t unknown2; + float x; + float y; + float z; + uint32_t unknown3; + +}; + + + +/** +* Structural representation of the packet sent by the server +* to start an actors casting +*/ +struct FFXIVIpcActorCast : FFXIVIpcBasePacket +{ + uint16_t action_id; + uint16_t unknown; + uint32_t unknown_1; // Also action id + float cast_time; + uint32_t target_id; + float rotation; // In radians + uint32_t unknown_2; + uint16_t posX; + uint16_t posY; + uint16_t posZ; + uint16_t unknown_3; +}; + +struct FFXIVIpcHateList : FFXIVIpcBasePacket +{ + uint32_t numEntries; + struct LsEntry + { + uint32_t actorId; + uint8_t hatePercent; + uint8_t unknown; + uint16_t padding; + } entry[32]; + uint32_t padding; +}; + +struct FFXIVIpcUpdateClassInfo : FFXIVIpcBasePacket +{ + uint8_t classId; + uint8_t classId1; + uint16_t level; + uint32_t nextLevelIndex; + uint32_t currentExp; + uint32_t restedExp; +}; + + +/** +* Structural representation of the packet sent by the server +* to initialize a zone for the player +*/ +struct FFXIVIpcInitZone : FFXIVIpcBasePacket +{ + uint16_t serverId; + uint16_t zoneId; + uint16_t unknown1; + uint16_t unknown2; + uint32_t unknown3; + uint32_t unknown4; + uint8_t weatherId; + uint8_t bitmask; + uint16_t unknown5; + uint16_t unknown6; + uint16_t unknown7; + uint32_t unknown8; + Common::FFXIVARR_POSITION3 pos; +}; + + +/** +* Structural representation of the packet sent by the server to initialize +* the client UI upon initial connection. +*/ +struct FFXIVIpcInitUI : FFXIVIpcBasePacket +{ + uint64_t contentId; + uint32_t unknown_8; + uint32_t unknown_9; + uint32_t charId; + uint32_t restedExp; + uint16_t unknown_12; + uint16_t unknown_13; + uint8_t race; + uint8_t tribe; + uint8_t gender; + uint8_t currentJob; + uint8_t currentClass; + uint8_t deity; + uint8_t namedayMonth; + uint8_t namedayDay; + Common::GrandCompany grandCompany; + uint8_t homepoint; + uint8_t unknown_19; + uint8_t petHotBar; + uint8_t companionRank; + uint8_t companionStars; + uint8_t companionSp; + uint8_t companionUnk1; + uint8_t companionColor; + uint8_t companionFavoFeed; + uint8_t companionUnk2[2]; + float companionTimePassed; + uint32_t companionCurrentExp; + uint32_t unknown_1b; + uint32_t fishCaught; + uint32_t useBaitCatalogId; + uint32_t unknown_1c; + uint16_t pvpWolfFoldMatches; + uint16_t pvpWolfFoldVictories; + uint16_t pvpWolfFoldWeeklyMatches; + uint16_t pvpWolfFoldWeeklyVictories; + uint16_t playerCommendations; + uint8_t pvpStats[14]; + uint32_t frontlineCampaigns; + uint16_t frontlineCampaignsWeekly; + uint8_t unknown_112; + uint8_t masterCrafterMask; + uint8_t unknown_001; + uint8_t unknown_002; + uint8_t unknown_003; + uint8_t unknown_004; + uint16_t unknown_005; + uint8_t unknown_114; + uint8_t padding_114; + uint8_t unknown_1141[52]; + uint8_t preNamePadding; + char name[32]; + uint8_t unknown_54[16]; + uint8_t unknown55; + uint16_t levels[25]; + uint32_t exp[25]; + uint8_t unlockBitmask[64]; + uint8_t aetheryte[16]; + uint8_t discovery[420]; + uint8_t howto[33]; + uint8_t minions[33]; + uint8_t chocoboTaxiMask[8]; + uint8_t contentClearMask[104]; + uint8_t companionBardingMask[8]; + uint8_t companionEquippedHead; + uint8_t companionEquippedBody; + uint8_t companionEquippedFeet; + uint8_t companion_fields[15]; + uint8_t companion_name[21]; + uint8_t companionDefRank; + uint8_t companionAttRank; + uint8_t companionHealRank; + uint8_t mountGuideMask[13]; + uint8_t fishingGuideMask[77]; + uint8_t fishingSpotVisited[25]; + uint8_t unknown_41; + uint16_t fishingRecordsFish[26]; + uint16_t fishingRecordsFishWeight[26]; + uint8_t unknown_590[62]; + uint8_t rankAmalJaa; + uint8_t rankSylph; + uint8_t rankKobold; + uint8_t rankSahagin; + uint8_t rankIxal; + uint8_t rankVanu; + uint8_t rankVath; + uint8_t rankMoogle; + uint16_t expAmalJaa; + uint16_t expSylph; + uint16_t expKobold; + uint16_t expSahagin; + uint16_t expIxal; + uint16_t expVanu; + uint16_t expVath; + uint16_t expMoogle; + uint8_t unknown_0341[20]; + uint8_t unknownMask0[5]; + uint8_t unknown_0342[15]; + uint8_t unknownMask1[28]; + uint32_t unknownDword0; + uint8_t unknown_0343[12]; + uint8_t sightseeingMask[19]; + uint8_t unknown_421; + uint32_t pvpFrontlineOverall1st; + uint32_t pvpFrontlineOverall2nd; + uint32_t pvpFrontlineOverall3rd; + uint16_t pvpFrontlineWeekly1st; + uint16_t pvpFrontlineWeekly2nd; + uint16_t pvpFrontlineWeekly3rd; + uint8_t unknownRest[32]; + uint8_t tripleTriadCards[26]; + uint8_t unknownRest1[21]; + uint8_t orchestrionMask[19]; + uint8_t hallOfNoviceCompleteMask[3]; + uint8_t unknownMask2[11]; + uint8_t unknownMask3[16]; + uint8_t unknown_500[9]; +}; + +/** +* Structural representation of the packet sent by the server +* to set a players stats +*/ +struct FFXIVIpcPlayerStats : FFXIVIpcBasePacket +{ + uint32_t strength; + uint32_t dexterity; + uint32_t vitality; + uint32_t intelligence; + uint32_t mind; + uint32_t piety; + uint32_t hp; + uint32_t mp; + uint32_t tp; + uint32_t unknown; + uint32_t unknown_1; + uint32_t unknown_2; + uint32_t parry; + uint32_t attack; + uint32_t defense; + uint32_t accuracy; + uint32_t spellSpeed; + uint32_t magicDefense; + uint32_t criticalHitRate; + uint32_t resistanceSlashing; + uint32_t resistancePiercing; + uint32_t resistanceBlunt; + uint32_t attackMagicPotency; + uint32_t healingMagicPotency; + uint32_t fire; + uint32_t ice; + uint32_t wind; + uint32_t earth; + uint32_t lightning; + uint32_t water; + uint32_t determination; + uint32_t skillSpeed; + uint32_t spellSpeed1; + uint32_t spellSpeedMod; + uint32_t unknown_6[5]; + uint32_t resistanceSlow; + uint32_t resistanceSilence; + uint32_t resistanceBlind; + uint32_t resistancePoison; + uint32_t resistanceStun; + uint32_t resistanceSleep; + uint32_t resistanceBind; + uint32_t resistanceHeavy; + uint32_t unknown_7[9]; +}; + +/** +* Structural representation of the packet sent by the server +* to set an actors current owner +*/ +struct FFXIVIpcActorOwner : FFXIVIpcBasePacket +{ + uint8_t type; + uint8_t padding[7]; + uint32_t actorId; + uint32_t actorId2; +}; + +/** +* Structural representation of the packet sent by the server +* to set a players state +*/ +struct FFXIVIpcPlayerStateFlags : FFXIVIpcBasePacket +{ + /* 0000 */ uint16_t padding; + /* 0002 */ uint8_t flags[7]; + /* 0009 */ uint8_t padding1[3]; + /* 000C */ uint32_t padding2; + +}; + +/** +* Structural representation of the packet sent by the server +* containing current class information +*/ +struct FFXIVIpcPlayerClassInfo : FFXIVIpcBasePacket +{ + uint16_t classId; + uint16_t unknown; + uint16_t level; + uint16_t level1; + uint8_t unknownFields[48]; +}; + +/** +* Structural representation of the packet sent by the server +* to update a players appearance +*/ +struct FFXIVIpcModelEquip : FFXIVIpcBasePacket +{ + /* 0000 */ uint64_t mainWeapon; + /* 0008 */ uint64_t offWeapon; + /* 0010 */ uint32_t padding1; + /* 0014 */ uint32_t models[10]; + /* 003C */ uint32_t padding2; +}; + +/** +* Structural representation of the packet sent by the server +* to update a players appearance +*/ +struct FFXIVIpcItemInfo : FFXIVIpcBasePacket +{ + uint32_t sequence; + uint32_t unknown; + uint16_t containerId; + uint16_t slot; + uint32_t quantity; + uint32_t catalogId; + uint32_t reservedFlag; + uint64_t signatureId; + uint8_t hqFlag; + uint8_t unknown2; + uint16_t condition; + uint16_t spiritBond; + uint16_t color; + uint32_t glamourCatalogId; + uint16_t materia1; + uint16_t materia2; + uint16_t materia3; + uint16_t materia4; + uint16_t materia5; + uint8_t buffer1; + uint8_t buffer2; + uint8_t buffer3; + uint8_t buffer4; + uint8_t buffer5; + uint8_t padding; + uint32_t unknown10; +}; + +/** +* Structural representation of the packet sent by the server +* to update a players appearance +*/ +struct FFXIVIpcContainerInfo : FFXIVIpcBasePacket +{ + uint32_t sequence; + uint32_t numItems; + uint32_t containerId; + uint32_t unknown; +}; + +/** +* Structural representation of the packet sent by the server +* to update a players appearance +*/ +struct FFXIVIpcCurrencyCrystalInfo : FFXIVIpcBasePacket +{ + uint32_t sequence; + uint16_t containerId; + uint16_t slot; + uint32_t quantity; + uint32_t unknown; + uint32_t catalogId; + uint32_t unknown1; + uint32_t unknown2; + uint32_t unknown3; +}; + +struct FFXIVIpcInventoryTransactionFinish : FFXIVIpcBasePacket +{ + uint32_t transactionId; + uint32_t transactionId1; + uint64_t padding; +}; + +struct FFXIVIpcInventoryTransaction : FFXIVIpcBasePacket +{ + uint32_t transactionId; + uint8_t type; + uint8_t padding; + uint16_t padding1; + uint32_t ownerId; + uint32_t storageId; + uint16_t slotId; + uint16_t padding2; + uint32_t stackSize; + uint32_t catalogId; + uint32_t someActorId; + int32_t targetStorageId; + uint32_t padding3[3]; +}; + + +struct FFXIVIpcInventoryActionAck : FFXIVIpcBasePacket +{ + uint32_t sequence; + uint16_t type; + uint16_t padding; + uint32_t padding1; + uint32_t padding2; +}; + + +/** +* Structural representation of the packet sent by the server +* to update a slot in the inventory +*/ +struct FFXIVIpcUpdateInventorySlot : FFXIVIpcBasePacket +{ + uint32_t sequence; + uint32_t unknown; + uint16_t containerId; + uint16_t slot; + uint32_t quantity; + uint32_t catalogId; + uint32_t reservedFlag; + uint64_t signatureId; + uint16_t hqFlag; + uint16_t condition; + uint16_t spiritBond; + uint16_t color; + uint32_t glamourCatalogId; + uint16_t materia1; + uint16_t materia2; + uint16_t materia3; + uint16_t materia4; + uint16_t materia5; + uint8_t buffer1; + uint8_t buffer2; + uint8_t buffer3; + uint8_t buffer4; + uint8_t buffer5; + uint8_t padding; + uint32_t unknown10; +}; + +/** +* Structural representation of the packet sent by the server +* to start an event, not actually playing it, but registering +*/ +struct FFXIVIpcEventStart : FFXIVIpcBasePacket +{ + /* 0000 */ uint64_t actorId; + /* 0008 */ uint32_t eventId; + /* 000C */ uint8_t param1; + /* 000D */ uint8_t param2; + /* 000E */ uint16_t padding; + /* 0010 */ uint32_t param3; + /* 0014 */ uint32_t padding1; +}; + + +/** +* Structural representation of the packet sent by the server +* to play an event +*/ +struct FFXIVIpcEventPlay : FFXIVIpcBasePacket +{ + uint64_t actorId; + uint32_t eventId; + uint16_t scene; + uint16_t padding; + uint32_t flags; + uint32_t param3; + uint8_t param4; + uint8_t padding1[3]; + uint32_t param5; + uint8_t unknown[8]; +}; + +/** +* Structural representation of the packet sent by the server +* to finish an event +*/ +struct FFXIVIpcEventFinish : FFXIVIpcBasePacket +{ + /* 0000 */ uint32_t eventId; + /* 0004 */ uint8_t param1; + /* 0005 */ uint8_t param2; + /* 0006 */ uint16_t padding; + /* 0008 */ uint32_t param3; + /* 000C */ uint32_t padding1; +}; + +/** +* Structural representation of the packet sent by the server +* to send the active quests +*/ +struct FFXIVIpcQuestActiveList : FFXIVIpcBasePacket +{ + Common::QuestActive activeQuests[30]; +}; + +/** +* Structural representation of the packet sent by the server +* to send update a quest slot +*/ +struct FFXIVIpcQuestUpdate : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t slot; + /* 0001 */ uint8_t padding[3]; + Common::QuestActive questInfo; +}; + +/** +* Structural representation of the packet sent by the server +* to send the completed quests mask +*/ +struct FFXIVIpcQuestCompleteList : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t questCompleteMask[200]; +}; + +/** +* Structural representation of the packet sent by the server +* to finish a quest +*/ +struct FFXIVIpcQuestFinish : FFXIVIpcBasePacket +{ + uint16_t questId; + uint8_t flag1; + uint8_t flag2; + uint32_t padding; +}; + +/** +* Structural representation of the packet sent by the server +* to send a quest message +* type 0 default +* type 1 icon +* type 5 status +*/ +struct FFXIVIpcQuestMessage : FFXIVIpcBasePacket +{ + /* 0000 */ uint32_t questId; + /* 0000 */ uint8_t msgId; + /* 0000 */ uint8_t type; + /* 0000 */ uint16_t padding1; + /* 0000 */ uint32_t var1; + /* 0000 */ uint32_t var2; +}; + +struct FFXIVIpcQuestTracker : FFXIVIpcBasePacket +{ + struct TrackerEntry + { + uint8_t active; + uint8_t questIndex; + } entry[5]; + uint16_t padding[3]; +}; + + +struct FFXIVIpcWeatherChange : FFXIVIpcBasePacket +{ + uint32_t weatherId; + float delay; +}; + +/** +* Structural representation of the packet sent by the server +* to send a unviel a map +*/ +struct FFXIVIpcDiscovery : FFXIVIpcBasePacket +{ + /* 0000 */ uint32_t map_part_id; + /* 0004 */ uint32_t map_id; +}; + + +/** +* UNKOWN TYPE +*/ +struct FFXIVARR_IPC_UNK322 : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t unk[8]; +}; + +/** +* UNKOWN TYPE +*/ +struct FFXIVARR_IPC_UNK320 : FFXIVIpcBasePacket +{ + /* 0000 */ uint8_t unk[32]; +}; + +/** +* Structural representation of the packet sent by the server +* prepare zoning, showing screenmessage +*/ +struct FFXIVIpcPrepareZoning : FFXIVIpcBasePacket +{ + uint32_t logMessage; + uint16_t targetZone; + uint16_t param3; + uint8_t param4; + uint8_t hideChar; + uint8_t fadeOut; + uint8_t param7; + uint32_t padding; +}; + +/** +* Structural representation of the packet sent by the server +* to trigger content finder events +* +* See https://gist.github.com/Minoost/c35843c4c8a7a931f31fdaac9bce64c2 +*/ +struct FFXIVIpcCFNotify : FFXIVIpcBasePacket +{ + uint32_t state1; // 3 = cancelled, 4 = duty ready + uint32_t state2; // if state1 == 3, state2 is cancelled reason + + uint8_t classJob; // classJobId you registered + uint8_t unknown0[3]; + + uint32_t unknown1; // unknown, just used 0x20442 from the dumped packet + + uint8_t languages; + uint8_t unknown2[3]; + + uint16_t unknown3; + uint16_t contents[5]; +}; + +/** +* Structural representation of the packet sent by the server +* to update contents available in duty finder or raid finder +* +* Do note that this packet has to come early in login phase (around initui packet) +* or it won't be applied until you reconnect +*/ +struct FFXIVIpcCFAvailableContents : FFXIVIpcBasePacket +{ + uint8_t contents[0x48]; +}; + +/** +* Structural representation of the packet sent by the server +* to update adventure in needs in duty roulette +*/ +struct FFXIVIpcCFPlayerInNeed : FFXIVIpcBasePacket +{ + // Ordered by roulette id + uint8_t inNeeds[0x10]; +}; + +/** +* Structural representation of the packet sent by the server +* to update duty info in general +*/ +struct FFXIVIpcCFDutyInfo : FFXIVIpcBasePacket +{ + uint8_t penaltyTime; + uint8_t unknown[7]; +}; + + +} /* Server */ +} /* Packets */ +} /* Network */ +} /* Core */ + + + +#endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/ diff --git a/src/servers/Server_Common/Util.cpp b/src/servers/Server_Common/Util.cpp new file mode 100644 index 00000000..2d09f7cc --- /dev/null +++ b/src/servers/Server_Common/Util.cpp @@ -0,0 +1,42 @@ +#include "Util.h" +#include + +std::string Core::Util::binaryToHexString( uint8_t* pBinData, uint16_t size ) +{ + + std::string outStr; + + for( unsigned int i = 0; i < size; i++ ) + { + outStr += boost::str( boost::format( "%|02X|" ) % ( int ) ( pBinData[i] & 0xFF ) ); + } + + return outStr; + +} + +uint64_t Core::Util::getTimeMs() +{ + std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); + auto now_ms = std::chrono::time_point_cast< std::chrono::milliseconds >( t1 ).time_since_epoch().count(); + + return now_ms; +} + +uint64_t Core::Util::getEorzeanTimeStamp() +{ + std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); + auto now = std::chrono::time_point_cast< std::chrono::seconds >( t1 ).time_since_epoch().count(); + + return static_cast< uint64_t >( now * 20.571428571428573f ); +} + +void Core::Util::valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ) +{ + uint32_t id = inVal; + outIndex = id / 8; + uint8_t bitIndex = id % 8; + + outVal = 1 << bitIndex; +} + diff --git a/src/servers/Server_Common/Util.h b/src/servers/Server_Common/Util.h new file mode 100644 index 00000000..4aa19c7e --- /dev/null +++ b/src/servers/Server_Common/Util.h @@ -0,0 +1,20 @@ +#ifndef _UTIL_H +#define _UTIL_H + +#include +#include + +namespace Core { +namespace Util { + +std::string binaryToHexString( uint8_t* pBinData, uint16_t size ); + +uint64_t getTimeMs(); + +uint64_t getEorzeanTimeStamp(); + +void valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex ); +} +} + +#endif diff --git a/src/servers/Server_Common/UtilMath.cpp b/src/servers/Server_Common/UtilMath.cpp new file mode 100644 index 00000000..fe534f5f --- /dev/null +++ b/src/servers/Server_Common/UtilMath.cpp @@ -0,0 +1,71 @@ +#include +#include "UtilMath.h" + +float Core::Math::Util::distanceSq( float x, float y, float z, float x1, float y1, float z1 ) +{ + float deltaX = x - x1; + float deltaY = y - y1; + float deltaZ = z - z1; + + return ( deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ ); +} + +float Core::Math::Util::distance( float x, float y, float z, float x1, float y1, float z1 ) +{ + return sqrtf( distanceSq( x, y, z, x1, y1, z1 ) ); +} + +float Core::Math::Util::distance2DSq( float x, float y, float x1, float y1 ) +{ + float deltaX = x - x1; + float deltaY = y - y1; + return ( deltaX * deltaX + deltaY * deltaY ); +} + +float Core::Math::Util::distance2D( float x, float y, float x1, float y1 ) +{ + return sqrtf( distance2DSq( x, y, x1, y1 ) ); +} + +float Core::Math::Util::calcAngTo( float x, float y, float x1, float y1 ) +{ + float dx = x - x1; + float dy = y - y1; + if( dy != 0.0f ) + { + return atan2( dy, dx ); + } + else + { + return 0.0f; + } +} + +float Core::Math::Util::calcAngFrom( float x, float y, float x1, float y1 ) +{ + float dx = x - x1; + float dy = y - y1; + if( dy != 0.0f ) + { + return atan2( dy, dx ); + } + else + { + return 0.0f; + } +} + +uint16_t Core::Math::Util::floatToUInt16( float val ) +{ + return static_cast< uint16_t >( 0x8000 + val * 32.767f ); +} + +uint16_t Core::Math::Util::floatToUInt16Rot( float val ) +{ + return static_cast< uint16_t >( 0x8000 * ( ( val + PI ) ) / PI ); +} + +uint8_t Core::Math::Util::floatToUInt8Rot( float val ) +{ + return static_cast< uint8_t >( 0x80 * ( ( val + PI ) ) / PI ); +} \ No newline at end of file diff --git a/src/servers/Server_Common/UtilMath.h b/src/servers/Server_Common/UtilMath.h new file mode 100644 index 00000000..326c54e6 --- /dev/null +++ b/src/servers/Server_Common/UtilMath.h @@ -0,0 +1,38 @@ +#ifndef _UTILMATH_H +#define _UTILMATH_H + +#include "Common.h" + +#define PI 3.14159265358979323846f + +namespace Core { +namespace Math { +namespace Util { + +float distanceSq( float x, float y, float z, float x1, float y1, float z1 ); + +float distance( float x, float y, float z, float x1, float y1, float z1 ); + +float distance2DSq( float x, float y, float x1, float y1 ); + +float distance2D( float x, float y, float x1, float y1 ); + +float calcAngTo( float x, float y, float x1, float y1 ); + +float calcAngFrom( float x, float y, float x1, float y1 ); + +uint16_t floatToUInt16( float val ); +uint16_t floatToUInt16Rot( float val ); +uint8_t floatToUInt8Rot( float val ); + +template + +T clamp( T val, T minimum, T maximum ) +{ + return std::max( std::min( val, maximum ), minimum ); +} +} +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/UtilNetwork.cpp b/src/servers/Server_Common/UtilNetwork.cpp new file mode 100644 index 00000000..84905726 --- /dev/null +++ b/src/servers/Server_Common/UtilNetwork.cpp @@ -0,0 +1,35 @@ +#include "UtilNetwork.h" +#include + +bool Core::Network::Util::bufferToPacketList( const std::vector< uint8_t > &buffer, + Packets::FFXIVARR_PACKET_HEADER &ipcHeader, + std::vector< Packets::FFXIVARR_PACKET_RAW > &packetList ) +{ + memcpy( &ipcHeader, ( uint8_t* ) &buffer[0], sizeof( struct Packets::FFXIVARR_PACKET_HEADER ) ); + + uint16_t offset = 0; + for( std::size_t x = 0; x < ipcHeader.count; x++ ) + { + Packets::FFXIVARR_PACKET_RAW packet; + + uint32_t headerSize = sizeof( struct Packets::FFXIVARR_PACKET_HEADER ); + uint32_t headerSegSize = sizeof( struct Packets::FFXIVARR_PACKET_SEGMENT_HEADER ); + memcpy( &packet.segHdr, ( uint8_t* ) &buffer[headerSize + offset], headerSegSize ); + + std::vector packetData; + + uint16_t startOff = sizeof( struct Packets::FFXIVARR_PACKET_HEADER ) + offset; + + for( std::size_t y = 0; y < packet.segHdr.size - sizeof( struct Packets::FFXIVARR_PACKET_SEGMENT_HEADER ); y++ ) + { + packet.data.push_back( buffer.at( startOff + y + sizeof( struct Packets::FFXIVARR_PACKET_SEGMENT_HEADER ) ) ); + } + + offset += packet.segHdr.size; + + packetList.push_back( packet ); + + } + + return true; +} diff --git a/src/servers/Server_Common/UtilNetwork.h b/src/servers/Server_Common/UtilNetwork.h new file mode 100644 index 00000000..8b7d7748 --- /dev/null +++ b/src/servers/Server_Common/UtilNetwork.h @@ -0,0 +1,18 @@ +#ifndef _UTILNETWORK_H +#define _UTILNETWORK_H + +#include "CommonNetwork.h" + +namespace Core { +namespace Network { +namespace Util { + + + bool bufferToPacketList( const std::vector< uint8_t > &buffer, + Packets::FFXIVARR_PACKET_HEADER &ipcHeader, + std::vector< Packets::FFXIVARR_PACKET_RAW > &packetList ); +} +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Common/XMLConfig.cpp b/src/servers/Server_Common/XMLConfig.cpp new file mode 100644 index 00000000..a28d8dfa --- /dev/null +++ b/src/servers/Server_Common/XMLConfig.cpp @@ -0,0 +1,35 @@ +#include "XMLConfig.h" +#include +#include +#include +#include +#include +#include + +namespace Core { + // instanciate and load a config + XMLConfig::XMLConfig() + { + + } + + XMLConfig::~XMLConfig() + { + + } + + using boost::property_tree::ptree; + const ptree& empty_ptree() + { + static ptree t; + return t; + }; + + bool XMLConfig::loadConfig( const std::string& fileName ) + { + + boost::property_tree::read_xml( fileName, m_propTree ); + return true; + } + +} diff --git a/src/servers/Server_Common/XMLConfig.h b/src/servers/Server_Common/XMLConfig.h new file mode 100644 index 00000000..cf9395b2 --- /dev/null +++ b/src/servers/Server_Common/XMLConfig.h @@ -0,0 +1,73 @@ +#ifndef __XMLCONFIG_H +#define __XMLCONFIG_H + +#include + +#include + +#include + +namespace Core { + + // very simple XML parser class + // this hasn't gotten much attention yet as it works good as it is. + class XMLConfig + { + public: + typedef std::map SettingMap; + typedef std::map CategoryMap; + + // instanciate and load a config + XMLConfig(); + + ~XMLConfig(); + + // load a config file + bool loadConfig( const std::string& fileName ); + + template< class T > + T getValue( const std::string& name, T defaultValue = T() ) + { + try + { + return m_propTree.get< T >( name ); + } + catch( ... ) + { + return defaultValue; + } + } + + template< class T > + void setValue( const std::string& name, T defaultValue = T() ) + { + m_propTree.put( name, defaultValue ); + } + + template< class T > + T getAttrValue( boost::property_tree::ptree node, const std::string& name, T defaultValue = T() ) + { + try + { + T outVal = node.get< T >( "." + name ); + return outVal; + } + catch( const std::runtime_error& ) + { + T outVal = defaultValue; + return outVal; + } + } + + boost::property_tree::ptree getChild( const std::string& name ) + { + auto val = m_propTree.get_child( name ); + return val; + } + + private: + boost::property_tree::ptree m_propTree; + }; + +} +#endif diff --git a/src/servers/Server_Common/base64.cpp b/src/servers/Server_Common/base64.cpp new file mode 100644 index 00000000..16e77159 --- /dev/null +++ b/src/servers/Server_Common/base64.cpp @@ -0,0 +1,123 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 Ren Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +"abcdefghijklmnopqrstuvwxyz" +"0123456789+/"; + + +static inline bool is_base64( unsigned char c ) { + return ( isalnum( c ) || ( c == '+' ) || ( c == '/' ) ); +} + +std::string Core::Util::base64_encode( unsigned char const* bytes_to_encode, unsigned int in_len ) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while( in_len-- ) { + char_array_3[i++] = *( bytes_to_encode++ ); + if( i == 3 ) { + char_array_4[0] = ( char_array_3[0] & 0xfc ) >> 2; + char_array_4[1] = ( ( char_array_3[0] & 0x03 ) << 4 ) + ( ( char_array_3[1] & 0xf0 ) >> 4 ); + char_array_4[2] = ( ( char_array_3[1] & 0x0f ) << 2 ) + ( ( char_array_3[2] & 0xc0 ) >> 6 ); + char_array_4[3] = char_array_3[2] & 0x3f; + + for( i = 0; ( i < 4 ); i++ ) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if( i ) + { + for( j = i; j < 3; j++ ) + char_array_3[j] = '\0'; + + char_array_4[0] = ( char_array_3[0] & 0xfc ) >> 2; + char_array_4[1] = ( ( char_array_3[0] & 0x03 ) << 4 ) + ( ( char_array_3[1] & 0xf0 ) >> 4 ); + char_array_4[2] = ( ( char_array_3[1] & 0x0f ) << 2 ) + ( ( char_array_3[2] & 0xc0 ) >> 6 ); + char_array_4[3] = char_array_3[2] & 0x3f; + + for( j = 0; ( j < i + 1 ); j++ ) + ret += base64_chars[char_array_4[j]]; + + while( ( i++ < 3 ) ) + ret += '='; + + } + + return ret; + +} + +std::string Core::Util::base64_decode( std::string const& encoded_string ) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while( in_len-- && ( encoded_string[in_] != '=' ) && is_base64( encoded_string[in_] ) ) { + char_array_4[i++] = encoded_string[in_]; in_++; + if( i == 4 ) { + for( i = 0; i < 4; i++ ) + char_array_4[i] = base64_chars.find( char_array_4[i] ); + + char_array_3[0] = ( char_array_4[0] << 2 ) + ( ( char_array_4[1] & 0x30 ) >> 4 ); + char_array_3[1] = ( ( char_array_4[1] & 0xf ) << 4 ) + ( ( char_array_4[2] & 0x3c ) >> 2 ); + char_array_3[2] = ( ( char_array_4[2] & 0x3 ) << 6 ) + char_array_4[3]; + + for( i = 0; ( i < 3 ); i++ ) + ret += char_array_3[i]; + i = 0; + } + } + + if( i ) { + for( j = i; j < 4; j++ ) + char_array_4[j] = 0; + + for( j = 0; j < 4; j++ ) + char_array_4[j] = base64_chars.find( char_array_4[j] ); + + char_array_3[0] = ( char_array_4[0] << 2 ) + ( ( char_array_4[1] & 0x30 ) >> 4 ); + char_array_3[1] = ( ( char_array_4[1] & 0xf ) << 4 ) + ( ( char_array_4[2] & 0x3c ) >> 2 ); + char_array_3[2] = ( ( char_array_4[2] & 0x3 ) << 6 ) + char_array_4[3]; + + for( j = 0; ( j < i - 1 ); j++ ) ret += char_array_3[j]; + } + + return ret; +} diff --git a/src/servers/Server_Common/base64.h b/src/servers/Server_Common/base64.h new file mode 100644 index 00000000..01b53c55 --- /dev/null +++ b/src/servers/Server_Common/base64.h @@ -0,0 +1,11 @@ +#include + +namespace Core +{ + namespace Util + { + std::string base64_encode( unsigned char const*, unsigned int len ); + std::string base64_decode( std::string const& s ); + } +} + diff --git a/src/servers/Server_Common/md5.cpp b/src/servers/Server_Common/md5.cpp new file mode 100644 index 00000000..1c1eaf6b --- /dev/null +++ b/src/servers/Server_Common/md5.cpp @@ -0,0 +1,256 @@ +/* +* RFC 1321 compliant MD5 implementation +* +* Copyright (C) 2001-2003 Christophe Devine +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include "md5.h" + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) >> 24 ); \ +} + +void Core::Util::md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process( Core::Util::md5_context *ctx, uint8_t data[64] ) +{ + uint32_t X[16], A, B, C, D; + + GET_UINT32( X[0], data, 0 ); + GET_UINT32( X[1], data, 4 ); + GET_UINT32( X[2], data, 8 ); + GET_UINT32( X[3], data, 12 ); + GET_UINT32( X[4], data, 16 ); + GET_UINT32( X[5], data, 20 ); + GET_UINT32( X[6], data, 24 ); + GET_UINT32( X[7], data, 28 ); + GET_UINT32( X[8], data, 32 ); + GET_UINT32( X[9], data, 36 ); + GET_UINT32( X[10], data, 40 ); + GET_UINT32( X[11], data, 44 ); + GET_UINT32( X[12], data, 48 ); + GET_UINT32( X[13], data, 52 ); + GET_UINT32( X[14], data, 56 ); + GET_UINT32( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ + { \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +void Core::Util::md5_update( md5_context *ctx, uint8_t *input, uint32_t length ) +{ + uint32_t left, fill; + + if( !length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( ( void * )( ctx->buffer + left ), + ( void * )input, fill ); + md5_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + md5_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( ( void * )( ctx->buffer + left ), + ( void * )input, length ); + } +} + +static uint8_t md5_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void Core::Util::md5_finish( md5_context *ctx, uint8_t digest[16] ) +{ + uint32_t last, padn; + uint32_t high, low; + uint8_t msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( low, msglen, 0 ); + PUT_UINT32( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); +} + +/* +* those are the standard RFC 1321 test vectors +*/ + +void Core::Util::md5( unsigned char *text, unsigned char *hash, int size ) +{ + md5_context ctx; + md5_starts( &ctx ); + md5_update( &ctx, ( uint8_t * )text, size ); + md5_finish( &ctx, hash ); +} \ No newline at end of file diff --git a/src/servers/Server_Common/md5.h b/src/servers/Server_Common/md5.h new file mode 100644 index 00000000..87bf4d7f --- /dev/null +++ b/src/servers/Server_Common/md5.h @@ -0,0 +1,51 @@ +#include + +#ifndef _MD52_H +#define _MD52_H + +namespace Core +{ + namespace Util + { + typedef struct + { + uint32_t total[2]; + uint32_t state[4]; + uint8_t buffer[64]; + } + md5_context; + + + void md5( unsigned char *text, unsigned char *hash, int size ); + void md5_starts( md5_context *ctx ); + void md5_update( md5_context *ctx, uint8_t *input, uint32_t length ); + void md5_finish( md5_context *ctx, uint8_t digest[16] ); + + static const char *msg[] = + { + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "12345678901234567890123456789012345678901234567890123456789012" \ + "345678901234567890" + }; + + static const char *val[] = + { + "d41d8cd98f00b204e9800998ecf8427e", + "0cc175b9c0f1b6a831c399e269772661", + "900150983cd24fb0d6963f7d28e17f72", + "f96b697d7cb7938d525a2f31aaf161d0", + "c3fcd3d76192e4007dfb496cca67e13b", + "d174ab98d277d9f5a5611c2c9f419d9f", + "57edf4a22be3c955ac49da2e2107b67a" + }; + + } +} + + +#endif /* md5.h */ diff --git a/src/servers/Server_Lobby/CMakeLists.txt b/src/servers/Server_Lobby/CMakeLists.txt new file mode 100644 index 00000000..57cdcd6b --- /dev/null +++ b/src/servers/Server_Lobby/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Sapphire) + +include_directories("../../libraries/external/ChaiScript-6.0.0/include/") +include_directories("../../libraries/sapphire/datReader/") +include_directories("../") + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") + +set(SERVER_COMMON_DIR ../Server_Common) +set(Boost_USE_STATIC_LIBS ON) + +if(UNIX) + include_directories("/usr/include/mysql/") + message(STATUS "Setting GCC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32") + + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + else() + if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib) + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + else() + message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!") + endif() + endif() +else() + add_definitions(-D_WIN32_WINNT=0x601) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL/") + message(STATUS "Setting MSVC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + message(STATUS "Using boost in /libraries/external") + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + else() + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR})) + set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR}) + set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR}) + else() + message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!") + endif() + endif() +endif() + + +include_directories(${Boost_INCLUDE_DIR}) + +link_directories(${BOOST_LIBRARYDIR}) +link_directories(${SERVER_COMMON_DIR}) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/zlib) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/") + +add_executable(server_lobby ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) +add_dependencies(server_lobby Common xivdat) + +set_target_properties(server_lobby PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" +) + +if (UNIX) + target_link_libraries(server_lobby Common xivdat pthread mysqlclient dl z) +else() + target_link_libraries(server_lobby Common xivdat libmysql zlib1) +endif() + +target_link_libraries(server_lobby ${Boost_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/src/servers/Server_Lobby/Forwards.h b/src/servers/Server_Lobby/Forwards.h new file mode 100644 index 00000000..94cebd2c --- /dev/null +++ b/src/servers/Server_Lobby/Forwards.h @@ -0,0 +1,33 @@ +#ifndef _FORWARDS_H +#define _FORWARDS_H + +#include +#include + +#define TYPE_FORWARD( x ) \ +class x; \ +typedef boost::shared_ptr< x > x ## Ptr; \ +typedef std::vector< x > x ## PtrList; + +namespace Core +{ + TYPE_FORWARD( LobbySession ); + + namespace Network + { + TYPE_FORWARD( Hive ); + TYPE_FORWARD( Acceptor ); + TYPE_FORWARD( Connection ); + TYPE_FORWARD( GameConnection ); + + namespace Packets + { + TYPE_FORWARD( GamePacket ); + } + } + + +} + + +#endif \ No newline at end of file diff --git a/src/servers/Server_Lobby/GameConnection.cpp b/src/servers/Server_Lobby/GameConnection.cpp new file mode 100644 index 00000000..5f40d522 --- /dev/null +++ b/src/servers/Server_Lobby/GameConnection.cpp @@ -0,0 +1,511 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "GameConnection.h" + +#include "ServerLobby.h" +#include "LobbyPacketContainer.h" + +#include "RestConnector.h" +#include "LobbySession.h" +#include "Forwards.h" +#include "blowfish.h" + + + + +extern Core::Logger g_log; +extern Core::ServerLobby g_serverLobby; +extern Core::Network::RestConnector g_restConnector; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Network::GameConnection::GameConnection( Core::Network::HivePtr pHive, + Core::Network::AcceptorPtr pAcceptor ) + : Connection( pHive ) + , m_pAcceptor( pAcceptor ) + , m_bEncryptionInitialized( false ) +{ +} + +Core::Network::GameConnection::~GameConnection() +{ + +} + + +// overwrite the parents onConnect for our game socket needs +void Core::Network::GameConnection::OnAccept( const std::string & host, uint16_t port ) +{ + GameConnectionPtr connection( new GameConnection( m_hive, m_pAcceptor ) ); + m_pAcceptor->Accept( connection ); + + g_log.info( "Connect from " + m_socket.remote_endpoint().address().to_string() ); +} + + +void Core::Network::GameConnection::OnDisconnect() +{ + g_log.debug( "DISCONNECT" ); +} + +void Core::Network::GameConnection::OnRecv( std::vector< uint8_t > & buffer ) +{ + Packets::FFXIVARR_PACKET_HEADER ipcHeader; + std::vector< Packets::FFXIVARR_PACKET_RAW > packetList; + + Network::Util::bufferToPacketList( buffer, ipcHeader, packetList ); + + handlePackets( ipcHeader, packetList ); + +} + +void Core::Network::GameConnection::OnError( const boost::system::error_code & error ) +{ + g_log.info( "GameConnection closed: " + error.message() ); +} + +void Core::Network::GameConnection::sendError( uint64_t sequence, uint32_t errorcode, uint16_t messageId, uint32_t tmpId ) +{ + GamePacketNew< FFXIVIpcLobbyError > errorPacket( tmpId ); + + errorPacket.data().seq = sequence; + errorPacket.data().error_id = errorcode; + errorPacket.data().message_id = messageId; + + Packets::LobbyPacketContainer pRP( m_encKey ); + pRP.addPacket( errorPacket ); + sendPacket( pRP ); +} + +void Core::Network::GameConnection::getCharList( FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ) +{ + uint64_t sequence = *reinterpret_cast< uint64_t* >( &packet.data[0] + 0x10 ); + g_log.info( "Sequence [" + std::to_string( sequence ) + "]" ); + + g_log.info( "[" + std::to_string( m_pSession->getAccountID() ) + "] ReqCharList" ); + Packets::LobbyPacketContainer pRP( m_encKey ); + + GamePacketNew< FFXIVIpcServerList > serverListPacket( tmpId ); + serverListPacket.data().seq = 1; + serverListPacket.data().offset = 0; + serverListPacket.data().numServers = 1; + serverListPacket.data().server[0].id = 1; + serverListPacket.data().server[0].index = 0; + serverListPacket.data().final = 1; + sprintf( serverListPacket.data().server[0].name, "Sapphire" ); + + pRP.addPacket( serverListPacket ); + + GamePacketNew< FFXIVIpcRetainerList > retainerListPacket( tmpId ); + retainerListPacket.data().padding[8] = 1; + + pRP.addPacket( retainerListPacket ); + + sendPacket( pRP ); + + auto charList = g_restConnector.getCharList( ( char * )m_pSession->getSessionId() ); + + int charIndex = 0; + + for( uint8_t i = 0; i < 4; i++ ) + { + GamePacketNew< FFXIVIpcCharList > charListPacket( tmpId ); + + charListPacket.data().seq = sequence; + charListPacket.data().numInPacket = 2; + charListPacket.data().counter = i * 4; + + for( uint8_t j = 0; j < 2; j++ ) + + { + if( charIndex < charList.size() && charList.size() != 0 ) + { + FFXIVIpcCharList::CharaDetails details; + memset( &details, 0, sizeof( FFXIVIpcCharList::CharaDetails ) ); + + auto& charEntry = charList[charIndex]; + details.uniqueId = get<1>( charEntry ); + details.contentId = get<2>( charEntry ); + details.serverId = 1; + details.index = charIndex; + strcpy( details.charDetailJson, get<3>( charEntry ).c_str() ); + strcpy( details.nameChara, get<0>( charEntry ).c_str() ); + strcpy( details.nameServer, "Sapphire" ); + + charListPacket.data().charaDetails[j] = details; + + g_log.debug( "[" + std::to_string( charIndex ) + "] " + std::to_string( details.index ) + " - " + + get<0>( charEntry ) + " - " + + std::to_string( get<1>( charEntry ) ) + " - " + + std::to_string( get<2>( charEntry ) ) + " - " + + get<3>( charEntry ) ); + } + charIndex++; + } + + + // TODO: Eventually move to account info storage + if( i == 3 ) + { + charListPacket.data().entitledExpansion = 2; + charListPacket.data().maxCharOnWorld = 8; + charListPacket.data().unknown8 = 8; + charListPacket.data().veteranRank = 12; + charListPacket.data().counter = ( i * 4 ) + 1; + charListPacket.data().unknown4 = 128; + } + + Packets::LobbyPacketContainer pRP( m_encKey ); + pRP.addPacket( charListPacket ); + sendPacket( pRP ); + + } +} + +void Core::Network::GameConnection::enterWorld( FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ) +{ + uint64_t sequence = *reinterpret_cast< uint64_t* >( &packet.data[0] + 0x10 ); + g_log.info( "Sequence [" + std::to_string( sequence ) + "]" ); + + g_log.info( "[" + std::to_string( m_pSession->getAccountID() ) + "] ReqEnterWorld" ); + + uint64_t lookupId = *reinterpret_cast< uint64_t* >( &packet.data[0] + 0x18 ); + + uint32_t logInCharId; + std::string logInCharName; + auto charList = g_restConnector.getCharList( ( char * )m_pSession->getSessionId() ); + for( uint32_t i = 0; i < charList.size(); i++ ) + { + uint64_t thisContentId = get<2>( charList[i] ); + + if( thisContentId == lookupId ) + { + logInCharId = get<1>( charList[i] ); + logInCharName = get<0>( charList[i] ); + break; + } + } + + + g_log.info( "[" + std::to_string( m_pSession->getAccountID() ) + "] Logging in as " + logInCharName + "(" + std::to_string( logInCharId ) + ")" ); + + Packets::LobbyPacketContainer pRP( m_encKey ); + + GamePacketNew< FFXIVIpcEnterWorld > enterWorldPacket( tmpId ); + + enterWorldPacket.data().contentId = lookupId; + + enterWorldPacket.data().seq = sequence; + strcpy( enterWorldPacket.data().host, g_serverLobby.m_pConfig->getValue< std::string >( "Settings.General.ZoneIp" ).c_str() ); + enterWorldPacket.data().port = g_serverLobby.m_pConfig->getValue< uint16_t >( "Settings.General.ZonePort" ); + enterWorldPacket.data().charId = logInCharId; + memcpy( enterWorldPacket.data().sid, m_pSession->getSessionId(), 66 ); + + pRP.addPacket( enterWorldPacket ); + sendPacket( pRP ); +} + +bool Core::Network::GameConnection::sendServiceAccountList( FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ) +{ + LobbySessionPtr pSession = g_serverLobby.getSession( ( char* )&packet.data[0] + 0x20 ); + if( pSession != nullptr ) + { + g_log.Log( LoggingSeverity::info, "Found session linked to accountId: " + std::to_string( pSession->getAccountID() ) ); + m_pSession = pSession; + GamePacketNew< FFXIVIpcServiceIdInfo > serviceIdInfoPacket( tmpId ); + sprintf( serviceIdInfoPacket.data().serviceAccount[0].name, "FINAL FANTASY XIV" ); + serviceIdInfoPacket.data().numServiceAccounts = 1; + + serviceIdInfoPacket.data().u1 = 3; + serviceIdInfoPacket.data().u2 = 0x99; + serviceIdInfoPacket.data().serviceAccount[0].id = 0x002E4A2B; + + Packets::LobbyPacketContainer pRP( m_encKey ); + pRP.addPacket( serviceIdInfoPacket ); + sendPacket( pRP ); + } + else + { + g_log.Log( LoggingSeverity::info, "Could not retrieve session: " + std::string( ( char* )&packet.data[0] + 0x20 ) ); + sendError( 1, 5006, 13001, tmpId ); + + return true; + } + return false; +} + +bool Core::Network::GameConnection::createOrModifyChar( FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ) +{ + uint64_t sequence = *reinterpret_cast< uint64_t* >( &packet.data[0] + 0x10 ); + uint8_t type = *reinterpret_cast< uint8_t* >( &packet.data[0] + 0x29 ); + g_log.info( "Sequence [" + std::to_string( sequence ) + "]" ); + g_log.info( "Type [" + std::to_string( type ) + "]" ); + + g_log.info( "[" + std::to_string( m_pSession->getAccountID() ) + "] ReqCharCreate" ); + + std::string name; + uint32_t newId = g_restConnector.getNextCharId(); + uint64_t newContentId = g_restConnector.getNextContentId(); + + if( type == 1 ) //Character creation name check + { + name = std::string( ( char* )&packet.data[0] + 0x2C ); + + g_log.Log( LoggingSeverity::info, "[" + std::to_string( m_pSession->getAccountID() ) + "] Type 1: " + name ); + + Packets::LobbyPacketContainer pRP( m_encKey ); + + m_pSession->newCharName = name; + + if( g_restConnector.checkNameTaken( m_pSession->newCharName ) ) + { + sendError( sequence, 3074, 13004, tmpId ); + return true; + } + + GamePacketNew< FFXIVIpcCharCreate > charCreatePacket( tmpId ); + + charCreatePacket.data().content_id = newContentId; + strcpy( charCreatePacket.data().name, name.c_str() ); + strcpy( charCreatePacket.data().world, "Sapphire" ); + charCreatePacket.data().type = 1; + charCreatePacket.data().seq = sequence; + charCreatePacket.data().unknown = 1; + charCreatePacket.data().unknown_2 = 1; + charCreatePacket.data().unknown_7 = 1; + charCreatePacket.data().unknown_8 = 1; + + pRP.addPacket( charCreatePacket ); + sendPacket( pRP ); + } + else if( type == 2 ) //Character creation finalize + { + std::string charDetails( ( char* )&packet.data[0] + 0x4C ); + g_log.Log( LoggingSeverity::info, "[" + std::to_string( m_pSession->getAccountID() ) + "] Type 2: " + charDetails ); + + if( g_restConnector.createCharacter( ( char* )m_pSession->getSessionId(), m_pSession->newCharName, charDetails ) != -1 ) + { + Packets::LobbyPacketContainer pRP( m_encKey ); + + GamePacketNew< FFXIVIpcCharCreate > charCreatePacket( tmpId ); + + charCreatePacket.data().content_id = newContentId; + strcpy( charCreatePacket.data().name, name.c_str() ); + strcpy( charCreatePacket.data().world, "Sapphire" ); + charCreatePacket.data().type = 2; + charCreatePacket.data().seq = sequence; + charCreatePacket.data().unknown = 1; + charCreatePacket.data().unknown_2 = 1; + charCreatePacket.data().unknown_7 = 1; + charCreatePacket.data().unknown_8 = 1; + + pRP.addPacket( charCreatePacket ); + sendPacket( pRP ); + } + else + { + sendError( sequence, 5006, 13001, tmpId ); + } + } + else if( type == 4 ) //Character delete + { + name = std::string( ( char* )&packet.data[0] + 0x2C ); + g_log.info( "[" + std::to_string( m_pSession->getAccountID() ) + "] Type 4: " + name ); + + + + if( g_restConnector.deleteCharacter( ( char* )m_pSession->getSessionId(), name ) ) + { + + GamePacketNew< FFXIVIpcCharCreate > charCreatePacket( tmpId ); + + //charCreatePacket.data().content_id = deletePlayer.getContentId(); + charCreatePacket.data().content_id = 0; + strcpy( charCreatePacket.data().name, name.c_str() ); + strcpy( charCreatePacket.data().world, "Sapphire" ); + charCreatePacket.data().type = 4; + charCreatePacket.data().seq = sequence; + charCreatePacket.data().unknown = 1; + charCreatePacket.data().unknown_2 = 1; + charCreatePacket.data().unknown_7 = 1; + charCreatePacket.data().unknown_8 = 1; + + Packets::LobbyPacketContainer pRP( m_encKey ); + pRP.addPacket( charCreatePacket ); + sendPacket( pRP ); + } + else + { + sendError( sequence, 5006, 13001, tmpId ); + } + } + else + { + g_log.error( "[" + std::to_string( m_pSession->getAccountID() ) + "] Unknown Character Creation Type: " + std::to_string( type ) ); + } + return false; +} + +void Core::Network::GameConnection::handleGamePacket( Packets::FFXIVARR_PACKET_RAW &packet ) +{ + + uint32_t tmpId = packet.segHdr.target_actor; + + g_log.info( "OpCode [" + std::to_string( *reinterpret_cast< uint16_t* >( &packet.data[2] ) ) + "]" ); + + switch( *reinterpret_cast< uint16_t* >( &packet.data[2] ) ) + { + case ReqServiceAccountList: + { + sendServiceAccountList( packet, tmpId ); + } + break; + + case ReqCharList: + { + getCharList( packet, tmpId ); + } + break; + + case ReqEnterWorld: + { + enterWorld( packet, tmpId ); + } + break; + + case ReqCharCreate: + { + createOrModifyChar( packet, tmpId ); + } + break; + + } + +} + +void Core::Network::GameConnection::sendPacket( Packets::LobbyPacketContainer& pLpc ) +{ + uint16_t size = pLpc.getSize(); + uint8_t* dataPtr = pLpc.getRawData( false ); + std::vector< uint8_t > sendBuffer; + sendBuffer.assign( dataPtr, dataPtr + size ); + Send( sendBuffer ); +} + +void Core::Network::GameConnection::sendPackets( Packets::PacketContainer * pPacket ) +{ + //g_log.Log(LoggingSeverity::info, pPacket->toString()); + std::vector< uint8_t > sendBuffer; + + pPacket->fillSendBuffer( sendBuffer ); + Send( sendBuffer ); +} + +void Core::Network::GameConnection::sendSinglePacket( Packets::GamePacket * pPacket ) +{ + PacketContainer pRP = PacketContainer(); + pRP.addPacket( *pPacket ); + sendPackets( &pRP ); +} + +void Core::Network::GameConnection::sendSinglePacket( Packets::GamePacket & pPacket ) +{ + PacketContainer pRP = PacketContainer(); + pRP.addPacket( pPacket ); + sendPackets( &pRP ); +} + +void Core::Network::GameConnection::generateEncryptionKey( uint32_t key, const std::string& keyPhrase ) +{ + memset( m_baseKey, 0, 0x2C ); + m_baseKey[0] = 0x78; + m_baseKey[1] = 0x56; + m_baseKey[2] = 0x34; + m_baseKey[3] = 0x12; + memcpy( m_baseKey + 0x04, &key, 4 ); + m_baseKey[8] = 0xA0; + m_baseKey[9] = 0x0F; + memcpy( ( char* )m_baseKey + 0x0C, keyPhrase.c_str(), keyPhrase.size() ); + Core::Util::md5( m_baseKey, m_encKey, 0x2C ); +} + +void Core::Network::GameConnection::handlePackets( const Core::Network::Packets::FFXIVARR_PACKET_HEADER& ipcHeader, + const std::vector& packetData ) +{ + + for( auto inPacket : packetData ) + { + + if( m_bEncryptionInitialized && inPacket.segHdr.type == 3 ) + { + BlowFish blowfish; + blowfish.initialize( m_encKey, 0x10 ); + blowfish.Decode( ( unsigned char* )( &inPacket.data[0] ), ( unsigned char* )( &inPacket.data[0] ), + ( inPacket.data.size() ) - 0x10 ); + } + + switch( inPacket.segHdr.type ) + { + case 9: // Encryption init + { + std::string key_phrase( reinterpret_cast< char* >( &inPacket.data[36] ) ); + generateEncryptionKey( *reinterpret_cast< uint32_t* >( &inPacket.data[100] ), key_phrase ); + m_bEncryptionInitialized = true; + + std::vector< uint8_t > samplePacket( 0x290 ); + memset( &samplePacket[0], 0, 0x290 ); + + *reinterpret_cast< uint16_t* >( &samplePacket[0] ) = 0x290; + *reinterpret_cast< uint8_t* >( &samplePacket[0] + 0x0C ) = 0x0A; + *reinterpret_cast< uint32_t* >( &samplePacket[0] + 0x10 ) = 0xE0003C2A; + + BlowFish blowfish; + blowfish.initialize( m_encKey, 0x10 ); + blowfish.Encode( &samplePacket[0] + 0x10, &samplePacket[0] + 0x10, 0x280 ); + + auto packet = GamePacket( reinterpret_cast< char* >( &samplePacket[0] ), static_cast< uint16_t >( samplePacket.size() ) ); + sendSinglePacket( packet ); + break; + } + + + case 3: // game packet + { + g_log.info( "GamePacket [" + std::to_string( inPacket.segHdr.type ) + "]" ); + handleGamePacket( inPacket ); + break; + } + case 7: // keep alive + { + uint32_t id = *reinterpret_cast< uint32_t* >( &inPacket.data[0] ); + uint32_t timeStamp = *reinterpret_cast< uint32_t* >( &inPacket.data[4] ); + + GamePacket pPe( 0x00, 0x18, 0, 0, 0x08 ); + pPe.setValAt< uint32_t >( 0x10, id ); + pPe.setValAt< uint32_t >( 0x14, timeStamp ); + sendSinglePacket( &pPe ); + + break; + } + case 8: + { + break; + } + } + } +} diff --git a/src/servers/Server_Lobby/GameConnection.h b/src/servers/Server_Lobby/GameConnection.h new file mode 100644 index 00000000..a8b73bdb --- /dev/null +++ b/src/servers/Server_Lobby/GameConnection.h @@ -0,0 +1,87 @@ +#ifndef GAMECONNECTION_H +#define GAMECONNECTION_H + +#include +#include +#include + +#include +#include +#include "LobbyPacketContainer.h" + + +#include + +#include "Forwards.h" + +#define DECLARE_HANDLER( x ) void x( Packets::GamePacketPtr pInPacket, Entity::PlayerPtr pPlayer ) + +namespace Core { +namespace Network { + +class GameConnection : public Connection +{ + +private: + // TODO move the next three params to the session, makes more sense there + // encryption key + uint8_t m_encKey[0x10]; + + // base key, the encryption key is generated from this + uint8_t m_baseKey[0x2C]; + + bool m_bEncryptionInitialized; + + AcceptorPtr m_pAcceptor; + + LobbySessionPtr m_pSession; + + LockedQueue< Packets::GamePacketPtr > m_inQueue; + LockedQueue< Packets::GamePacketPtr > m_outQueue; + +public: + GameConnection( HivePtr pHive, AcceptorPtr pAcceptor ); + + ~GameConnection(); + + void generateEncryptionKey( uint32_t key, const std::string& keyPhrase ); + + // overwrite the parents onConnect for our game socket needs + void OnAccept( const std::string & host, uint16_t port ) override; + + void OnDisconnect() override; + + void OnRecv( std::vector< uint8_t > & buffer ) override; + + void OnError( const boost::system::error_code & error ) override; + void sendError( uint64_t sequence, uint32_t errorcode, uint16_t messageId, uint32_t tmpId ); + + void getCharList( Packets::FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ); + void enterWorld( Packets::FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ); + bool sendServiceAccountList( Packets::FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ); + bool createOrModifyChar( Packets::FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ); + + void handlePackets( const Packets::FFXIVARR_PACKET_HEADER& ipcHeader, + const std::vector& packetData ); + + void handleGamePacket( Packets::FFXIVARR_PACKET_RAW &pPacket ); + + void handleGamePacket( Packets::GamePacketPtr pPacket ); + + void sendPackets( Packets::PacketContainer * pPacket ); + + void sendPacket( Packets::LobbyPacketContainer& pLpc ); + + void sendSinglePacket( Packets::GamePacket * pPacket ); + + void sendSinglePacket( Packets::GamePacket & pPacket ); + +}; + + + +} +} + + +#endif \ No newline at end of file diff --git a/src/servers/Server_Lobby/LobbyPacketContainer.cpp b/src/servers/Server_Lobby/LobbyPacketContainer.cpp new file mode 100644 index 00000000..3b70a67b --- /dev/null +++ b/src/servers/Server_Lobby/LobbyPacketContainer.cpp @@ -0,0 +1,59 @@ +#include "LobbyPacketContainer.h" +#include +#include + +#include "blowfish.h" + +namespace Core { + namespace Network { + namespace Packets { + + LobbyPacketContainer::LobbyPacketContainer( uint8_t* encKey ) + { + memset( &m_header, 0, sizeof( Core::Network::Packets::FFXIVARR_PACKET_HEADER ) ); + m_header.size = sizeof( Core::Network::Packets::FFXIVARR_PACKET_HEADER ); + + m_encKey = encKey; + + memset( m_dataBuf, 0, 0x1570 ); + } + + LobbyPacketContainer::~LobbyPacketContainer() + { + m_entryList.clear(); + } + + void LobbyPacketContainer::addPacket( GamePacketPtr pEntry ) + { + memcpy( m_dataBuf + m_header.size, pEntry->getData(), pEntry->getSize() ); + + // encryption key is set, we want to encrypt this packet + if( m_encKey != NULL ) + { + BlowFish blowfish; + blowfish.initialize( m_encKey, 0x10 ); + blowfish.Encode( m_dataBuf + m_header.size + 0x10, m_dataBuf + m_header.size + 0x10, pEntry->getSize() - 0x10 ); + } + + m_header.size += pEntry->getSize(); + m_header.count++; + } + + uint16_t LobbyPacketContainer::getSize() const + { + return m_header.size; + } + + uint8_t* LobbyPacketContainer::getRawData( bool addstuff ) + { + if( addstuff ) + { + m_header.unknown_0 = 0xff41a05252; + m_header.timestamp = Core::Util::getTimeMs(); + } + memcpy( m_dataBuf, &m_header, sizeof( Core::Network::Packets::FFXIVARR_PACKET_HEADER ) ); + return m_dataBuf; + } + } + } +} diff --git a/src/servers/Server_Lobby/LobbyPacketContainer.h b/src/servers/Server_Lobby/LobbyPacketContainer.h new file mode 100644 index 00000000..cc17e55d --- /dev/null +++ b/src/servers/Server_Lobby/LobbyPacketContainer.h @@ -0,0 +1,45 @@ +#pragma once + +#ifndef _LobbyPacketContainer_H_ +#define _LobbyPacketContainer_H_ + +#include +#include + +#include +#include + +#include "Forwards.h" + +namespace Core { + namespace Network { + namespace Packets { + class GamePacket; + + class LobbyPacketContainer + { + public: + LobbyPacketContainer( uint8_t* encKey = nullptr ); + ~LobbyPacketContainer(); + + void addPacket( GamePacketPtr pEntry ); + + uint16_t getSize() const; + + uint8_t* getRawData( bool addstuff = true ); + + private: + Core::Network::Packets::FFXIVARR_PACKET_HEADER m_header; + + uint8_t * m_encKey; + + std::vector m_entryList; + + uint8_t m_dataBuf[0x2000]; + + }; + + } + } +} +#endif diff --git a/src/servers/Server_Lobby/LobbySession.cpp b/src/servers/Server_Lobby/LobbySession.cpp new file mode 100644 index 00000000..e8f4d902 --- /dev/null +++ b/src/servers/Server_Lobby/LobbySession.cpp @@ -0,0 +1,15 @@ +#include "LobbySession.h" +namespace Core +{ + LobbySession::LobbySession( void ) + { + //setSocket(NULL); + + } + + LobbySession::~LobbySession( void ) + { + + } +} + diff --git a/src/servers/Server_Lobby/LobbySession.h b/src/servers/Server_Lobby/LobbySession.h new file mode 100644 index 00000000..867a1008 --- /dev/null +++ b/src/servers/Server_Lobby/LobbySession.h @@ -0,0 +1,62 @@ +#pragma once + +#ifndef _CLobbySession_H_ +#define _CLobbySession_H_ + +#include +#include +#include + +namespace Core { + + class LobbySession + { + + private: + uint32_t m_IP; + uint32_t m_accountID; + + uint8_t m_sessionId[56]; + + public: + + std::string newCharName; + + LobbySession( void ); + ~LobbySession( void ); + + uint32_t getIP() + { + return m_IP; + } + + uint8_t * getSessionId() + { + return m_sessionId; + } + + void setSessionId( uint8_t * sessionId ) + { + memcpy( m_sessionId, sessionId, 56 ); + } + + void setIP( uint32_t iP ) + { + m_IP = iP; + } + + uint32_t getAccountID() + { + return m_accountID; + } + + void setAccountID( uint32_t iD ) + { + m_accountID = iD; + } + + }; + +} + +#endif diff --git a/src/servers/Server_Lobby/RestConnector.cpp b/src/servers/Server_Lobby/RestConnector.cpp new file mode 100644 index 00000000..997739e0 --- /dev/null +++ b/src/servers/Server_Lobby/RestConnector.cpp @@ -0,0 +1,343 @@ +#include "RestConnector.h" +#include "LobbySession.h" +#include "ServerLobby.h" +#include +#include +#include +#include +#include + +#define BOOST_SPIRIT_THREADSAFE +#include +#include +#include +#include +#include +#include + +extern Core::Logger g_log; + +typedef std::vector> CharList; + +Core::Network::RestConnector::RestConnector() +{ + +} + +Core::Network::RestConnector::~RestConnector() +{ + +} + +HttpResponse Core::Network::RestConnector::requestApi( std::string endpoint, std::string data ) +{ + HttpClient client( restHost ); + + std::string reqstr = "/sapphire-api/lobby/" + endpoint; + + HttpResponse r; + try + { + r = client.request( "POST", reqstr, data ); + } + catch( std::exception& e ) + { + g_log.error( endpoint + " failed, REST is not reachable: " + std::string( e.what() ) ); + return nullptr; + } + return r; +} + +Core::LobbySessionPtr Core::Network::RestConnector::getSession( char* sId ) +{ + std::string json_string = "{\"sId\": \"" + std::string( sId ) + "\",\"secret\": \"" + serverSecret + "\"}"; + + HttpResponse r = requestApi( "checkSession", json_string ); + + if( r == nullptr ) + return nullptr; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return nullptr; + } + + if( content.find( "invalid" ) == std::string::npos ) + { + LobbySessionPtr pSession( new Core::LobbySession() ); + pSession->setAccountID( atoi( pt.get( "result" ).c_str() ) ); + pSession->setSessionId( (uint8_t *)sId ); + return pSession; + } + else + { + return nullptr; + } + } + else + { + return nullptr; + } +} + +bool Core::Network::RestConnector::checkNameTaken( std::string name ) +{ + std::string json_string = "{\"name\": \"" + name + "\",\"secret\": \"" + serverSecret + "\"}"; + + HttpResponse r = requestApi( "checkNameTaken", json_string ); + + if( r == nullptr ) + return true; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return true; + } + + if( pt.get( "result" ) != "invalid" && pt.get( "result" ) == "false" ) + return false; + return true; + } + else + { + return true; + } +} + +uint32_t Core::Network::RestConnector::getNextCharId() +{ + std::string json_string = "{\"secret\": \"" + serverSecret + "\"}"; + + HttpResponse r = requestApi( "getNextCharId", json_string ); + + if( r == nullptr ) + return -1; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return -1; + } + + if( content.find( "invalid" ) == std::string::npos ) + { + return pt.get( "result" ); + } + else + { + return -1; + } + } + else + { + return -1; + } +} + +uint64_t Core::Network::RestConnector::getNextContentId() +{ + std::string json_string = "{\"secret\": \"" + serverSecret + "\"}"; + + HttpResponse r = requestApi( "getNextContentId", json_string ); + + if( r == nullptr ) + return -1; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return -1; + } + + if( content.find( "invalid" ) == std::string::npos ) + { + return pt.get( "result" ); + } + else + { + return -1; + } + } + else + { + return -1; + } +} + +CharList Core::Network::RestConnector::getCharList( char * sId ) +{ + std::string json_string = "{\"sId\": \"" + std::string( sId, 56 ) + "\",\"secret\": \"" + serverSecret + "\"}"; + + HttpResponse r = requestApi( "getCharacterList", json_string ); + + CharList list; + if( r == nullptr ) + return list; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + g_log.debug( content ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return list; + } + + if( pt.get( "result" ).find( "invalid" ) == std::string::npos ) + { + + g_log.debug( pt.get_value( "result" ) ); + + for( auto& child : pt.get_child( "charArray" ) ){ + g_log.debug( child.second.get( "contentId" ) ); + list.push_back( std::make_tuple( child.second.get( "name" ), atoi( child.second.get( "charId" ).c_str() ), child.second.get( "contentId" ), child.second.get( "infoJson" ) ) ); + } + + return list; + + } + else + { + return list; + } + } + else + { + return list; + } +} + +bool Core::Network::RestConnector::deleteCharacter( char* sId, std::string name ) +{ + std::string json_string = "{\"sId\": \"" + std::string( sId, 56 ) + "\",\"secret\": \"" + serverSecret + "\",\"name\": \"" + name + "\"}"; + + HttpResponse r = requestApi( "deleteCharacter", json_string ); + + if( r == nullptr ) + return false; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return false; + } + + if( content.find( "invalid" ) == std::string::npos ) + return true; + return false; + } + else + { + return false; + } +} + +int Core::Network::RestConnector::createCharacter( char * sId, std::string name, std::string infoJson ) +{ + std::string json_string = "{\"sId\": \"" + std::string( sId, 56 ) + "\",\"secret\": \"" + serverSecret + "\",\"name\": \"" + name + "\",\"infoJson\": \"" + Core::Util::base64_encode( (unsigned char *)infoJson.c_str(), infoJson.length() ) + "\"}"; + + HttpResponse r = requestApi( "createCharacter", json_string ); + + if( r == nullptr ) + return -1; + + std::string content = std::string( std::istreambuf_iterator( r->content ), {} ); + g_log.debug( content ); + if( r->status_code.find( "200" ) != std::string::npos ) + { + using namespace boost::property_tree; + ptree pt; + + try { + std::stringstream ss; + ss << content; + + read_json( ss, pt ); + } + catch( std::exception& e ) + { + g_log.debug( "Could not parse REST response: " + std::string( e.what() ) ); + return -1; + } + + if( content.find( "invalid" ) == std::string::npos ) + return atoi( pt.get( "result" ).c_str() ); + return -1; + } + else + { + return -1; + } +} \ No newline at end of file diff --git a/src/servers/Server_Lobby/RestConnector.h b/src/servers/Server_Lobby/RestConnector.h new file mode 100644 index 00000000..c31c2a6e --- /dev/null +++ b/src/servers/Server_Lobby/RestConnector.h @@ -0,0 +1,44 @@ +#ifndef _RESTCONNECTOR_H_ +#define _RESTCONNECTOR_H_ + +#include +#include +#include + +#include "client_http.hpp" +#include "Forwards.h" + +typedef SimpleWeb::Client HttpClient; +typedef std::shared_ptr::Response> HttpResponse; + +namespace Core +{ + class Session; + + namespace Network + { + class LobbySession; + + class RestConnector + { + public: + RestConnector(); + ~RestConnector(); + + HttpResponse requestApi( std::string endpoint, std::string data ); + LobbySessionPtr getSession( char* sId ); + int createCharacter( char * sId, std::string name, std::string infoJson ); + std::vector> getCharList( char * sId ); + bool deleteCharacter( char* sId, std::string name ); + bool checkNameTaken( std::string name ); + uint32_t getNextCharId(); + uint64_t getNextContentId(); + + std::string serverSecret; + std::string restHost; + + }; + } +} + +#endif diff --git a/src/servers/Server_Lobby/ServerLobby.cpp b/src/servers/Server_Lobby/ServerLobby.cpp new file mode 100644 index 00000000..c8724111 --- /dev/null +++ b/src/servers/Server_Lobby/ServerLobby.cpp @@ -0,0 +1,137 @@ + +#include +#include +#include + +#include +#include + +#include +#include + +//#include "LobbySession.h" + +#include "ServerLobby.h" + +#include "GameConnection.h" +#include "RestConnector.h" + +#include +#include + +#include +#include + +#include + +Core::Logger g_log; +Core::Network::RestConnector g_restConnector; + +namespace Core { + + + ServerLobby::ServerLobby( const std::string& configPath ) : + m_configPath( configPath ), + m_numConnections( 0 ) + { + m_pConfig = boost::shared_ptr( new XMLConfig ); + } + + ServerLobby::~ServerLobby( void ) + { + } + + LobbySessionPtr ServerLobby::getSession( char* sessionId ) + { + return g_restConnector.getSession( sessionId ); + } + + void ServerLobby::run( int argc, char* argv[] ) + { + g_log.setLogPath( "log\\SapphireLobby" ); + g_log.init(); + + g_log.info( "===========================================================" ); + g_log.info( "Sapphire Server Project " ); + g_log.info( "Version: x.y.z" ); + g_log.info( "Compiled: " __DATE__ " " __TIME__ ); + g_log.info( "===========================================================" ); + + if( !loadSettings( argc, argv ) ) + { + g_log.fatal( "Error loading settings! " ); + return; + } + + Network::HivePtr hive( new Network::Hive() ); + Network::addServerToHive< Network::GameConnection >( m_ip, m_port, hive ); + + g_log.info( "Lobbyserver ready for connections." ); + + boost::thread_group worker_threads; + worker_threads.create_thread( boost::bind( &Network::Hive::Run, hive.get() ) ); + worker_threads.join_all(); + + } + + bool ServerLobby::loadSettings( int argc, char* argv[] ) + { + g_log.info( "Loading config " + m_configPath ); + + if( !m_pConfig->loadConfig( m_configPath ) ) + { + g_log.fatal( "Error loading config " + m_configPath ); + return false; + } + std::vector args( argv + 1, argv + argc ); + for( auto i = 0; i + 1 < args.size(); i += 2 ) + { + std::string arg( "" ); + std::string val( "" ); + + try + { + arg = std::string( args[i] ); + val = std::string( args[i + 1] ); + + // trim '-' from start of arg + arg = arg.erase( 0, arg.find_first_not_of( '-' ) ); + + if( arg == "ip" ) + { + // todo: ip addr in config + m_pConfig->setValue< std::string >( "Settings.General.ListenIP", val ); + } + else if( arg == "p" || arg == "port" ) + { + m_pConfig->setValue< std::string >( "Settings.General.ListenPort", val ); + } + else if( arg == "ap" || arg == "auth" || arg == "authport" ) + { + m_pConfig->setValue< std::string>( "Settings.General.AuthPort", val ); + } + else if( arg == "worldIP" || arg == "worldIp" ) + { + m_pConfig->setValue < std::string >( "Settings.General.WorldIp", val ); + } + else if( arg == "worldPort" ) + { + m_pConfig->setValue< std::string >( "Settings.General.WorldPort", val ); + } + } + catch( ... ) + { + g_log.error( "Error parsing argument: " + arg + " " + "value: " + val + "\n" ); + g_log.error( "Usage: \n" ); + } + } + + m_port = m_pConfig->getValue< uint16_t >( "Settings.General.ListenPort" ); + m_ip = m_pConfig->getValue< std::string >( "Settings.General.ListenIp" ); + + g_restConnector.restHost = m_pConfig->getValue< std::string >( "Settings.General.RestHost" ); + g_restConnector.serverSecret = m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ); + + return true; + } +} diff --git a/src/servers/Server_Lobby/ServerLobby.h b/src/servers/Server_Lobby/ServerLobby.h new file mode 100644 index 00000000..6bc07868 --- /dev/null +++ b/src/servers/Server_Lobby/ServerLobby.h @@ -0,0 +1,55 @@ +#pragma once + +#ifndef _CSERVERLOBBY_H_ +#define _CSERVERLOBBY_H_ + +#include + +#include +#include + +#include "Forwards.h" + +const std::string LOBBY_VERSION = "0.0.5"; + +namespace Core +{ + class LobbySession; + class XMLConfig; + typedef std::map< std::string, LobbySessionPtr > LobbySessionMap; + + class ServerLobby + { + + friend class LobbyConnection; + private: + + LobbySessionMap m_sessionMap; + std::string m_configPath; + + uint16_t m_port; + std::string m_ip; + + public: + ServerLobby( const std::string& configPath ); + ~ServerLobby( void ); + + void run( int argc, char* argv[] ); + + bool loadSettings( int argc, char* argv[] ); + + void addSession( char* sessionId, LobbySessionPtr pSession ) + { + m_sessionMap[std::string( sessionId )] = pSession; + } + + LobbySessionPtr getSession( char* sessionId ); + uint32_t m_numConnections; + boost::shared_ptr m_pConfig; + + }; + + +} + +#endif diff --git a/src/servers/Server_Lobby/blowfish.cpp b/src/servers/Server_Lobby/blowfish.cpp new file mode 100644 index 00000000..ab15ecad --- /dev/null +++ b/src/servers/Server_Lobby/blowfish.cpp @@ -0,0 +1,258 @@ +// blowfish.cpp C++ class implementation of the BLOWFISH encryption algorithm +// _THE BLOWFISH ENCRYPTION ALGORITHM_ +// by Bruce Schneier +// Revised code--3/20/94 +// Converted to C++ class 5/96, Jim Conger + +#include "blowfish.h" +#include "blowfish.h2" // holds the random digit tables + +#define S(x,i) (SBoxes[i][x.w.byte##i]) +#define bf_F(x) (((S(x,0) + S(x,1)) ^ S(x,2)) + S(x,3)) +#define ROUND(a,b,n) (a.dword ^= bf_F(b) ^ PArray[n]) + + +BlowFish::BlowFish () +{ + PArray = new DWORD [18] ; + SBoxes = new DWORD [4][256] ; +} + +BlowFish::~BlowFish () +{ + delete PArray ; + delete [] SBoxes ; +} + + // the low level (private) encryption function +void BlowFish::Blowfish_encipher (DWORD *xl, DWORD *xr) +{ + union aword Xl, Xr ; + + Xl.dword = *xl ; + Xr.dword = *xr ; + + Xl.dword ^= PArray [0]; + ROUND (Xr, Xl, 1) ; ROUND (Xl, Xr, 2) ; + ROUND (Xr, Xl, 3) ; ROUND (Xl, Xr, 4) ; + ROUND (Xr, Xl, 5) ; ROUND (Xl, Xr, 6) ; + ROUND (Xr, Xl, 7) ; ROUND (Xl, Xr, 8) ; + ROUND (Xr, Xl, 9) ; ROUND (Xl, Xr, 10) ; + ROUND (Xr, Xl, 11) ; ROUND (Xl, Xr, 12) ; + ROUND (Xr, Xl, 13) ; ROUND (Xl, Xr, 14) ; + ROUND (Xr, Xl, 15) ; ROUND (Xl, Xr, 16) ; + Xr.dword ^= PArray [17] ; + + *xr = Xl.dword ; + *xl = Xr.dword ; +} + + // the low level (private) decryption function +void BlowFish::Blowfish_decipher (DWORD *xl, DWORD *xr) +{ + union aword Xl ; + union aword Xr ; + + Xl.dword = *xl ; + Xr.dword = *xr ; + + Xl.dword ^= PArray [17] ; + ROUND (Xr, Xl, 16) ; ROUND (Xl, Xr, 15) ; + ROUND (Xr, Xl, 14) ; ROUND (Xl, Xr, 13) ; + ROUND (Xr, Xl, 12) ; ROUND (Xl, Xr, 11) ; + ROUND (Xr, Xl, 10) ; ROUND (Xl, Xr, 9) ; + ROUND (Xr, Xl, 8) ; ROUND (Xl, Xr, 7) ; + ROUND (Xr, Xl, 6) ; ROUND (Xl, Xr, 5) ; + ROUND (Xr, Xl, 4) ; ROUND (Xl, Xr, 3) ; + ROUND (Xr, Xl, 2) ; ROUND (Xl, Xr, 1) ; + Xr.dword ^= PArray[0]; + + *xl = Xr.dword; + *xr = Xl.dword; +} + + + // constructs the enctryption sieve +void BlowFish::initialize (BYTE key[], int keybytes) +{ + int i, j ; + DWORD datal, datar ; + + + // first fill arrays from data tables + for (i = 0 ; i < 18 ; i++) + PArray [i] = bf_P [i] ; + + for (i = 0 ; i < 4 ; i++) + { + for (j = 0 ; j < 256 ; j++) + SBoxes [i][j] = bf_S [i][j] ; + } + + int v12; // eax@6 + int v13; // ecx@6 + int v14; // eax@8 + int v15; // edx@8 + int v16; // edx@8 + int v17; // eax@10 + int v18; // ecx@10 + int v19; // ecx@10 + int v20; // edx@12 + int v21; // edx@12 + + + + int v10 = keybytes; + int v9 = (int)key; + int v8 = 0; + int v11 = 0; + do { + v13 = (char)(*(BYTE *)(v8 + v9)); + v12 = v8 + 1; + if ( v12 >= v10 ) + v12 = 0; + v16 = (char)*(BYTE *)(v12 + v9); + v14 = v12 + 1; + v15 = (v13 << 8) | v16; + if ( v14 >= v10 ) + v14 = 0; + v19 = (char)*(BYTE *)(v14 + v9); + v17 = v14 + 1; + v18 = (v15 << 8) | v19; + if ( v17 >= v10 ) + v17 = 0; + v21 = (char)*(BYTE *)(v17 + v9); + v8 = v17 + 1; + v20 = (v18 << 8) | v21; + if ( v8 >= v10 ) + v8 = 0; + *((DWORD *)PArray + v11++) ^= v20; + } while ( v11 < 18 ); + + + + datal = 0 ; + datar = 0 ; + + for (i = 0 ; i < NPASS + 2 ; i += 2) + { + Blowfish_encipher (&datal, &datar) ; + PArray [i] = datal ; + PArray [i + 1] = datar ; + } + + for (i = 0 ; i < 4 ; ++i) + { + for (j = 0 ; j < 256 ; j += 2) + { + Blowfish_encipher (&datal, &datar) ; + SBoxes [i][j] = datal ; + SBoxes [i][j + 1] = datar ; + } + } +} + + // get output length, which must be even MOD 8 +DWORD BlowFish::GetOutputLength (DWORD lInputLong) +{ + DWORD lVal ; + + lVal = lInputLong % 8 ; // find out if uneven number of bytes at the end + if (lVal != 0) + return lInputLong + 8 - lVal ; + else + return lInputLong ; +} + + // Encode pIntput into pOutput. Input length in lSize. Returned value + // is length of output which will be even MOD 8 bytes. Input buffer and + // output buffer can be the same, but be sure buffer length is even MOD 8. +DWORD BlowFish::Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize) +{ + DWORD lCount, lOutSize, lGoodBytes ; + BYTE *pi, *po ; + int i, j ; + int SameDest = (pInput == pOutput ? 1 : 0) ; + + lOutSize = GetOutputLength (lSize) ; + for (lCount = 0 ; lCount < lOutSize ; lCount += 8) + { + if (SameDest) // if encoded data is being written into input buffer + { + if (lCount < lSize - 7) // if not dealing with uneven bytes at end + { + Blowfish_encipher ((DWORD *) pInput, + (DWORD *) (pInput + 4)) ; + } + else // pad end of data with null bytes to complete encryption + { + po = pInput + lSize ; // point at byte past the end of actual data + j = (int) (lOutSize - lSize) ; // number of bytes to set to null + for (i = 0 ; i < j ; i++) + *po++ = 0 ; + Blowfish_encipher ((DWORD *) pInput, + (DWORD *) (pInput + 4)) ; + } + pInput += 8 ; + } + else // output buffer not equal to input buffer, so must copy + { // input to output buffer prior to encrypting + if (lCount < lSize - 7) // if not dealing with uneven bytes at end + { + pi = pInput ; + po = pOutput ; + for (i = 0 ; i < 8 ; i++) +// copy bytes to output + *po++ = *pi++ ; + Blowfish_encipher ((DWORD *) pOutput, // now encrypt them + (DWORD *) (pOutput + 4)) ; + } + else // pad end of data with null bytes to complete encryption + { + lGoodBytes = lSize - lCount ; // number of remaining data bytes + po = pOutput ; + for (i = 0 ; i < (int) lGoodBytes ; i++) + *po++ = *pInput++ ; + for (j = i ; j < 8 ; j++) + *po++ = 0 ; + Blowfish_encipher ((DWORD *) pOutput, + (DWORD *) (pOutput + 4)) ; + } + pInput += 8 ; + pOutput += 8 ; + } + } + return lOutSize ; + } + + // Decode pIntput into pOutput. Input length in lSize. Input buffer and + // output buffer can be the same, but be sure buffer length is even MOD 8. +void BlowFish::Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize) +{ + DWORD lCount ; + BYTE *pi, *po ; + int i ; + int SameDest = (pInput == pOutput ? 1 : 0) ; + + for (lCount = 0 ; lCount < lSize ; lCount += 8) + { + if (SameDest) // if encoded data is being written into input buffer + { + Blowfish_decipher ((DWORD *) pInput, + (DWORD *) (pInput + 4)) ; + pInput += 8 ; + } + else // output buffer not equal to input buffer + { // so copy input to output before decoding + pi = pInput ; + po = pOutput ; + for (i = 0 ; i < 8 ; i++) + *po++ = *pi++ ; + Blowfish_decipher ((DWORD *) pOutput, + (DWORD *) (pOutput + 4)) ; + pInput += 8 ; + pOutput += 8 ; + } + } +} + diff --git a/src/servers/Server_Lobby/blowfish.h b/src/servers/Server_Lobby/blowfish.h new file mode 100644 index 00000000..e05b422b --- /dev/null +++ b/src/servers/Server_Lobby/blowfish.h @@ -0,0 +1,72 @@ +// blowfish.h interface file for blowfish.cpp +// _THE BLOWFISH ENCRYPTION ALGORITHM_ +// by Bruce Schneier +// Revised code--3/20/94 +// Converted to C++ class 5/96, Jim Conger + +#define MAXKEYBYTES 56 // 448 bits max +#define NPASS 16 // SBox passes + +#define DWORD unsigned long +#define WORD unsigned short +#define BYTE unsigned char + +class BlowFish +{ +private: + DWORD * PArray ; + DWORD (* SBoxes)[256]; + void Blowfish_encipher (DWORD *xl, DWORD *xr) ; + void Blowfish_decipher (DWORD *xl, DWORD *xr) ; + +public: + BlowFish () ; + ~BlowFish () ; + void initialize (BYTE key[], int keybytes) ; + DWORD GetOutputLength (DWORD lInputLong) ; + DWORD Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ; + void Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ; + +} ; + +// choose a byte order for your hardware +#define ORDER_DCBA // chosing Intel in this case + +#ifdef ORDER_DCBA // DCBA - little endian - intel + union aword { + DWORD dword; + BYTE byte [4]; + struct { + unsigned int byte3:8; + unsigned int byte2:8; + unsigned int byte1:8; + unsigned int byte0:8; + } w; + }; +#endif + +#ifdef ORDER_ABCD // ABCD - big endian - motorola + union aword { + DWORD dword; + BYTE byte [4]; + struct { + unsigned int byte0:8; + unsigned int byte1:8; + unsigned int byte2:8; + unsigned int byte3:8; + } w; + }; +#endif + +#ifdef ORDER_BADC // BADC - vax + union aword { + DWORD dword; + BYTE byte [4]; + struct { + unsigned int byte1:8; + unsigned int byte0:8; + unsigned int byte3:8; + unsigned int byte2:8; + } w; +}; +#endif diff --git a/src/servers/Server_Lobby/blowfish.h2 b/src/servers/Server_Lobby/blowfish.h2 new file mode 100644 index 00000000..a6b34893 --- /dev/null +++ b/src/servers/Server_Lobby/blowfish.h2 @@ -0,0 +1,267 @@ +// blowfish.h2 header file containing random number tables + +static DWORD bf_P[NPASS + 2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b, +}; +static DWORD bf_S[4][256] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +}; diff --git a/src/servers/Server_Lobby/client_http.hpp b/src/servers/Server_Lobby/client_http.hpp new file mode 100644 index 00000000..c7b9137b --- /dev/null +++ b/src/servers/Server_Lobby/client_http.hpp @@ -0,0 +1,418 @@ +#ifndef CLIENT_HTTP_HPP +#define CLIENT_HTTP_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class case_insensitive_equals { +public: + bool operator()( const std::string &key1, const std::string &key2 ) const { + return boost::algorithm::iequals( key1, key2 ); + } +}; +class case_insensitive_hash { +public: + size_t operator()( const std::string &key ) const { + std::size_t seed = 0; + for( auto &c : key ) + boost::hash_combine( seed, std::tolower( c ) ); + return seed; + } +}; + +namespace SimpleWeb { + template + class Client; + + template + class ClientBase { + public: + virtual ~ClientBase() {} + + class Response { + friend class ClientBase; + friend class Client; + public: + std::string http_version, status_code; + + std::istream content; + + std::unordered_multimap header; + + private: + boost::asio::streambuf content_buffer; + + Response() : content( &content_buffer ) {} + }; + + class Config { + friend class ClientBase; + private: + Config() {} + public: + /// Set timeout on requests in seconds. Default value: 0 (no timeout). + size_t timeout = 0; + /// Set proxy server (server:port) + std::string proxy_server; + }; + + /// Set before calling request + Config config; + + std::shared_ptr request( const std::string& request_type, const std::string& path = "/", boost::string_ref content = "", + const std::map& header = std::map() ) { + auto corrected_path = path; + if( corrected_path == "" ) + corrected_path = "/"; + if( !config.proxy_server.empty() && std::is_same::value ) + corrected_path = "http://" + host + ':' + std::to_string( port ) + corrected_path; + + boost::asio::streambuf write_buffer; + std::ostream write_stream( &write_buffer ); + write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; + write_stream << "Host: " << host << "\r\n"; + for( auto& h : header ) { + write_stream << h.first << ": " << h.second << "\r\n"; + } + if( content.size()>0 ) + write_stream << "Content-Length: " << content.size() << "\r\n"; + write_stream << "\r\n"; + + connect(); + + auto timer = get_timeout_timer(); + boost::asio::async_write( *socket, write_buffer, + [this, &content, timer]( const boost::system::error_code &ec, size_t /*bytes_transferred*/ ) { + if( timer ) + timer->cancel(); + if( !ec ) { + if( !content.empty() ) { + auto timer = get_timeout_timer(); + boost::asio::async_write( *socket, boost::asio::buffer( content.data(), content.size() ), + [this, timer]( const boost::system::error_code &ec, size_t /*bytes_transferred*/ ) { + if( timer ) + timer->cancel(); + if( ec ) { + std::lock_guard lock( socket_mutex ); + this->socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + } + } + else { + std::lock_guard lock( socket_mutex ); + socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + io_service.reset(); + io_service.run(); + + return request_read(); + } + + std::shared_ptr request( const std::string& request_type, const std::string& path, std::iostream& content, + const std::map& header = std::map() ) { + auto corrected_path = path; + if( corrected_path == "" ) + corrected_path = "/"; + if( !config.proxy_server.empty() && std::is_same::value ) + corrected_path = "http://" + host + ':' + std::to_string( port ) + corrected_path; + + content.seekp( 0, std::ios::end ); + auto content_length = content.tellp(); + content.seekp( 0, std::ios::beg ); + + boost::asio::streambuf write_buffer; + std::ostream write_stream( &write_buffer ); + write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; + write_stream << "Host: " << host << "\r\n"; + for( auto& h : header ) { + write_stream << h.first << ": " << h.second << "\r\n"; + } + if( content_length>0 ) + write_stream << "Content-Length: " << content_length << "\r\n"; + write_stream << "\r\n"; + if( content_length>0 ) + write_stream << content.rdbuf(); + + connect(); + + auto timer = get_timeout_timer(); + boost::asio::async_write( *socket, write_buffer, + [this, timer]( const boost::system::error_code &ec, size_t /*bytes_transferred*/ ) { + if( timer ) + timer->cancel(); + if( ec ) { + std::lock_guard lock( socket_mutex ); + socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + io_service.reset(); + io_service.run(); + + return request_read(); + } + + void close() { + std::lock_guard lock( socket_mutex ); + if( socket ) { + boost::system::error_code ec; + socket->lowest_layer().shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec ); + socket->lowest_layer().close(); + } + } + + protected: + boost::asio::io_service io_service; + boost::asio::ip::tcp::resolver resolver; + + std::unique_ptr socket; + std::mutex socket_mutex; + + std::string host; + unsigned short port; + + ClientBase( const std::string& host_port, unsigned short default_port ) : resolver( io_service ) { + auto parsed_host_port = parse_host_port( host_port, default_port ); + host = parsed_host_port.first; + port = parsed_host_port.second; + } + + std::pair parse_host_port( const std::string &host_port, unsigned short default_port ) { + std::pair parsed_host_port; + size_t host_end = host_port.find( ':' ); + if( host_end == std::string::npos ) { + parsed_host_port.first = host_port; + parsed_host_port.second = default_port; + } + else { + parsed_host_port.first = host_port.substr( 0, host_end ); + parsed_host_port.second = static_cast( stoul( host_port.substr( host_end + 1 ) ) ); + } + return parsed_host_port; + } + + virtual void connect() = 0; + + std::shared_ptr get_timeout_timer() { + if( config.timeout == 0 ) + return nullptr; + + auto timer = std::make_shared( io_service ); + timer->expires_from_now( boost::posix_time::seconds( config.timeout ) ); + timer->async_wait( [this]( const boost::system::error_code& ec ) { + if( !ec ) { + close(); + } + } ); + return timer; + } + + void parse_response_header( const std::shared_ptr &response ) const { + std::string line; + getline( response->content, line ); + size_t version_end = line.find( ' ' ); + if( version_end != std::string::npos ) { + if( 5http_version = line.substr( 5, version_end - 5 ); + if( ( version_end + 1 )status_code = line.substr( version_end + 1, line.size() - ( version_end + 1 ) - 1 ); + + getline( response->content, line ); + size_t param_end; + while( ( param_end = line.find( ':' ) ) != std::string::npos ) { + size_t value_start = param_end + 1; + if( ( value_start )header.insert( std::make_pair( line.substr( 0, param_end ), line.substr( value_start, line.size() - value_start - 1 ) ) ); + } + + getline( response->content, line ); + } + } + } + + std::shared_ptr request_read() { + std::shared_ptr response( new Response() ); + + boost::asio::streambuf chunked_streambuf; + + auto timer = get_timeout_timer(); + boost::asio::async_read_until( *socket, response->content_buffer, "\r\n\r\n", + [this, &response, &chunked_streambuf, timer]( const boost::system::error_code& ec, size_t bytes_transferred ) { + if( timer ) + timer->cancel(); + if( !ec ) { + size_t num_additional_bytes = response->content_buffer.size() - bytes_transferred; + + parse_response_header( response ); + + auto header_it = response->header.find( "Content-Length" ); + if( header_it != response->header.end() ) { + auto content_length = stoull( header_it->second ); + if( content_length>num_additional_bytes ) { + auto timer = get_timeout_timer(); + boost::asio::async_read( *socket, response->content_buffer, + boost::asio::transfer_exactly( content_length - num_additional_bytes ), + [this, timer]( const boost::system::error_code& ec, size_t /*bytes_transferred*/ ) { + if( timer ) + timer->cancel(); + if( ec ) { + std::lock_guard lock( socket_mutex ); + this->socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + } + } + else if( ( header_it = response->header.find( "Transfer-Encoding" ) ) != response->header.end() && header_it->second == "chunked" ) { + request_read_chunked( response, chunked_streambuf ); + } + } + else { + std::lock_guard lock( socket_mutex ); + socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + io_service.reset(); + io_service.run(); + + return response; + } + + void request_read_chunked( const std::shared_ptr &response, boost::asio::streambuf &streambuf ) { + auto timer = get_timeout_timer(); + boost::asio::async_read_until( *socket, response->content_buffer, "\r\n", + [this, &response, &streambuf, timer]( const boost::system::error_code& ec, size_t bytes_transferred ) { + if( timer ) + timer->cancel(); + if( !ec ) { + std::string line; + getline( response->content, line ); + bytes_transferred -= line.size() + 1; + line.pop_back(); + std::streamsize length = stol( line, 0, 16 ); + + auto num_additional_bytes = static_cast( response->content_buffer.size() - bytes_transferred ); + + auto post_process = [this, &response, &streambuf, length] { + std::ostream stream( &streambuf ); + if( length>0 ) { + std::vector buffer( static_cast( length ) ); + response->content.read( &buffer[0], length ); + stream.write( &buffer[0], length ); + } + + //Remove "\r\n" + response->content.get(); + response->content.get(); + + if( length>0 ) + request_read_chunked( response, streambuf ); + else { + std::ostream response_stream( &response->content_buffer ); + response_stream << stream.rdbuf(); + } + }; + + if( ( 2 + length )>num_additional_bytes ) { + auto timer = get_timeout_timer(); + boost::asio::async_read( *socket, response->content_buffer, + boost::asio::transfer_exactly( 2 + length - num_additional_bytes ), + [this, post_process, timer]( const boost::system::error_code& ec, size_t /*bytes_transferred*/ ) { + if( timer ) + timer->cancel(); + if( !ec ) { + post_process(); + } + else { + std::lock_guard lock( socket_mutex ); + this->socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + } + else + post_process(); + } + else { + std::lock_guard lock( socket_mutex ); + socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + } + }; + + template + class Client : public ClientBase {}; + + typedef boost::asio::ip::tcp::socket HTTP; + + template<> + class Client : public ClientBase { + public: + Client( const std::string& server_port_path ) : ClientBase::ClientBase( server_port_path, 80 ) {} + + protected: + void connect() { + if( !socket || !socket->is_open() ) { + std::unique_ptr query; + if( config.proxy_server.empty() ) + query = std::unique_ptr( new boost::asio::ip::tcp::resolver::query( host, std::to_string( port ) ) ); + else { + auto proxy_host_port = parse_host_port( config.proxy_server, 8080 ); + query = std::unique_ptr( new boost::asio::ip::tcp::resolver::query( proxy_host_port.first, std::to_string( proxy_host_port.second ) ) ); + } + resolver.async_resolve( *query, [this]( const boost::system::error_code &ec, + boost::asio::ip::tcp::resolver::iterator it ) { + if( !ec ) { + { + std::lock_guard lock( socket_mutex ); + socket = std::unique_ptr( new HTTP( io_service ) ); + } + + auto timer = get_timeout_timer(); + boost::asio::async_connect( *socket, it, [this, timer] + ( const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/ ) { + if( timer ) + timer->cancel(); + if( !ec ) { + boost::asio::ip::tcp::no_delay option( true ); + this->socket->set_option( option ); + } + else { + std::lock_guard lock( socket_mutex ); + this->socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + } + else { + std::lock_guard lock( socket_mutex ); + socket = nullptr; + throw boost::system::system_error( ec ); + } + } ); + io_service.reset(); + io_service.run(); + } + } + }; +} + +#endif /* CLIENT_HTTP_HPP */ diff --git a/src/servers/Server_Lobby/mainLobbyServer.cpp b/src/servers/Server_Lobby/mainLobbyServer.cpp new file mode 100644 index 00000000..5627449d --- /dev/null +++ b/src/servers/Server_Lobby/mainLobbyServer.cpp @@ -0,0 +1,12 @@ +#include "ServerLobby.h" + +Core::ServerLobby g_serverLobby( "config/settings_lobby.xml" ); + +int main( int argc, char* argv[] ) +{ + + g_serverLobby.run( argc, argv ); + + return 0; +} + diff --git a/src/servers/Server_REST/CMakeLists.txt b/src/servers/Server_REST/CMakeLists.txt new file mode 100644 index 00000000..80e8d840 --- /dev/null +++ b/src/servers/Server_REST/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Sapphire) + +include_directories("../../libraries/external/ChaiScript-6.0.0/include/") +include_directories("../../libraries/sapphire/datReader/") +include_directories("../") + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") + +set(SERVER_COMMON_DIR ../Server_Common) +set(Boost_USE_STATIC_LIBS ON) + +if(UNIX) + include_directories("/usr/include/mysql/") + message(STATUS "Setting GCC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32") + + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + else() + if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib) + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + else() + message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!") + endif() + endif() +else() + add_definitions(-D_WIN32_WINNT=0x601) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL/") + message(STATUS "Setting MSVC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + message(STATUS "Using boost in /src/lib") + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + else() + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR})) + set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR}) + set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR}) + else() + message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!") + endif() + endif() +endif() + + +include_directories(${Boost_INCLUDE_DIR}) + +link_directories(${BOOST_LIBRARYDIR}) +link_directories(${SERVER_COMMON_DIR}) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/zlib) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/") +add_executable(server_rest ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) +add_dependencies(server_rest Common xivdat) + +set_target_properties(server_rest PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" +) + +if (UNIX) + target_link_libraries (server_rest Common xivdat pthread mysqlclient dl z) +else() + target_link_libraries (server_rest Common xivdat libmysql zlib1) +endif() + +target_link_libraries( server_rest ${Boost_LIBRARIES} ${Boost_LIBRARIES} ) diff --git a/src/servers/Server_REST/Forwards.h b/src/servers/Server_REST/Forwards.h new file mode 100644 index 00000000..56d3b92d --- /dev/null +++ b/src/servers/Server_REST/Forwards.h @@ -0,0 +1,102 @@ +#ifndef _FORWARDS_H +#define _FORWARDS_H + +#include + +namespace Core +{ + + class Cell; + class Zone; + class Item; + class ItemContainer; + class Inventory; + class Session; + class XMLConfig; + class ZonePosition; + + typedef boost::shared_ptr ZonePtr; + typedef boost::shared_ptr ItemPtr; + typedef boost::shared_ptr ItemContainerPtr; + typedef boost::shared_ptr InventoryPtr; + typedef boost::shared_ptr SessionPtr; + typedef boost::shared_ptr XMLConfigPtr; + typedef boost::shared_ptr ZonePositionPtr; + + namespace StatusEffect + { + class StatusEffect; + class StatusEffectContainer; + + typedef boost::shared_ptr StatusEffectPtr; + typedef boost::shared_ptr StatusEffectContainerPtr; + } + + namespace Entity + { + class Actor; + class Player; + class BattleNpc; + + typedef boost::shared_ptr ActorPtr; + typedef boost::shared_ptr PlayerPtr; + typedef boost::shared_ptr BattleNpcPtr; + } + + namespace Event + { + class Event; + + typedef boost::shared_ptr EventPtr; + } + + namespace Action + { + class Action; + class ActionTeleport; + class EventAction; + + typedef boost::shared_ptr ActionPtr; + typedef boost::shared_ptr ActionTeleportPtr; + typedef boost::shared_ptr EventActionPtr; + } + + + namespace Network + { + class Hive; + class Acceptor; + class Connection; + class WorldConnection; + class SessionConnection; + class ZoneConnection; + + typedef boost::shared_ptr HivePtr; + typedef boost::shared_ptr AcceptorPtr; + typedef boost::shared_ptr ConnectionPtr; + typedef boost::shared_ptr WorldConnectionPtr; + typedef boost::shared_ptr ZoneConnectionPtr; + typedef boost::shared_ptr SessionConnectionPtr; + + namespace Packets + { + class GamePacket; + + + typedef boost::shared_ptr GamePacketPtr; + + + } + } + + namespace Scripting + { + typedef std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t ) > EventReturnCallback; + } + + typedef std::function< void( Entity::Player&, uint32_t, uint64_t ) > ActionCallback; + +} + + +#endif diff --git a/src/servers/Server_REST/LoginSession.cpp b/src/servers/Server_REST/LoginSession.cpp new file mode 100644 index 00000000..d403ed37 --- /dev/null +++ b/src/servers/Server_REST/LoginSession.cpp @@ -0,0 +1,12 @@ +#include "LoginSession.h" +namespace Core { + LoginSession::LoginSession(void) { + //setSocket(NULL); + + } + + LoginSession::~LoginSession(void) { + + } +} + diff --git a/src/servers/Server_REST/LoginSession.h b/src/servers/Server_REST/LoginSession.h new file mode 100644 index 00000000..b74fd8fe --- /dev/null +++ b/src/servers/Server_REST/LoginSession.h @@ -0,0 +1,59 @@ +#pragma once + +#ifndef _CLoginSession_H_ +#define _CLoginSession_H_ + +#include +#include +#include + +namespace Core { + + class LoginSession { + + private: + uint32_t m_IP; + uint32_t m_accountID; + + uint8_t m_sessionId[56]; + + public: + + std::string newCharName; + + LoginSession(void); + ~LoginSession(void); + + uint32_t getIP() { + return m_IP; + } + + void setSessionId(uint8_t * sessionId) { + memcpy(m_sessionId, sessionId, 56); + } + + void setIP(uint32_t iP) { + m_IP = iP; + } + + uint32_t getAccountID() { + return m_accountID; + } + + void setAccountID(uint32_t iD) { + m_accountID = iD; + } + + /*INLINE CLobbySocket* getSocket() { + return m_pGS; + } + + INLINE void setSocket(CLobbySocket * pS) { + m_pGS = pS; + }*/ + + }; + +} + +#endif diff --git a/src/servers/Server_REST/PlayerMinimal.cpp b/src/servers/Server_REST/PlayerMinimal.cpp new file mode 100644 index 00000000..356057a4 --- /dev/null +++ b/src/servers/Server_REST/PlayerMinimal.cpp @@ -0,0 +1,423 @@ +#include "PlayerMinimal.h" + +//#include "Core/Server_Game/CServerGame.h" +#include +#include +#include +#include + + +extern Core::Db::Database g_database; +extern Core::Data::ExdData g_exdData; + +namespace Core { + + using namespace Common; + + // player constructor + PlayerMinimal::PlayerMinimal( void ) : m_iD( 0 ) + { + + + } + + // deconstructor + PlayerMinimal::~PlayerMinimal( void ) + { + + } + + // load player from the db + // TODO change void CPlayer::load to bool, we want to know if something went wrong + void PlayerMinimal::load( uint32_t charId ) + { + + boost::shared_ptr pQR = g_database.query( "SELECT c.Name, " + " c.Customize, " + " cpc.BirthDay, " + " cpc.BirthMonth, " + " cpc.GuardianDeity, " + " cpc.Class, " + " c.ModelEquip, " + " c.ContentId, " + " c.PrimaryTerritoryId " + " FROM charabase AS c " + " INNER JOIN charadetail AS cpc " + " ON cpc.CharacterId = c.CharacterId " + " WHERE c.CharacterId = " + std::to_string( charId ) + ";" ); + if( !pQR ) + { + return; + } + + m_iD = charId; + + Db::Field *field = pQR->fetch(); + + memset( m_name, 0, 32 ); + + strcpy( m_name, field[0].getString() ); + + field[1].getBinary( (char*)m_look, 26 ); + + field[6].getBinary( (char*)m_modelEquip, 40 ); + + for( int i = 0; i < 26; i++ ) + { + m_lookMap[i] = m_look[i]; + } + + setBirthDay( field[2].getInt8(), field[3].getInt8() ); + m_guardianDeity = field[4].getInt8(); + m_class = field[5].getInt8(); + m_contentId = field[7].getUInt64(); + m_zoneId = field[8].getUInt16(); + + auto pQR2 = g_database.query( "SELECT * FROM characlass WHERE CharacterId = " + std::to_string( charId ) + ";" ); + + Db::Field* field2 = pQR2->fetch(); + + for( uint8_t i = 0; i < 25; i++ ) + { + uint8_t index = i * 2; + m_classMap[i] = field2[index].getUInt8(); + //m_expArray[i] = + } + } + + + std::string PlayerMinimal::getLookString() + { + + auto it = m_lookMap.begin(); + + std::string lookString; + + for( ; it != m_lookMap.end(); ++it ) + { + + std::string s = std::to_string( it->second ); + + lookString += "\"" + s + "\""; + if( it != m_lookMap.end() ) + { + lookString += ","; + } + } + + return lookString.substr( 0, lookString.size() - 1 ); + } + + std::string PlayerMinimal::getModelString() + { + std::string modelString = "\"" + + std::to_string( m_modelEquip[2] ) + "\",\"" + + std::to_string( m_modelEquip[3] ) + "\",\"" + + std::to_string( m_modelEquip[4] ) + "\",\"" + + std::to_string( m_modelEquip[6] ) + "\",\"" + + std::to_string( m_modelEquip[7] ) + "\",\"5\",\"6\",\"7\",\"8\",\"9\""; + return modelString; + } + + std::string PlayerMinimal::getInfoJson() + { + std::string charDetails = "{\"content\":[\"" + std::string( getName() ) + "\"," + + "[" + getClassString() + "]," + + "\"0\",\"0\",\"0\",\"" + std::to_string( getBirthMonth() ) + "\",\"" + std::to_string( getBirthDay() ) + "\",\"" + std::to_string( getGuardianDeity() ) + "\",\"" + std::to_string( m_class ) + "\",\"0\",\"" + std::to_string( getZoneId() ) + "\"," + + "[" + getLookString() + "]," + + "\"0\",\"0\"," + + "[" + getModelString() + "]," + + "\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"]," + + "\"classname\":\"ClientSelectData\",\"classid\":116}"; + return charDetails; + } + + std::string PlayerMinimal::getClassString() + { + + std::map::iterator it; + + it = m_classMap.begin(); + + std::string classString; + + for( ; it != m_classMap.end(); ++it ) + { + + std::string s = std::to_string( it->second ); + + classString += "\"" + s + "\""; + if( it != m_classMap.end() ) + { + classString += ","; + } + } + + return classString.substr( 0, classString.size() - 1 ); + } + + void PlayerMinimal::saveAsNew() + { + + char customize[32]; + char howTo[32]; + memset( howTo, 0, 32 ); + + char aetherytes[12]; + memset( aetherytes, 0, 12 ); + + char discovery[411]; + memset( discovery, 0, 411 ); + + char questComplete[200]; + memset( questComplete, 0, 200 ); + + char unlocks[64]; + memset( unlocks, 0, 64 ); + + int16_t questTracking[5] = { -1, -1, -1, -1, -1 }; + + uint16_t size = static_cast< uint16_t >( m_lookMap.size() ); + + for( int i = 0; i < m_lookMap.size(); i++ ) + { + customize[i] = m_lookMap[i]; + } + + uint32_t equipModel[10]; + memset( equipModel, 0, 40 ); + + uint32_t startZone; + float x, y, z, o; + int startTown = 0; + + switch( m_class ) + { + case Core::Common::ClassJob::CLASS_CONJURER: + case Core::Common::ClassJob::CLASS_LANCER: + case Core::Common::ClassJob::CLASS_ARCHER: + x = 127.0f; + y = -13.0f; + z = 147.0f; + o = -2.1f; + startZone = 183; + startTown = 2; + break; + + case Core::Common::ClassJob::CLASS_MARAUDER: + case Core::Common::ClassJob::CLASS_ARCANIST: + x = -53.0f; + y = 18.0f; + z = 0.0f; + o = 1.5f; + startTown = 1; + startZone = 181; + break; + + case Core::Common::ClassJob::CLASS_THAUMATURGE: + case Core::Common::ClassJob::CLASS_PUGILIST: + case Core::Common::ClassJob::CLASS_GLADIATOR: + x = 42.0f; + y = 4.0f; + z = -157.6f; + o = -0.3f; + startTown = 3; + startZone = 182; + break; + } + + g_database.execute( "INSERT INTO charabase " + "(Hp, " + " Mp, " + " CharacterId, " + " ContentId, " + " Customize, " + " Name, " + " Voice, " + " FirstLogin, " + " IsNewGame, " + " PrimaryTerritoryId, " + " Pos_0_0, " + " Pos_0_1, " + " Pos_0_2, " + " Pos_0_3, " + " AccountId, " + " ModelEquip, " + " IsNewAdventurer, " + " UPDATE_DATE ) " + " VALUES (100, 100, " + std::to_string( m_iD ) + ", " + std::to_string( m_contentId ) + ", " + + " UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)customize, size ) ) + "'), " + + "'" + std::string( m_name ) + "', " + std::to_string( m_voice ) + ", 1, 1, " + + std::to_string( startZone ) + ", " + std::to_string( x ) + ", " + + std::to_string( y ) + ", " + std::to_string( z ) + ", " + std::to_string( o ) + ", " + + std::to_string( m_accountId ) + ", UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)equipModel, 40 ) ) + "'), 1, NOW());" ); + + g_database.execute( "INSERT INTO charadetail " + "(CharacterId, " + " GuardianDeity, " + " Birthday, " + " BirthMonth, " + " Class, " + " CreateUnixTime, " + " IsActive, " + " Status, " + " FirstClass, " + " HomePoint, " + " StartTown, " + " Discovery, " + " HowTo, " + " QuestCompleteFlags, " + " unlocks, " + " QuestTracking, " + " Aetheryte, " + " UPDATE_DATE ) " + " VALUES (" + std::to_string( m_iD ) + ", " + + std::to_string( m_guardianDeity ) + ", " + + std::to_string( m_birthDay ) + ", " + + std::to_string( m_birthMonth ) + ", " + + std::to_string( m_class ) + ", UNIX_TIMESTAMP(NOW()), 1, 1, " + + std::to_string( m_class ) + ", 2, " + + std::to_string( startTown ) + ", " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)discovery, 411 ) ) + "'), " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)howTo, 32 ) ) + "'), " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)questComplete, 200 ) ) + "'), " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)unlocks, 64 ) ) + "'), " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)questTracking, 10 ) ) + "'), " + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)aetherytes, 12 ) ) + "'), NOW());" ); + + + g_database.execute( "INSERT INTO characlass (CharacterId, Lv_" + std::to_string( g_exdData.m_classJobInfoMap[m_class].exp_idx ) + ", UPDATE_DATE ) " + " VALUES (" + std::to_string( m_iD ) + ", 1, NOW());" ); + + g_database.execute( "INSERT INTO charaquest (CharacterId, UPDATE_DATE ) " + " VALUES (" + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charainfosearch (CharacterId, UPDATE_DATE ) " + " VALUES (" + std::to_string( m_iD ) + ", NOW());" ); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // SET UP INVENTORIES + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Bag0 ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Bag1 ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Bag2 ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Bag3 ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryOff ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryHead ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryBody ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryHand ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryWaist ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryLegs ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryFeet ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmotyNeck ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryEar ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE )" + "VALUES ( " + std::to_string( InventoryType::ArmoryWrist ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryRing ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::ArmoryMain ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Currency ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + g_database.execute( "INSERT INTO charaiteminventory (storageId, CharacterId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::Crystal ) + ", " + std::to_string( m_iD ) + ", NOW());" ); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // SETUP EQUIPMENT / STARTING GEAR + auto classJobInfo = g_exdData.m_classJobInfoMap[m_class]; + uint32_t weaponId = classJobInfo.start_weapon_id; + uint64_t uniqueId = g_database.getNextUId(); + + uint8_t race = customize[CharaLook::Race]; + uint8_t gender = customize[CharaLook::Gender]; + + auto raceInfo = g_exdData.getRaceInfo( race ); + + int32_t body; + int32_t hands; + int32_t legs; + int32_t feet; + uint64_t bodyUid = g_database.getNextUId(); + uint64_t handsUid = g_database.getNextUId(); + uint64_t legsUid = g_database.getNextUId(); + uint64_t feetUid = g_database.getNextUId(); + + if( gender == 0 ) + { + body = raceInfo->male_body; + hands = raceInfo->male_hands; + legs = raceInfo->male_legs; + feet = raceInfo->male_feet; + } + else + { + body = raceInfo->female_body; + hands = raceInfo->female_hands; + legs = raceInfo->female_legs; + feet = raceInfo->female_feet; + } + + g_database.execute( "INSERT INTO charaglobalitem (CharacterId, ItemId, catalogId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( m_iD ) + ", " + std::to_string( uniqueId ) + ", " + + std::to_string( weaponId ) + ", NOW());" ); + g_database.execute( "INSERT INTO charaglobalitem (CharacterId, ItemId, catalogId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( m_iD ) + ", " + std::to_string( bodyUid ) + ", " + + std::to_string( body ) + ", NOW());" ); + g_database.execute( "INSERT INTO charaglobalitem (CharacterId, ItemId, catalogId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( m_iD ) + ", " + std::to_string( handsUid ) + ", " + + std::to_string( hands ) + ", NOW());" ); + g_database.execute( "INSERT INTO charaglobalitem (CharacterId, ItemId, catalogId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( m_iD ) + ", " + std::to_string( legsUid ) + ", " + + std::to_string( legs ) + ", NOW());" ); + g_database.execute( "INSERT INTO charaglobalitem (CharacterId, ItemId, catalogId, UPDATE_DATE ) " + "VALUES ( " + std::to_string( m_iD ) + ", " + std::to_string( feetUid ) + ", " + + std::to_string( feet ) + ", NOW());" ); + g_database.execute( "INSERT INTO charaitemgearset (storageId, CharacterId, " + "container_" + std::to_string( EquipSlot::MainHand ) + ", " + "container_" + std::to_string( EquipSlot::Body ) + ", " + "container_" + std::to_string( EquipSlot::Hands ) + ", " + "container_" + std::to_string( EquipSlot::Legs ) + ", " + "container_" + std::to_string( EquipSlot::Feet ) + ", " + "UPDATE_DATE ) " + "VALUES ( " + std::to_string( InventoryType::GearSet0 ) + ", " + std::to_string( m_iD ) + ", " + + std::to_string( uniqueId ) + ", " + + std::to_string( bodyUid ) + ", " + + std::to_string( handsUid ) + ", " + + std::to_string( legsUid ) + ", " + + std::to_string( feetUid ) + ", " + + "NOW());" ); + + + + } +} diff --git a/src/servers/Server_REST/PlayerMinimal.h b/src/servers/Server_REST/PlayerMinimal.h new file mode 100644 index 00000000..af195966 --- /dev/null +++ b/src/servers/Server_REST/PlayerMinimal.h @@ -0,0 +1,171 @@ +#ifndef _PLAYERMINIMAL_H +#define _PLAYERMINIMAL_H + +#include +#include +#include +namespace Core { + + + class PlayerMinimal + { + public: + PlayerMinimal( void ); + ~PlayerMinimal( void ); + + // write player to the database + void write(); + + // load player from db, by id + void load( uint32_t charId ); + + void saveAsNew(); + + std::string getLookString(); + + std::string getInfoJson(); + + std::string getModelString(); + + std::string getClassString(); + + // return the id of the actor + uint32_t getId() + { + return m_iD; + } + + void setId( uint32_t id ) + { + m_iD = id; + } + + void setContentId( uint64_t id ) + { + m_contentId = id; + } + + uint64_t getContentId() + { + return m_contentId; + } + + + uint32_t getAccountId() + { + return m_accountId; + } + + void setAccountId( uint32_t accountId ) + { + m_accountId = accountId; + } + + // return the actors name + char * getName() + { + return m_name; + } + + void setLook( uint8_t index, uint32_t value ) + { + m_lookMap[index] = value; + } + + + // return the actors name + void setName( const char* name ) + { + strcpy( m_name, name ); + } + + void setClass( uint8_t classId ) + { + m_class = classId; + } + + uint8_t getClass() + { + return m_class; + } + + uint8_t getGuardianDeity() + { + return m_guardianDeity; + } + + void setGuardianDeity( uint8_t guardianId ) + { + m_guardianDeity = guardianId; + } + + void setBirthDay( uint8_t day, uint8_t month ) + { + m_birthDay = day; + m_birthMonth = month; + } + + uint8_t getBirthDay() + { + return m_birthDay; + } + + uint8_t getBirthMonth() + { + return m_birthMonth; + } + + uint8_t getVoice() + { + return m_birthMonth; + } + + void setVoice( uint8_t voice ) + { + m_voice = voice; + } + + uint32_t getZoneId() + { + return m_zoneId; + } + + uint32_t getTribe() + { + return m_tribe; + } + + void setTribe( uint8_t tribe ) + { + m_tribe = tribe; + } + + uint32_t m_modelEquip[10]; + + private: + uint32_t m_accountId; + uint32_t m_iD; + uint64_t m_contentId; + + uint8_t m_guardianDeity; + uint8_t m_birthMonth; + uint8_t m_birthDay; + uint8_t m_class; + + uint8_t m_voice; + + uint8_t m_tribe; + + uint16_t m_zoneId; + + std::map m_lookMap; + std::map m_classMap; + uint8_t m_look[26]; + + char m_name[34]; + + + }; + +} +#endif diff --git a/src/servers/Server_REST/SapphireAPI.cpp b/src/servers/Server_REST/SapphireAPI.cpp new file mode 100644 index 00000000..764ef7b1 --- /dev/null +++ b/src/servers/Server_REST/SapphireAPI.cpp @@ -0,0 +1,294 @@ +#include "SapphireAPI.h" +#include +#include "Session.h" +#include "PlayerMinimal.h" +#include +#include + +#define BOOST_SPIRIT_THREADSAFE +#include +#include +#include +#include +#include +#include + +extern Core::Db::Database g_database; + +Core::Network::SapphireAPI::SapphireAPI() +{ + +} + +Core::Network::SapphireAPI::~SapphireAPI() +{ + +} + +bool Core::Network::SapphireAPI::login( const std::string& username, const std::string& pass, std::string& sId ) +{ + std::string query = "SELECT account_id FROM accounts WHERE account_name = '" + username + "' AND account_pass = '" + pass + "';"; + + // check if a user with that name / password exists + auto pQR = g_database.query( query ); + // found? + if( !pQR ) + return false; + + // user found, proceed + int accountId = pQR->fetch()[0].getUInt32(); + + // session id string generation + srand( ( unsigned int )time( NULL ) + 42 ); + uint8_t sid[58]; + + for( int i = 0; i < 56; i += 4 ) + { + short number = 0x1111 + rand() % 0xFFFF; + sprintf( ( char* )sid + i, "%04hx", number ); + } + + // create session for the new sessionid and store to sessionlist + auto pSession = boost::make_shared< Session >(); + pSession->setAccountId( accountId ); + pSession->setSessionId( sid ); + + //auto ip2 = boost::asio::ip::address::from_string( request->remote_endpoint_address ); + + //pSession->setIP( ip2.to_v4().to_ulong() ); + + std::stringstream ss; + + for( size_t i = 0; i < 56; i++ ) + { + ss << std::hex << sid[i]; + } + m_sessionMap[ ss.str() ] = pSession; + sId = ss.str(); + + return true; + +} + +bool Core::Network::SapphireAPI::createAccount( const std::string& username, const std::string& pass, std::string& sId ) +{ + // get account from login name + auto pQR = g_database.query( "SELECT account_id FROM accounts WHERE account_name = '" + username + "';" ); + + // if account was found + if( pQR ) + return false; + + // we are clear and can create a new account + // get the next free account id + pQR = g_database.query( "SELECT MAX(account_id) FROM accounts;" ); + int accountId = pQR->fetch()[0].getUInt32() + 1; + + // store the account to the db + g_database.execute( "INSERT INTO accounts (account_Id, account_name, account_pass, account_created) VALUE(%i, '%s', '%s', %i);", + accountId, + username.c_str(), + pass.c_str(), + time( NULL ) ); + + + if( !login( username, pass, sId ) ) + return false; + + return true; + +} + +int Core::Network::SapphireAPI::createCharacter( const int& accountId, const std::string& name, const std::string& infoJson ) +{ + Core::PlayerMinimal newPlayer; + + newPlayer.setAccountId( accountId ); + newPlayer.setId( getNextCharId() ); + newPlayer.setContentId( getNextContentId() ); + newPlayer.setName( name.c_str() ); + + boost::property_tree::ptree pt; + + std::stringstream ss; + ss << infoJson; + + boost::property_tree::read_json( ss, pt ); + + const char *ptr = infoJson.c_str() + 50; + + std::string lookPart( ptr ); + int pos = lookPart.find_first_of( "]" ); + if( pos != std::string::npos ) + { + lookPart = lookPart.substr( 0, pos + 1 ); + } + + std::vector tmpVector; + std::vector tmpVector2; + + BOOST_FOREACH( boost::property_tree::ptree::value_type &v, pt.get_child( "content" ) ) + { + boost::property_tree::ptree subtree1 = v.second; + BOOST_FOREACH( boost::property_tree::ptree::value_type &vs, subtree1 ) + { + boost::property_tree::ptree subtree2 = vs.second; + //std::cout << vs.second.data(); + tmpVector.push_back( std::stoi( vs.second.data() ) ); + } + if( !v.second.data().empty() ) + tmpVector2.push_back( std::stoi( v.second.data() ) ); + } + std::vector::iterator it = tmpVector.begin(); + for( int i = 0; it != tmpVector.end(); ++it, i++ ) + { + newPlayer.setLook( i, *it ); + } + + std::string rest = infoJson.substr( pos + 53 ); + + newPlayer.setVoice( tmpVector2.at( 0 ) ); + newPlayer.setGuardianDeity( tmpVector2.at( 1 ) ); + newPlayer.setBirthDay( tmpVector2.at( 3 ), tmpVector2.at( 2 ) ); + newPlayer.setClass( tmpVector2.at( 4 ) ); + newPlayer.setTribe( tmpVector2.at( 5 ) ); + + newPlayer.saveAsNew(); + + return newPlayer.getAccountId(); +} + +void Core::Network::SapphireAPI::deleteCharacter( std::string name, uint32_t accountId ) +{ + PlayerMinimal deletePlayer; + auto charList = getCharList( accountId ); + for( uint32_t i = 0; i < charList.size(); i++ ) + { + PlayerMinimal tmpPlayer = charList.at( i ); + + if( tmpPlayer.getName() == name ) + { + deletePlayer = tmpPlayer; + break; + } + } + + int id = deletePlayer.getId(); + + g_database.execute( "DELETE FROM charabase WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM characlass WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charadetail WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charaglobalitem WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charainfoblacklist WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charainfofriendlist WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charainfolinkshell WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charainfosearch WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charaitemcrystal WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charaiteminventory WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charaitemgearset WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); + g_database.execute( "DELETE FROM charaquest WHERE CharacterId LIKE '" + std::to_string( id ) + "';" ); +} + +std::vector Core::Network::SapphireAPI::getCharList( uint32_t accountId ) +{ + + std::vector charList; + + boost::shared_ptr pQR = g_database.query( "SELECT CharacterId, ContentId FROM charabase WHERE AccountId = " + std::to_string( accountId ) + ";" ); + + if( !pQR ) + { + // no chars found for the account + return charList; + } + + do + { + Core::PlayerMinimal player; + + Core::Db::Field *field = pQR->fetch(); + + uint32_t charId = field[0].getInt32(); + + player.load( charId ); + + charList.push_back( player ); + + } while( pQR->nextRow() ); + + return charList; +} + +bool Core::Network::SapphireAPI::checkNameTaken( std::string name ) +{ + std::string query = "SELECT * FROM charabase WHERE Name = '" + name + "';"; + + auto pQR = g_database.query( query ); + + if( !pQR ) + return false; + else + return true; +} + +uint32_t Core::Network::SapphireAPI::getNextCharId() +{ + int charId = 0; + + boost::shared_ptr pQR = g_database.query( "SELECT MAX(CharacterId) FROM charabase" ); + + if( !pQR ) + { + return 0x00200001; + } + + charId = pQR->fetch()[0].getUInt32() + 1; + if( charId < 0x00200001 ) + { + return 0x00200001; + } + + return charId; +} + +uint64_t Core::Network::SapphireAPI::getNextContentId() +{ + uint64_t contentId = 0; + + boost::shared_ptr pQR = g_database.query( "SELECT MAX(ContentId) FROM charabase" ); + + if( !pQR ) + { + return 0x0040000001000001; + } + + contentId = pQR->fetch()[0].getUInt64() + 1; + if( contentId < 0x0040000001000001 ) + { + return 0x0040000001000001; + } + + + return contentId; +} + +int Core::Network::SapphireAPI::checkSession( const std::string& sId ) +{ + auto it = m_sessionMap.find( sId ); + + if( it == m_sessionMap.end() ) + return -1; + + return it->second->getAccountId(); +} + + +bool Core::Network::SapphireAPI::removeSession( const std::string& sId ) +{ + auto it = m_sessionMap.find( sId ); + + if( it != m_sessionMap.end() ) + m_sessionMap.erase( sId ); + + return true; +} \ No newline at end of file diff --git a/src/servers/Server_REST/SapphireAPI.h b/src/servers/Server_REST/SapphireAPI.h new file mode 100644 index 00000000..abb48f72 --- /dev/null +++ b/src/servers/Server_REST/SapphireAPI.h @@ -0,0 +1,52 @@ +#ifndef _SAPPHIREAPI_H_ +#define _SAPPHIREAPI_H_ + +#include +#include +#include +#include + +#include "PlayerMinimal.h" + +namespace Core +{ + class Session; + + namespace Network + { + + class SapphireAPI + { + public: + SapphireAPI(); + ~SapphireAPI(); + + typedef std::map< std::string, boost::shared_ptr< Session > > SessionMap; + + bool login( const std::string& username, const std::string& pass, std::string& sId ); + + bool createAccount( const std::string& username, const std::string& pass, std::string& sId ); + + int createCharacter( const int& accountId, const std::string& name, const std::string& infoJson ); + + void deleteCharacter( std::string name, uint32_t accountId ); + + std::vector getCharList( uint32_t accountId ); + + bool checkNameTaken( std::string name ); + + uint32_t getNextCharId(); + + uint64_t getNextContentId(); + + int checkSession( const std::string& sId ); + + bool removeSession( const std::string& sId ); + + SessionMap m_sessionMap; + + }; + } +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_REST/Session.cpp b/src/servers/Server_REST/Session.cpp new file mode 100644 index 00000000..6cd54ff3 --- /dev/null +++ b/src/servers/Server_REST/Session.cpp @@ -0,0 +1,37 @@ +#include "Session.h" +namespace Core { + Session::Session( void ) + { + + } + + Session::~Session( void ) + { + + } + + uint32_t Session::getIp() const + { + return m_ip; + } + + void Session::setSessionId( uint8_t * sessionId ) + { + memcpy( m_sessionId, sessionId, 56 ); + } + + void Session::setIp( uint32_t ip ) + { + m_ip = ip; + } + + uint32_t Session::getAccountId() const + { + return m_accountId; + } + + void Session::setAccountId( uint32_t id ) + { + m_accountId = id; + } +} diff --git a/src/servers/Server_REST/Session.h b/src/servers/Server_REST/Session.h new file mode 100644 index 00000000..a2b98116 --- /dev/null +++ b/src/servers/Server_REST/Session.h @@ -0,0 +1,40 @@ +#ifndef _SESSION_H_ +#define _SESSION_H_ + +#include +#include +#include + +namespace Core { + + class Session + { + + private: + uint32_t m_ip; + uint32_t m_accountId; + + uint8_t m_sessionId[56]; + + public: + + std::string newCharName; + + Session( void ); + ~Session( void ); + + uint32_t getIp() const; + + void setSessionId( uint8_t * sessionId ); + + void setIp( uint32_t ip ); + + uint32_t getAccountId() const; + + void setAccountId( uint32_t id ); + + }; + +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_REST/client_http.hpp b/src/servers/Server_REST/client_http.hpp new file mode 100644 index 00000000..077cb0fe --- /dev/null +++ b/src/servers/Server_REST/client_http.hpp @@ -0,0 +1,403 @@ +#ifndef CLIENT_HTTP_HPP +#define CLIENT_HTTP_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace SimpleWeb { + template + class Client; + + template + class ClientBase { + public: + virtual ~ClientBase() {} + + class Response { + friend class ClientBase; + friend class Client; + public: + std::string http_version, status_code; + + std::istream content; + + std::unordered_multimap header; + + private: + boost::asio::streambuf content_buffer; + + Response(): content(&content_buffer) {} + }; + + class Config { + friend class ClientBase; + private: + Config() {} + public: + /// Set timeout on requests in seconds. Default value: 0 (no timeout). + size_t timeout=0; + /// Set proxy server (server:port) + std::string proxy_server; + }; + + /// Set before calling request + Config config; + + std::shared_ptr request(const std::string& request_type, const std::string& path="/", boost::string_ref content="", + const std::map& header=std::map()) { + auto corrected_path=path; + if(corrected_path=="") + corrected_path="/"; + if(!config.proxy_server.empty() && std::is_same::value) + corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; + + boost::asio::streambuf write_buffer; + std::ostream write_stream(&write_buffer); + write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; + write_stream << "Host: " << host << "\r\n"; + for(auto& h: header) { + write_stream << h.first << ": " << h.second << "\r\n"; + } + if(content.size()>0) + write_stream << "Content-Length: " << content.size() << "\r\n"; + write_stream << "\r\n"; + + connect(); + + auto timer=get_timeout_timer(); + boost::asio::async_write(*socket, write_buffer, + [this, &content, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(!ec) { + if(!content.empty()) { + auto timer=get_timeout_timer(); + boost::asio::async_write(*socket, boost::asio::buffer(content.data(), content.size()), + [this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(ec) { + std::lock_guard lock(socket_mutex); + this->socket=nullptr; + throw boost::system::system_error(ec); + } + }); + } + } + else { + std::lock_guard lock(socket_mutex); + socket=nullptr; + throw boost::system::system_error(ec); + } + }); + io_service.reset(); + io_service.run(); + + return request_read(); + } + + std::shared_ptr request(const std::string& request_type, const std::string& path, std::iostream& content, + const std::map& header=std::map()) { + auto corrected_path=path; + if(corrected_path=="") + corrected_path="/"; + if(!config.proxy_server.empty() && std::is_same::value) + corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; + + content.seekp(0, std::ios::end); + auto content_length=content.tellp(); + content.seekp(0, std::ios::beg); + + boost::asio::streambuf write_buffer; + std::ostream write_stream(&write_buffer); + write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; + write_stream << "Host: " << host << "\r\n"; + for(auto& h: header) { + write_stream << h.first << ": " << h.second << "\r\n"; + } + if(content_length>0) + write_stream << "Content-Length: " << content_length << "\r\n"; + write_stream << "\r\n"; + if(content_length>0) + write_stream << content.rdbuf(); + + connect(); + + auto timer=get_timeout_timer(); + boost::asio::async_write(*socket, write_buffer, + [this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(ec) { + std::lock_guard lock(socket_mutex); + socket=nullptr; + throw boost::system::system_error(ec); + } + }); + io_service.reset(); + io_service.run(); + + return request_read(); + } + + void close() { + std::lock_guard lock(socket_mutex); + if(socket) { + boost::system::error_code ec; + socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(); + } + } + + protected: + boost::asio::io_service io_service; + boost::asio::ip::tcp::resolver resolver; + + std::unique_ptr socket; + std::mutex socket_mutex; + + std::string host; + unsigned short port; + + ClientBase(const std::string& host_port, unsigned short default_port) : resolver(io_service) { + auto parsed_host_port=parse_host_port(host_port, default_port); + host=parsed_host_port.first; + port=parsed_host_port.second; + } + + std::pair parse_host_port(const std::string &host_port, unsigned short default_port) { + std::pair parsed_host_port; + size_t host_end=host_port.find(':'); + if(host_end==std::string::npos) { + parsed_host_port.first=host_port; + parsed_host_port.second=default_port; + } + else { + parsed_host_port.first=host_port.substr(0, host_end); + parsed_host_port.second=static_cast(stoul(host_port.substr(host_end+1))); + } + return parsed_host_port; + } + + virtual void connect()=0; + + std::shared_ptr get_timeout_timer() { + if(config.timeout==0) + return nullptr; + + auto timer=std::make_shared(io_service); + timer->expires_from_now(boost::posix_time::seconds(config.timeout)); + timer->async_wait([this](const boost::system::error_code& ec) { + if(!ec) { + close(); + } + }); + return timer; + } + + void parse_response_header(const std::shared_ptr &response) const { + std::string line; + getline(response->content, line); + size_t version_end=line.find(' '); + if(version_end!=std::string::npos) { + if(5http_version=line.substr(5, version_end-5); + if((version_end+1)status_code=line.substr(version_end+1, line.size()-(version_end+1)-1); + + getline(response->content, line); + size_t param_end; + while((param_end=line.find(':'))!=std::string::npos) { + size_t value_start=param_end+1; + if((value_start)header.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1))); + } + + getline(response->content, line); + } + } + } + + std::shared_ptr request_read() { + std::shared_ptr response(new Response()); + + boost::asio::streambuf chunked_streambuf; + + auto timer=get_timeout_timer(); + boost::asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n", + [this, &response, &chunked_streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) { + if(timer) + timer->cancel(); + if(!ec) { + size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred; + + parse_response_header(response); + + auto header_it=response->header.find("Content-Length"); + if(header_it!=response->header.end()) { + auto content_length=stoull(header_it->second); + if(content_length>num_additional_bytes) { + auto timer=get_timeout_timer(); + boost::asio::async_read(*socket, response->content_buffer, + boost::asio::transfer_exactly(content_length-num_additional_bytes), + [this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(ec) { + std::lock_guard lock(socket_mutex); + this->socket=nullptr; + throw boost::system::system_error(ec); + } + }); + } + } + else if((header_it=response->header.find("Transfer-Encoding"))!=response->header.end() && header_it->second=="chunked") { + request_read_chunked(response, chunked_streambuf); + } + } + else { + std::lock_guard lock(socket_mutex); + socket=nullptr; + throw boost::system::system_error(ec); + } + }); + io_service.reset(); + io_service.run(); + + return response; + } + + void request_read_chunked(const std::shared_ptr &response, boost::asio::streambuf &streambuf) { + auto timer=get_timeout_timer(); + boost::asio::async_read_until(*socket, response->content_buffer, "\r\n", + [this, &response, &streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) { + if(timer) + timer->cancel(); + if(!ec) { + std::string line; + getline(response->content, line); + bytes_transferred-=line.size()+1; + line.pop_back(); + std::streamsize length=stol(line, 0, 16); + + auto num_additional_bytes=static_cast(response->content_buffer.size()-bytes_transferred); + + auto post_process=[this, &response, &streambuf, length] { + std::ostream stream(&streambuf); + if(length>0) { + std::vector buffer(static_cast(length)); + response->content.read(&buffer[0], length); + stream.write(&buffer[0], length); + } + + //Remove "\r\n" + response->content.get(); + response->content.get(); + + if(length>0) + request_read_chunked(response, streambuf); + else { + std::ostream response_stream(&response->content_buffer); + response_stream << stream.rdbuf(); + } + }; + + if((2+length)>num_additional_bytes) { + auto timer=get_timeout_timer(); + boost::asio::async_read(*socket, response->content_buffer, + boost::asio::transfer_exactly(2+length-num_additional_bytes), + [this, post_process, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(!ec) { + post_process(); + } + else { + std::lock_guard lock(socket_mutex); + this->socket=nullptr; + throw boost::system::system_error(ec); + } + }); + } + else + post_process(); + } + else { + std::lock_guard lock(socket_mutex); + socket=nullptr; + throw boost::system::system_error(ec); + } + }); + } + }; + + template + class Client : public ClientBase {}; + + typedef boost::asio::ip::tcp::socket HTTP; + + template<> + class Client : public ClientBase { + public: + Client(const std::string& server_port_path) : ClientBase::ClientBase(server_port_path, 80) {} + + protected: + void connect() { + if(!socket || !socket->is_open()) { + std::unique_ptr query; + if(config.proxy_server.empty()) + query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(host, std::to_string(port))); + else { + auto proxy_host_port=parse_host_port(config.proxy_server, 8080); + query=std::unique_ptr(new boost::asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); + } + resolver.async_resolve(*query, [this](const boost::system::error_code &ec, + boost::asio::ip::tcp::resolver::iterator it){ + if(!ec) { + { + std::lock_guard lock(socket_mutex); + socket=std::unique_ptr(new HTTP(io_service)); + } + + auto timer=get_timeout_timer(); + boost::asio::async_connect(*socket, it, [this, timer] + (const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/){ + if(timer) + timer->cancel(); + if(!ec) { + boost::asio::ip::tcp::no_delay option(true); + this->socket->set_option(option); + } + else { + std::lock_guard lock(socket_mutex); + this->socket=nullptr; + throw boost::system::system_error(ec); + } + }); + } + else { + std::lock_guard lock(socket_mutex); + socket=nullptr; + throw boost::system::system_error(ec); + } + }); + io_service.reset(); + io_service.run(); + } + } + }; +} + +#endif /* CLIENT_HTTP_HPP */ diff --git a/src/servers/Server_REST/main.cpp b/src/servers/Server_REST/main.cpp new file mode 100644 index 00000000..fcbb036e --- /dev/null +++ b/src/servers/Server_REST/main.cpp @@ -0,0 +1,603 @@ +#include "server_http.hpp" +#include "client_http.hpp" + +#define BOOST_SPIRIT_THREADSAFE +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +//Added for the default_resource example +#include +#include +#include +#include +#include + +#include "Forwards.h" +#include "SapphireAPI.h" + + +Core::Logger g_log; +Core::Db::Database g_database; +Core::Data::ExdData g_exdData; +Core::Network::SapphireAPI g_sapphireAPI; + +using namespace std; +using namespace boost::property_tree; + +typedef SimpleWeb::Server HttpServer; +typedef SimpleWeb::Client HttpClient; + +//Added for the default_resource example +void default_resource_send( const HttpServer &server, const shared_ptr &response, + const shared_ptr &ifs ); + +void print_request_info( shared_ptr request ) { + g_log.info( "Request from " + request->remote_endpoint_address + " (" + request->path + ")" ); +} + +int main() +{ + g_log.setLogPath( "SapphireAPI" ); + g_log.init(); + + g_log.info( "===========================================================" ); + g_log.info( "Sapphire API Server " ); + g_log.info( "Version: 0.0.1" ); + g_log.info( "Compiled: " __DATE__ " " __TIME__ ); + g_log.info( "===========================================================" ); + + auto m_pConfig = new Core::XMLConfig(); + + g_log.info( "Loading config settings_rest.xml" ); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( !g_exdData.init( m_pConfig->getValue< std::string >( "Settings.General.DataPath" ) ) ) + { + g_log.fatal( "Error setting up EXD data " ); + return 1; + } + + Core::Db::DatabaseParams params; + params.bufferSize = 16384; + params.connectionCount = 3; + params.databaseName = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" ); + params.hostname = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Host", "127.0.0.1" ); + params.password = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Pass", "" ); + params.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 ); + params.username = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" ); + + if( !g_database.initialize( params ) ) + { + std::this_thread::sleep_for( std::chrono::milliseconds( 5000 ) ); + return -1; + } + + g_log.info( "Database: Connected to " + params.hostname + ":" + std::to_string( params.port ) ); + g_exdData.loadZoneInfo(); + + HttpServer server; + server.config.port = stoi( m_pConfig->getValue< std::string >( "Settings.General.HttpPort", "80" ) ); + g_log.info( "Starting REST server at port " + m_pConfig->getValue< std::string >( "Settings.General.HttpPort", "80" ) + "..." ); + + server.resource["^/ZoneName/([0-9]+)$"]["GET"] = [&server]( shared_ptr response, shared_ptr request ) { + string number = request->path_match[1]; + auto it = g_exdData.m_zoneInfoMap.find( atoi( number.c_str() ) ); + std::string responseStr = "Not found!"; + if( it != g_exdData.m_zoneInfoMap.end() ) + { + responseStr = it->second.zone_name + ", " + it->second.zone_str; + } + *response << "HTTP/1.1 200 OK\r\nContent-Length: " << responseStr.length() << "\r\n\r\n" << responseStr; + }; + + + /* Create account */ + server.resource["^/sapphire-api/lobby/createAccount"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + std::string responseStr = "HTTP/1.1 400\r\n\r\n"; + try + { + + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string pass = pt.get( "pass" ); + std::string user = pt.get( "username" ); + + std::string sId; + if( g_sapphireAPI.createAccount( user, pass, sId ) ) + { + auto m_pConfig = new Core::XMLConfig(); + + g_log.info( "Loading config settings_rest.xml" ); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + std::string json_string = "{\"sId\":\"" + sId + "\", \"lobbyHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.LobbyHost" ) + "\", \"frontierHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.FrontierHost" ) + "\"}"; + *response << "HTTP/1.1 200 OK\r\n " + << "Content-Type: application/json\r\n" + << "Content-Length: " << json_string.length() << "\r\n\r\n" + << json_string; + } + else + *response << "HTTP/1.1 400\r\n\r\n"; + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + *response << "HTTP/1.1 200 OK\r\nContent-Length: " << responseStr.length() << "\r\nContent-Type: text/xml\r\n\r\n" << responseStr; + }; + + + server.resource["^/sapphire-api/lobby/login"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string pass = pt.get( "pass" ); + std::string user = pt.get( "username" ); + + std::string sId; + + if( g_sapphireAPI.login( user, pass, sId ) ) + { + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + std::string json_string = "{\"sId\":\"" + sId + "\", \"lobbyHost\":\"" + m_pConfig->getValue< std::string >("Settings.General.LobbyHost") + "\", \"frontierHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.FrontierHost" ) + "\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + *response << "HTTP/1.1 400\r\n\r\n"; + + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/deleteCharacter"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string sId = pt.get( "sId" ); + std::string secret = pt.get( "secret" ); + std::string name = pt.get( "name" ); + + int accountId = g_sapphireAPI.checkSession( sId ); + + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + g_sapphireAPI.deleteCharacter( name, accountId ); + std::string json_string = "{\"result\":\"success\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/createCharacter"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string sId = pt.get( "sId" ); + std::string secret = pt.get( "secret" ); + std::string name = pt.get( "name" ); + std::string infoJson = pt.get( "infoJson" ); + + std::string finalJson = Core::Util::base64_decode( infoJson ); + + int result = g_sapphireAPI.checkSession( sId ); + + if( result != -1 ) + { + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + int charId = g_sapphireAPI.createCharacter( result, name, finalJson ); + + std::string json_string = "{\"result\":\"" + std::to_string( charId ) + "\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + else + { + std::string json_string = "{\"result\":\"invalid\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/checkNameTaken"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string name = pt.get( "name" ); + std::string secret = pt.get( "secret" ); + + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + std::string json_string; + if( !g_sapphireAPI.checkNameTaken( name ) ) + json_string = "{\"result\":\"false\"}"; + else + json_string = "{\"result\":\"true\"}"; + + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/checkSession"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string sId = pt.get( "sId" ); + std::string secret = pt.get( "secret" ); + + int result = g_sapphireAPI.checkSession( sId ); + + if( result != -1 ) + { + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + std::string json_string = "{\"result\":\"" + std::to_string( result ) + "\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + else + { + std::string json_string = "{\"result\":\"invalid\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/getNextCharId"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string secret = pt.get( "secret" ); + + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + std::string json_string = "{\"result\":\"" + std::to_string( g_sapphireAPI.getNextCharId() ) + "\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/getNextContentId"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string secret = pt.get( "secret" ); + + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + std::string json_string = "{\"result\":\"" + std::to_string( g_sapphireAPI.getNextContentId() ) + "\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + server.resource["^/sapphire-api/lobby/getCharacterList"]["POST"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); + + std::string sId = pt.get( "sId" ); + std::string secret = pt.get( "secret" ); + + int result = g_sapphireAPI.checkSession( sId ); + + if( result != -1 ) + { + auto m_pConfig = new Core::XMLConfig(); + + if( !m_pConfig->loadConfig( "config/settings_rest.xml" ) ) + { + g_log.fatal( "Error loading config settings_rest.xml" ); + return 1; + } + + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + auto charList = g_sapphireAPI.getCharList( result ); + using boost::property_tree::ptree; + ptree pt; + ptree char_tree; + + for( auto entry : charList ) + { + ptree tree_entry; + tree_entry.put( "name", std::string( entry.getName() ) ); + tree_entry.put( "charId", std::to_string( entry.getId() ) ); + tree_entry.put( "contentId", std::to_string( entry.getContentId() ) ); + tree_entry.put( "infoJson", std::string( entry.getInfoJson() ) ); + char_tree.push_back( std::make_pair( "", tree_entry ) ); + } + + pt.add_child( "charArray", char_tree ); + pt.put( "result", "success" ); + std::ostringstream oss; + write_json( oss, pt ); + std::string responseStr = oss.str(); + *response << "HTTP/1.1 200\r\nContent-Length: " << responseStr.length() << "\r\n\r\n" << responseStr; + } + } + else + { + std::string json_string = "{\"result\":\"invalid\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } + + }; + + //Default GET-example. If no other matches, this anonymous function will be called. + //Will respond with content in the web/-directory, and its subdirectories. + //Default file: index.html + //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server + server.default_resource["GET"] = [&server]( shared_ptr response, shared_ptr request ) { + print_request_info( request ); + + try + { + auto web_root_path = boost::filesystem::canonical( "web" ); + auto path = boost::filesystem::canonical( web_root_path / request->path ); + //Check if path is within web_root_path + if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) || + !equal( web_root_path.begin(), web_root_path.end(), path.begin() ) ) + throw invalid_argument( "path must be within root path" ); + if( boost::filesystem::is_directory( path ) ) + path /= "index.html"; + if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) ) + throw invalid_argument( "file does not exist" ); + + std::string cache_control, etag; + + // Uncomment the following line to enable Cache-Control + // cache_control="Cache-Control: max-age=86400\r\n"; + + auto ifs = make_shared(); + ifs->open( path.string(), ifstream::in | ios::binary | ios::ate ); + + if( *ifs ) + { + auto length = ifs->tellg(); + ifs->seekg( 0, ios::beg ); + + *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; + default_resource_send( server, response, ifs ); + } + else + throw invalid_argument( "could not read file" ); + } + catch( const exception &e ) + { + string content = "Path not found: " + request->path; + *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content; + } + }; + + thread server_thread( [&server]() { + //Start server + server.start(); + } ); + + //Wait for server to start so that the client can connect + this_thread::sleep_for( chrono::seconds( 1 ) ); + + server_thread.join(); + g_log.info( "Started REST server at port " + std::to_string( server.config.port ) ); + + return 0; +} + +void default_resource_send( const HttpServer &server, const shared_ptr &response, + const shared_ptr &ifs ) +{ + //read and send 128 KB at a time + static vector buffer( 131072 ); // Safe when server is running on one thread + streamsize read_length; + if( ( read_length = ifs->read( &buffer[0], buffer.size() ).gcount() ) > 0 ) + { + response->write( &buffer[0], read_length ); + if( read_length == static_cast< streamsize >( buffer.size() ) ) + { + server.send( response, [&server, response, ifs]( const boost::system::error_code &ec ) { + if( !ec ) + default_resource_send( server, response, ifs ); + else + cerr << "Connection interrupted" << endl; + } ); + } + } +} diff --git a/src/servers/Server_REST/server_http.hpp b/src/servers/Server_REST/server_http.hpp new file mode 100644 index 00000000..2651eaba --- /dev/null +++ b/src/servers/Server_REST/server_http.hpp @@ -0,0 +1,462 @@ +#ifndef SERVER_HTTP_HPP +#define SERVER_HTTP_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH +#define CASE_INSENSITIVE_EQUALS_AND_HASH +//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html +class case_insensitive_equals { +public: + bool operator()(const std::string &key1, const std::string &key2) const { + return boost::algorithm::iequals(key1, key2); + } +}; +class case_insensitive_hash { +public: + size_t operator()(const std::string &key) const { + std::size_t seed=0; + for(auto &c: key) + boost::hash_combine(seed, std::tolower(c)); + return seed; + } +}; +#endif + +// Late 2017 TODO: remove the following checks and always use std::regex +#ifdef USE_BOOST_REGEX +#include +#define REGEX_NS boost +#else +#include +#define REGEX_NS std +#endif + +// TODO when switching to c++14, use [[deprecated]] instead +#ifndef DEPRECATED +#ifdef __GNUC__ +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#define DEPRECATED +#endif +#endif + +namespace SimpleWeb { + template + class Server; + + template + class ServerBase { + public: + virtual ~ServerBase() {} + + class Response : public std::ostream { + friend class ServerBase; + + boost::asio::streambuf streambuf; + + std::shared_ptr socket; + + Response(const std::shared_ptr &socket): std::ostream(&streambuf), socket(socket) {} + + public: + size_t size() { + return streambuf.size(); + } + }; + + class Content : public std::istream { + friend class ServerBase; + public: + size_t size() { + return streambuf.size(); + } + std::string string() { + std::stringstream ss; + ss << rdbuf(); + return ss.str(); + } + private: + boost::asio::streambuf &streambuf; + Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {} + }; + + class Request { + friend class ServerBase; + friend class Server; + public: + std::string method, path, http_version; + + Content content; + + std::unordered_multimap header; + + REGEX_NS::smatch path_match; + + std::string remote_endpoint_address; + unsigned short remote_endpoint_port; + + private: + Request(const socket_type &socket): content(streambuf) { + try { + remote_endpoint_address=socket.lowest_layer().remote_endpoint().address().to_string(); + remote_endpoint_port=socket.lowest_layer().remote_endpoint().port(); + } + catch(...) {} + } + + boost::asio::streambuf streambuf; + }; + + class Config { + friend class ServerBase; + + Config(unsigned short port): port(port) {} + public: + /// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS. + unsigned short port; + /// Number of threads that the server will use when start() is called. Defaults to 1 thread. + size_t thread_pool_size=1; + /// Timeout on request handling. Defaults to 5 seconds. + size_t timeout_request=5; + /// Timeout on content handling. Defaults to 300 seconds. + size_t timeout_content=300; + /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation. + /// If empty, the address will be any address. + std::string address; + /// Set to false to avoid binding the socket to an address that is already in use. Defaults to true. + bool reuse_address=true; + }; + ///Set before calling start(). + Config config; + + private: + class regex_orderable : public REGEX_NS::regex { + std::string str; + public: + regex_orderable(const char *regex_cstr) : REGEX_NS::regex(regex_cstr), str(regex_cstr) {} + regex_orderable(const std::string ®ex_str) : REGEX_NS::regex(regex_str), str(regex_str) {} + bool operator<(const regex_orderable &rhs) const { + return str::Response>, std::shared_ptr::Request>)> > > resource; + + std::map::Response>, std::shared_ptr::Request>)> > default_resource; + + std::function::Request>, const boost::system::error_code&)> on_error; + + std::function socket, std::shared_ptr::Request>)> on_upgrade; + + virtual void start() { + if(!io_service) + io_service=std::make_shared(); + + if(io_service->stopped()) + io_service->reset(); + + boost::asio::ip::tcp::endpoint endpoint; + if(config.address.size()>0) + endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port); + else + endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port); + + if(!acceptor) + acceptor=std::unique_ptr(new boost::asio::ip::tcp::acceptor(*io_service)); + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address)); + acceptor->bind(endpoint); + acceptor->listen(); + + accept(); + + //If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling + threads.clear(); + for(size_t c=1;crun(); + }); + } + + //Main thread + if(config.thread_pool_size>0) + io_service->run(); + + //Wait for the rest of the threads, if any, to finish as well + for(auto& t: threads) { + t.join(); + } + } + + void stop() { + acceptor->close(); + if(config.thread_pool_size>0) + io_service->stop(); + } + + ///Use this function if you need to recursively send parts of a longer message + void send(const std::shared_ptr &response, const std::function& callback=nullptr) const { + boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { + if(callback) + callback(ec); + }); + } + + /// If you have your own boost::asio::io_service, store its pointer here before running start(). + /// You might also want to set config.thread_pool_size to 0. + std::shared_ptr io_service; + protected: + std::unique_ptr acceptor; + std::vector threads; + + ServerBase(unsigned short port) : config(port) {} + + virtual void accept()=0; + + std::shared_ptr get_timeout_timer(const std::shared_ptr &socket, long seconds) { + if(seconds==0) + return nullptr; + + auto timer=std::make_shared(*io_service); + timer->expires_from_now(boost::posix_time::seconds(seconds)); + timer->async_wait([socket](const boost::system::error_code& ec){ + if(!ec) { + boost::system::error_code ec; + socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(); + } + }); + return timer; + } + + void read_request_and_content(const std::shared_ptr &socket) { + //Create new streambuf (Request::streambuf) for async_read_until() + //shared_ptr is used to pass temporary objects to the asynchronous functions + std::shared_ptr request(new Request(*socket)); + + //Set timeout on the following boost::asio::async-read or write function + auto timer=this->get_timeout_timer(socket, config.timeout_request); + + boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", + [this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) { + if(timer) + timer->cancel(); + if(!ec) { + //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs: + //"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter" + //The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the + //streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content). + size_t num_additional_bytes=request->streambuf.size()-bytes_transferred; + + if(!this->parse_request(request)) + return; + + //If content, read that as well + auto it=request->header.find("Content-Length"); + if(it!=request->header.end()) { + unsigned long long content_length; + try { + content_length=stoull(it->second); + } + catch(const std::exception &e) { + if(on_error) + on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category())); + return; + } + if(content_length>num_additional_bytes) { + //Set timeout on the following boost::asio::async-read or write function + auto timer=this->get_timeout_timer(socket, config.timeout_content); + boost::asio::async_read(*socket, request->streambuf, + boost::asio::transfer_exactly(content_length-num_additional_bytes), + [this, socket, request, timer] + (const boost::system::error_code& ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(!ec) + this->find_resource(socket, request); + else if(on_error) + on_error(request, ec); + }); + } + else + this->find_resource(socket, request); + } + else + this->find_resource(socket, request); + } + else if(on_error) + on_error(request, ec); + }); + } + + bool parse_request(const std::shared_ptr &request) const { + std::string line; + getline(request->content, line); + size_t method_end; + if((method_end=line.find(' '))!=std::string::npos) { + size_t path_end; + if((path_end=line.find(' ', method_end+1))!=std::string::npos) { + request->method=line.substr(0, method_end); + request->path=line.substr(method_end+1, path_end-method_end-1); + + size_t protocol_end; + if((protocol_end=line.find('/', path_end+1))!=std::string::npos) { + if(line.compare(path_end+1, protocol_end-path_end-1, "HTTP")!=0) + return false; + request->http_version=line.substr(protocol_end+1, line.size()-protocol_end-2); + } + else + return false; + + getline(request->content, line); + size_t param_end; + while((param_end=line.find(':'))!=std::string::npos) { + size_t value_start=param_end+1; + if((value_start)header.emplace(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)); + } + + getline(request->content, line); + } + } + else + return false; + } + else + return false; + return true; + } + + void find_resource(const std::shared_ptr &socket, const std::shared_ptr &request) { + //Upgrade connection + if(on_upgrade) { + auto it=request->header.find("Upgrade"); + if(it!=request->header.end()) { + on_upgrade(socket, request); + return; + } + } + //Find path- and method-match, and call write_response + for(auto ®ex_method: resource) { + auto it=regex_method.second.find(request->method); + if(it!=regex_method.second.end()) { + REGEX_NS::smatch sm_res; + if(REGEX_NS::regex_match(request->path, sm_res, regex_method.first)) { + request->path_match=std::move(sm_res); + write_response(socket, request, it->second); + return; + } + } + } + auto it=default_resource.find(request->method); + if(it!=default_resource.end()) { + write_response(socket, request, it->second); + } + } + + void write_response(const std::shared_ptr &socket, const std::shared_ptr &request, + std::function::Response>, + std::shared_ptr::Request>)>& resource_function) { + //Set timeout on the following boost::asio::async-read or write function + auto timer=this->get_timeout_timer(socket, config.timeout_content); + + auto response=std::shared_ptr(new Response(socket), [this, request, timer](Response *response_ptr) { + auto response=std::shared_ptr(response_ptr); + this->send(response, [this, response, request, timer](const boost::system::error_code& ec) { + if(timer) + timer->cancel(); + if(!ec) { + float http_version; + try { + http_version=stof(request->http_version); + } + catch(const std::exception &e){ + if(on_error) + on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category())); + return; + } + + auto range=request->header.equal_range("Connection"); + for(auto it=range.first;it!=range.second;it++) { + if(boost::iequals(it->second, "close")) + return; + } + if(http_version>1.05) + this->read_request_and_content(response->socket); + } + else if(on_error) + on_error(request, ec); + }); + }); + + try { + resource_function(response, request); + } + catch(const std::exception &e) { + if(on_error) + on_error(request, boost::system::error_code(boost::system::errc::operation_canceled, boost::system::generic_category())); + return; + } + } + }; + + template + class Server : public ServerBase {}; + + typedef boost::asio::ip::tcp::socket HTTP; + + template<> + class Server : public ServerBase { + public: + DEPRECATED Server(unsigned short port, size_t thread_pool_size=1, long timeout_request=5, long timeout_content=300) : + Server() { + config.port=port; + config.thread_pool_size=thread_pool_size; + config.timeout_request=timeout_request; + config.timeout_content=timeout_content; + } + + Server() : ServerBase::ServerBase(80) {} + + protected: + void accept() { + //Create new socket for this connection + //Shared_ptr is used to pass temporary objects to the asynchronous functions + auto socket=std::make_shared(*io_service); + + acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec){ + //Immediately start accepting a new connection (if io_service hasn't been stopped) + if (ec != boost::asio::error::operation_aborted) + accept(); + + if(!ec) { + boost::asio::ip::tcp::no_delay option(true); + socket->set_option(option); + + this->read_request_and_content(socket); + } + else if(on_error) + on_error(std::shared_ptr(new Request(*socket)), ec); + }); + } + }; +} +#endif /* SERVER_HTTP_HPP */ diff --git a/src/servers/Server_Zone/Action.cpp b/src/servers/Server_Zone/Action.cpp new file mode 100644 index 00000000..2cdf945b --- /dev/null +++ b/src/servers/Server_Zone/Action.cpp @@ -0,0 +1,85 @@ +#include "Action.h" + +#include + + +Core::Action::Action::Action() +{ +} + +Core::Action::Action::~Action() +{ + +} + +uint32_t Core::Action::Action::getId() const +{ + return m_id; +} + +Core::Common::ActionType Core::Action::Action::getActionType() const +{ + return m_actionType; +} + +Core::Entity::ActorPtr Core::Action::Action::getTargetActor() const +{ + return m_pTarget; +} + +bool Core::Action::Action::isInterrupted() const +{ + return m_bInterrupt; +} + +void Core::Action::Action::setInterrupted() +{ + m_bInterrupt = true; +} + +uint64_t Core::Action::Action::getStartTime() const +{ + return m_startTime; +} + +void Core::Action::Action::setStartTime( uint64_t startTime ) +{ + m_startTime = startTime; +} + +uint32_t Core::Action::Action::getCastTime() const +{ + return m_castTime; +} + +void Core::Action::Action::setCastTime( uint32_t castTime ) +{ + m_castTime = castTime; +} + +Core::Entity::ActorPtr Core::Action::Action::getActionSource() const +{ + return m_pSource; +} + +bool Core::Action::Action::update() +{ + // action has not been started yet + if( m_startTime == 0 ) + return false; + + if( m_bInterrupt ) + { + onInterrupt(); + return true; + } + + uint64_t currTime = Util::getTimeMs(); + + if( ( currTime - m_startTime ) > m_castTime ) + { + onFinish(); + return true; + } + return false; +} diff --git a/src/servers/Server_Zone/Action.h b/src/servers/Server_Zone/Action.h new file mode 100644 index 00000000..8a004bf4 --- /dev/null +++ b/src/servers/Server_Zone/Action.h @@ -0,0 +1,60 @@ +#ifndef _ACTION_H_ +#define _ACTION_H_ + +#include +#include "Forwards.h" + +namespace Core { namespace Action { + + class Action + { + + public: + Action(); + virtual ~Action(); + + uint32_t getId() const; + + Common::ActionType getActionType() const; + + Entity::ActorPtr getTargetActor() const; + + bool isInterrupted() const; + + void setInterrupted(); + + uint64_t getStartTime() const; + + void setStartTime( uint64_t startTime ); + + uint32_t getCastTime() const; + + void setCastTime( uint32_t castTime ); + + Entity::ActorPtr getActionSource() const; + + virtual void onStart() {}; + virtual void onFinish() {}; + virtual void onInterrupt() {}; + + // update action, if returns true, action is done and has to be removed from the actor + virtual bool update(); + + protected: + uint32_t m_id; + Common::ActionType m_actionType; + + uint64_t m_startTime; + uint32_t m_castTime; + + Entity::ActorPtr m_pSource; + Entity::ActorPtr m_pTarget; + + bool m_bInterrupt; + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/ActionCast.cpp b/src/servers/Server_Zone/ActionCast.cpp new file mode 100644 index 00000000..0e1a37e1 --- /dev/null +++ b/src/servers/Server_Zone/ActionCast.cpp @@ -0,0 +1,107 @@ +#include "ActionCast.h" + +#include +#include +#include +#include +#include + +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "Player.h" + +using namespace Core::Common; +using namespace Core::Network; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +extern Core::Data::ExdData g_exdData; +extern Core::Logger g_log; + +Core::Action::ActionCast::ActionCast() +{ + m_actionType = Common::ActionType::Event; +} + +Core::Action::ActionCast::ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr pTarget, uint32_t actionId ) +{ + m_startTime = 0; + m_id = actionId; + m_actionType = ActionType::Spell; + m_castTime = g_exdData.m_actionInfoMap[actionId].cast_time; // TODO: Add security checks. + m_pSource = pActor; + m_pTarget = pTarget; + m_bInterrupt = false; +} + +Core::Action::ActionCast::~ActionCast() +{ + +} + +void Core::Action::ActionCast::onStart() +{ + if( !m_pSource ) + return; + + m_pSource->getAsPlayer()->sendDebug( "onStart()" ); + m_startTime = Util::getTimeMs(); + + GamePacketNew< FFXIVIpcActorCast > castPacket( getId() ); + + castPacket.data().action_id = m_id; + castPacket.data().unknown = 1; + castPacket.data().cast_time = m_castTime; + castPacket.data().target_id = m_pTarget->getId(); + + m_pSource->sendToInRangeSet( castPacket, true ); + m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + +} + +void Core::Action::ActionCast::onFinish() +{ + if( !m_pSource ) + return; + + auto pPlayer = m_pSource->getAsPlayer(); + pPlayer->sendDebug( "onFinish()" ); + + m_pTarget->onActionHostile( pPlayer->shared_from_this() ); + + pPlayer->unsetStateFlag( PlayerStateFlag::Casting ); + pPlayer->sendStateFlags(); + + GamePacketNew< FFXIVIpcEffect > effectPacket( pPlayer->getId() ); + effectPacket.data().targetId = m_pTarget->getId(); + effectPacket.data().actionAnimationId = m_id; + effectPacket.data().unknown_2 = 0; +// effectPacket.data().unknown_3 = 1; + effectPacket.data().actionTextId = m_id; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + effectPacket.data().effectTarget = m_pTarget->getId(); + effectPacket.data().effects[0].param1 = 30; + effectPacket.data().effects[0].unknown_1 = 3; + effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].unknown_3 = 7; + + pPlayer->sendToInRangeSet( effectPacket, true ); + m_pTarget->takeDamage( 30 ); +} + +void Core::Action::ActionCast::onInterrupt() +{ + if( !m_pSource ) + return; + + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + + auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, + 0x219, 0x04, m_id, 1 ); + m_pSource->sendToInRangeSet( control, true ); + +} diff --git a/src/servers/Server_Zone/ActionCast.h b/src/servers/Server_Zone/ActionCast.h new file mode 100644 index 00000000..ce69da18 --- /dev/null +++ b/src/servers/Server_Zone/ActionCast.h @@ -0,0 +1,28 @@ +#ifndef _ACTIONCAST_H_ +#define _ACTIONCAST_H_ + +#include "Forwards.h" +#include "Action.h" + +namespace Core { namespace Action { + + class ActionCast : public Action + { + private: + + public: + ActionCast(); + ~ActionCast(); + + ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr pTarget, uint32_t actionId ); + + void onStart() override; + void onFinish() override; + void onInterrupt() override; + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/ActionTeleport.cpp b/src/servers/Server_Zone/ActionTeleport.cpp new file mode 100644 index 00000000..1d5b0d8e --- /dev/null +++ b/src/servers/Server_Zone/ActionTeleport.cpp @@ -0,0 +1,114 @@ +#include "ActionTeleport.h" + +#include +#include +#include + +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "Player.h" + +using namespace Core::Common; +using namespace Core::Network; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +extern Core::Data::ExdData g_exdData; +extern Core::Logger g_log; + +Core::Action::ActionTeleport::ActionTeleport() +{ + m_actionType = Common::ActionType::Event; +} + +Core::Action::ActionTeleport::ActionTeleport( Entity::ActorPtr pActor, uint16_t targetZone, uint16_t cost ) +{ + m_startTime = 0; + m_id = 5; + m_actionType = ActionType::Teleport; + m_castTime = g_exdData.m_actionInfoMap[5].cast_time; // TODO: Add security checks. + m_pSource = pActor; + m_bInterrupt = false; + m_targetAetheryte = targetZone; + m_cost = cost; +} + +Core::Action::ActionTeleport::~ActionTeleport() +{ + +} + +void Core::Action::ActionTeleport::onStart() +{ + if( !m_pSource ) + return; + + m_startTime = Util::getTimeMs(); + + GamePacketNew< FFXIVIpcActorCast > castPacket( getId() ); + + castPacket.data().action_id = 5; + castPacket.data().unknown = 1; + castPacket.data().cast_time = 5.0f; + castPacket.data().target_id = m_pSource->getId(); + + m_pSource->sendToInRangeSet( castPacket, true ); + m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + +} + +void Core::Action::ActionTeleport::onFinish() +{ + if( !m_pSource ) + return; + + auto pPlayer = m_pSource->getAsPlayer(); + + // check we can finish teleporting + if( pPlayer->getCurrency( Inventory::CurrencyType::Gil ) < m_cost ) + { + onInterrupt(); + return; + } + + pPlayer->removeCurrency( Inventory::CurrencyType::Gil, m_cost ); + + pPlayer->unsetStateFlag( PlayerStateFlag::Casting ); + pPlayer->sendStateFlags(); + + // TODO: not sure if this ever gets sent + //auto control = Network::Packets::Server::ActorControlPacket142( m_pSource->getId(), Common::ActorControlType::TeleportDone ); + //m_pSource->sendToInRangeSet( control, false ); + + pPlayer->setZoningType( Common::ZoneingType::Teleport ); + + GamePacketNew< FFXIVIpcEffect > effectPacket( pPlayer->getId() ); + effectPacket.data().targetId = pPlayer->getId(); + effectPacket.data().actionAnimationId = 5; + //effectPacket.data().unknown_3 = 1; + effectPacket.data().actionTextId = 5; + effectPacket.data().unknown_5 = 1; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = static_cast< uint16_t >( 0x8000 * ((pPlayer->getRotation() + 3.1415926)) / 3.1415926 ); + effectPacket.data().effectTarget = pPlayer->getId(); + pPlayer->sendToInRangeSet( effectPacket, true ); + + pPlayer->teleport( m_targetAetheryte ); + + +} + +void Core::Action::ActionTeleport::onInterrupt() +{ + if( !m_pSource ) + return; + + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + m_pSource->getAsPlayer()->sendStateFlags(); + + auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, + 0x219, 0x04, m_id, 1 ); + m_pSource->sendToInRangeSet( control, true ); + +} diff --git a/src/servers/Server_Zone/ActionTeleport.h b/src/servers/Server_Zone/ActionTeleport.h new file mode 100644 index 00000000..e8d16b68 --- /dev/null +++ b/src/servers/Server_Zone/ActionTeleport.h @@ -0,0 +1,30 @@ +#ifndef _ACTIONTELEPORT_H_ +#define _ACTIONTELEPORT_H_ + +#include "Forwards.h" +#include "Action.h" + +namespace Core { namespace Action { + + class ActionTeleport : public Action + { + private: + uint16_t m_targetAetheryte; + uint16_t m_cost; + + public: + ActionTeleport(); + ~ActionTeleport(); + + ActionTeleport( Entity::ActorPtr pActor, uint16_t action, uint16_t cost ); + + void onStart() override; + void onFinish() override; + void onInterrupt() override; + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/Actor.cpp b/src/servers/Server_Zone/Actor.cpp new file mode 100644 index 00000000..82b0cb7a --- /dev/null +++ b/src/servers/Server_Zone/Actor.cpp @@ -0,0 +1,600 @@ +#include +#include +#include + +#include "Forwards.h" +#include "Action.h" +#include "Actor.h" +#include "Zone.h" +#include +#include "GameConnection.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "ActorControlPacket144.h" +#include "UpdateHpMpTpPacket.h" + +#include "StatusEffectContainer.h" +#include "ServerZone.h" +#include "Session.h" +#include "Player.h" + +extern Core::ServerZone g_serverZone; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Entity::Actor::Actor() +{ +} + +Core::Entity::Actor::~Actor() +{ +} + +/*! \return the id of the actor */ +uint32_t Core::Entity::Actor::getId() const +{ + return m_id; +} + +/*! \return the actors position object */ +Core::Common::FFXIVARR_POSITION3& Core::Entity::Actor::getPos() +{ + return m_pos; +} +/*! \return the actors name */ +std::string Core::Entity::Actor::getName() const +{ + return std::string( m_name ); +} + +/*! \return true if the actor is of type player */ +bool Core::Entity::Actor::isPlayer() const +{ + return ( m_type == ActorType::Player ? true : false ); +} + +/*! \return true if the actor is of type mob */ +bool Core::Entity::Actor::isMob() const +{ + return ( m_type == ActorType::BattleNpc ? true : false ); +} + +/*! \return list of actors currently in range */ +std::set< Core::Entity::ActorPtr > Core::Entity::Actor::getInRangeActors() const +{ + return m_inRangeActors; +} + +/*! \return current stance of the actors */ +Core::Entity::Actor::Stance Core::Entity::Actor::getStance() const +{ + return m_currentStance; +} + +/*! \return current HP */ +uint32_t Core::Entity::Actor::getHp() const +{ + return m_hp; +} + +/*! \return current MP */ +uint32_t Core::Entity::Actor::getMp() const +{ + return m_mp; +} + +/*! \return current TP */ +uint16_t Core::Entity::Actor::getTp() const +{ + return m_tp; +} + +/*! \return current GP */ +uint16_t Core::Entity::Actor::getGp() const +{ + return m_gp; +} + +/*! \return current class or job */ +Core::Common::ClassJob Core::Entity::Actor::getClass() const +{ + return m_class; +} + +/*! \return current class or job as int ( this feels pointless ) */ +uint8_t Core::Entity::Actor::getClassAsInt() const +{ + return static_cast< uint8_t >( m_class ); +} + +/*! \param ClassJob to set */ +void Core::Entity::Actor::setClass( Common::ClassJob classJob ) +{ + m_class = classJob; +} + +/*! \param Id of the target to set */ +void Core::Entity::Actor::setTargetId( uint64_t targetId ) +{ + m_targetId = targetId; +} + +/*! \return Id of the current target */ +uint64_t Core::Entity::Actor::getTargetId() const +{ + return m_targetId; +} + +/*! \return True if the actor is alive */ +bool Core::Entity::Actor::isAlive() const +{ + return ( m_hp > 0 ); +} + +/*! \return max hp for the actor */ +uint32_t Core::Entity::Actor::getMaxHp() const +{ + return m_baseStats.max_hp; +} + +/*! \return max mp for the actor */ +uint32_t Core::Entity::Actor::getMaxMp() const +{ + return m_baseStats.max_mp; +} + +/*! \return reset hp to current max hp */ +void Core::Entity::Actor::resetHp() +{ + m_hp = getMaxHp(); +} + +/*! \return reset mp to current max mp */ +void Core::Entity::Actor::resetMp() +{ + m_mp = getMaxMp(); +} + +/*! \param hp amount to set ( caps to maxHp ) */ +void Core::Entity::Actor::setHp( uint32_t hp ) +{ + m_hp = hp < getMaxHp() ? hp : getMaxHp(); +} + +/*! \param mp amount to set ( caps to maxMp ) */ +void Core::Entity::Actor::setMp( uint32_t mp ) +{ + m_mp = mp < getMaxMp() ? mp : getMaxMp(); +} + +/*! \param mp amount to set ( caps to maxMp ) */ +void Core::Entity::Actor::setGp( uint32_t gp ) +{ + m_gp = gp; +} + +/*! \return current status of the actor */ +Core::Entity::Actor::ActorStatus Core::Entity::Actor::getStatus() const +{ + return m_status; +} + +/*! \param status to set */ +void Core::Entity::Actor::setStatus( ActorStatus status ) +{ + m_status = status; +} + +/*! +Performs necessary steps to mark an actor dead. +Sets hp/mp/tp, sets status, plays animation and fires onDeath event +*/ +void Core::Entity::Actor::die() +{ + m_status = ActorStatus::Dead; + m_hp = 0; + m_mp = 0; + m_tp = 0; + + // fire onDeath event + onDeath(); + + bool selfNeedsUpdate = false; + + // if the actor is a player, the update needs to be send to himself too + if( isPlayer() ) + selfNeedsUpdate = true; + + sendToInRangeSet( ActorControlPacket142( m_id, SetStatus, static_cast< uint8_t>( ActorStatus::Dead ) ), selfNeedsUpdate ); + + // TODO: not all actor show the death animation when they die, some quest npcs might just despawn + // although that might be handled by setting the HP to 1 and doing some script magic + sendToInRangeSet( ActorControlPacket142( m_id, DeathAnimation, 0, 0, 0, 0x20 ), selfNeedsUpdate ); + +} + +/*! +Calculates and sets the rotation to look towards a specified +position + +\param Position to look towards +*/ +bool Core::Entity::Actor::face( const Common::FFXIVARR_POSITION3& p ) +{ + float oldRot = getRotation(); + float rot = Math::Util::calcAngFrom( getPos().x, getPos().z, p.x, p.z ); + float newRot = PI - rot + ( PI / 2 ); + + m_pCell = nullptr; + + setRotation( newRot ); + + if( oldRot != newRot ) + return true; + + return false; +} + +/*! +Sets the actors position and notifies the zone to propagate the change + +\param Position to set +*/ +void Core::Entity::Actor::setPosition( const Common::FFXIVARR_POSITION3& pos ) +{ + m_pos = pos; + m_pCurrentZone->changeActorPosition( shared_from_this() ); +} + +void Core::Entity::Actor::setPosition( float x, float y, float z ) +{ + m_pos.x = x; + m_pos.y = y; + m_pos.z = z; + m_pCurrentZone->changeActorPosition( shared_from_this() ); +} + +/*! +Set and propagate the actor stance to in range players +( not the actor himself ) + +\param stance to set +*/ +void Core::Entity::Actor::setStance( Stance stance ) +{ + m_currentStance = stance; + + sendToInRangeSet( ActorControlPacket142( m_id, ToggleAggro, stance, 1 ) ); +} + +/*! +Check if an action is queued for execution, if so update it +and if fully performed, clean up again. + +\return true if a queued action has been updated +*/ +bool Core::Entity::Actor::checkAction() +{ + + if( m_pCurrentAction == nullptr ) + return false; + + if( m_pCurrentAction->update() ) + m_pCurrentAction.reset(); + + return true; + +} + +/*! +Change the current target and propagate to in range players + +\param target actor id +*/ +void Core::Entity::Actor::changeTarget( uint64_t targetId ) +{ + setTargetId( targetId ); + + sendToInRangeSet( ActorControlPacket144( m_id, SetTarget, + 0, 0, 0, 0, targetId ) ); +} + +/*! +Dummy function \return 0 +*/ +uint8_t Core::Entity::Actor::getLevel() const +{ + return 0; +} + +/*! +Let an actor take damage and perform necessary steps +according to resulting hp, propagates new hp value to players +in range +TODO: eventually this needs to distinguish between physical and +magical dmg and take status effects into account + +\param amount of damage to be taken +*/ +void Core::Entity::Actor::takeDamage( uint32_t damage ) +{ + if( damage >= m_hp ) + { + m_hp = 0; + die(); + } + else + m_hp -= damage; + + sendStatusUpdate( false ); +} + +/*! +Send an HpMpTp update to players in range ( and potentially to self ) +TODO: poor naming, should be changed. Status is not HP. Also should be virtual +so players can have their own version and we can abolish the param. + +\param true if the update should also be sent to the actor ( player ) himself +*/ +void Core::Entity::Actor::sendStatusUpdate( bool toSelf ) +{ + UpdateHpMpTpPacket updateHpPacket( shared_from_this() ); + sendToInRangeSet( updateHpPacket ); +} + +/*! \return pointer to this instance as PlayerPtr */ +Core::Entity::PlayerPtr Core::Entity::Actor::getAsPlayer() +{ + return boost::dynamic_pointer_cast< Entity::Player, Entity::Actor >( shared_from_this() ); +} + +/*! \return pointer to this instance as BattleNpcPtr */ +Core::Entity::BattleNpcPtr Core::Entity::Actor::getAsBattleNpc() +{ + return boost::reinterpret_pointer_cast< Entity::BattleNpc, Entity::Actor >( shared_from_this() ); +} + +/*! \return ActionPtr of the currently registered action, or nullptr */ +Core::Action::ActionPtr Core::Entity::Actor::getCurrentAction() const +{ + return m_pCurrentAction; +} + +/*! \param ActionPtr of the action to be registered */ +void Core::Entity::Actor::setCurrentAction( Core::Action::ActionPtr pAction ) +{ + m_pCurrentAction = pAction; +} + +/*! +check if a given actor is in the actors in range set + +\param ActorPtr to be checked for +\return true if the actor was found +*/ +bool Core::Entity::Actor::isInRangeSet( ActorPtr pActor ) const +{ + return !( m_inRangeActors.find( pActor ) == m_inRangeActors.end() ); +} + +/*! \return ActorPtr of the closest actor in range, if none, nullptr */ +Core::Entity::ActorPtr Core::Entity::Actor::getClosestActor() +{ + if( m_inRangeActors.empty() ) + // no actors in range, don't bother + return nullptr; + + ActorPtr tmpActor = nullptr; + + // arbitrary high number + float minDistance = 10000; + + for( auto pCurAct : m_inRangeActors ) + { + float distance = Math::Util::distance( getPos().x, + getPos().y, + getPos().z, + pCurAct->getPos().x, + pCurAct->getPos().y, + pCurAct->getPos().z ); + + if( distance < minDistance ) + { + minDistance = distance; + tmpActor = pCurAct; + } + } + + return tmpActor; +} + +/*! +Send a packet to all players in range, potentially to self if set and is player + +\param GamePacketPtr to send +\param bool should be send to self? +*/ +void Core::Entity::Actor::sendToInRangeSet( Network::Packets::GamePacketPtr pPacket, bool bToSelf ) +{ + + if( bToSelf && isPlayer() ) + { + auto pPlayer = getAsPlayer(); + + auto pSession = g_serverZone.getSession( pPlayer->getId() ); + + // it might be that the player DC'd in which case the session would be invalid + if( pSession ) + pSession->getZoneConnection()->queueOutPacket( pPacket ); + } + + if( m_inRangePlayers.empty() ) + return; + + for( auto pCurAct : m_inRangePlayers ) + { + assert( pCurAct ); + pPacket->setValAt( 0x04, m_id ); + pPacket->setValAt( 0x08, pCurAct->m_id ); + // it might be that the player DC'd in which case the session would be invalid + pCurAct->queuePacket( pPacket ); + } +} + +/*! +Add a given actor to the fitting in range set according to type +but also to the global actor map + +\param ActorPtr to add +*/ +void Core::Entity::Actor::addInRangeActor( ActorPtr pActor ) +{ + + // if this is null, something went wrong + assert( pActor ); + + // add actor to in range set + m_inRangeActors.insert( pActor ); + + if( pActor->isPlayer() ) + { + auto pPlayer = pActor->getAsPlayer(); + + // if actor is a player, add it to the in range player set + m_inRangePlayers.insert( pPlayer ); + } + + m_inRangeActorMap[pActor->getId()] = pActor; +} + +/*! +Remove a given actor from the fitting in range set according to type +but also to the global actor map + +\param ActorPtr to remove +*/ +void Core::Entity::Actor::removeInRangeActor( ActorPtr pActor ) +{ + // if this is null, something went wrong + assert( pActor ); + + // call virtual event + onRemoveInRangeActor( pActor ); + + // remove actor from in range actor set + m_inRangeActors.erase( pActor ); + + // if actor is a player, despawn ourself for him + // TODO: move to virtual onRemove? + if( isPlayer() ) + pActor->despawn( shared_from_this() ); + + if( pActor->isPlayer() ) + { + auto pPlayer = pActor->getAsPlayer(); + m_inRangePlayers.erase( pPlayer ); + } + + m_inRangeActorMap.erase( pActor->getId() ); +} + +/*! \return true if there is at least one actor in the in range set */ +bool Core::Entity::Actor::hasInRangeActor() const +{ + return ( m_inRangeActors.size() > 0 ); +} + +/*! Clear the whole in range set, this does no cleanup */ +void Core::Entity::Actor::clearInRangeSet() +{ + m_inRangeActors.clear(); + m_inRangePlayers.clear(); + m_inRangeActorMap.clear(); +} + +/*! \return ZonePtr to the current zone, nullptr if not set */ +Core::ZonePtr Core::Entity::Actor::getCurrentZone() const +{ + return m_pCurrentZone; +} + +/*! \param ZonePtr to the zone to be set as current */ +void Core::Entity::Actor::setCurrentZone( ZonePtr currZone ) +{ + m_pCurrentZone = currZone; +} + +/*! +Get the current cell of a region the actor is in + +\return Cell* +*/ +Core::Cell * Core::Entity::Actor::getCell() const +{ + return m_pCell; +} + +/*! +Set the current cell the actor is in + +\param Cell* for the cell to be set +*/ +void Core::Entity::Actor::setCell( Cell * pCell ) +{ + m_pCell = pCell; +} + +/*! +Autoattack prototype implementation +TODO: move the check if the autoAttack can be performed to the callee +also rename autoAttack to autoAttack as that is more elaborate +On top of that, this only solves attacks from melee classes. +Will have to be extended for ranged attacks. + +\param ActorPtr the autoAttack is performed on +*/ +void Core::Entity::Actor::autoAttack( ActorPtr pTarget ) +{ + + uint64_t tick = Util::getTimeMs(); + + if( ( tick - m_lastAttack ) > 2500 ) + { + pTarget->onActionHostile( shared_from_this() ); + m_lastAttack = tick; + srand( static_cast< uint32_t >( tick ) ); + + uint32_t damage = 10 + rand() % 12; + uint32_t variation = 0 + rand() % 3; + + GamePacketNew< FFXIVIpcEffect > effectPacket( getId() ); + effectPacket.data().targetId = pTarget->getId(); + effectPacket.data().actionAnimationId = 0x366; + effectPacket.data().unknown_2 = variation; +// effectPacket.data().unknown_3 = 1; + effectPacket.data().actionTextId = 0x366; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + effectPacket.data().effectTarget = pTarget->getId(); + effectPacket.data().effects[0].param1 = damage; + effectPacket.data().effects[0].unknown_1 = 3; + effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].unknown_3 = 7; + + sendToInRangeSet( effectPacket ); + + if( this->isPlayer() ) + this->getAsPlayer()->queuePacket( effectPacket ); + + pTarget->takeDamage( damage ); + } +} + +/*! \param StatusEffectPtr to be applied to the actor */ +void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect ) +{ + m_pStatusEffectContainer->addStatusEffect( pEffect ); +} + diff --git a/src/servers/Server_Zone/Actor.h b/src/servers/Server_Zone/Actor.h new file mode 100644 index 00000000..aa6dc39b --- /dev/null +++ b/src/servers/Server_Zone/Actor.h @@ -0,0 +1,303 @@ +#ifndef _ACTOR_H_ +#define _ACTOR_H_ + +#include +#include + +#include "Forwards.h" +#include +#include + +namespace Core { +namespace Entity { + +/*! +\class Actor +\brief Base class for all actors + +\author Mordred +*/ +class Actor : public boost::enable_shared_from_this< Actor > +{ +public: + enum ActorType : uint8_t + { + None = 0x00, + Player = 0x01, + BattleNpc = 0x02, + EventNpc = 0x03, + unk1 = 0x04, + AetheryteNpc = 0x05, + ResourceNode = 0x06, + EventObj = 0x07, + Mount = 0x08, + Minion = 0x09, + Retainer = 0x0A, + unk2 = 0x0B, + Furniture = 0x0C, + }; + + enum Stance : uint8_t + { + Passive = 0, + Active = 1, + }; + + enum struct ActorStatus : uint8_t + { + Idle = 0x01, + Dead = 0x02, + Sitting = 0x03, + Mounted = 0x04, + Crafting = 0x05, + Gathering = 0x06, + Melding = 0x07, + SMachine = 0x08 + }; + +protected: + // TODO: The position class should probably be abolished and + // the FFXIV_POS struct used instead ( the functions in there + // could be moved to a FFXIV_POS_Helper namespace and rotation to + // its own member ) + /*! Position of the actor */ + Common::FFXIVARR_POSITION3 m_pos; + float m_rot; + /*! Name of the actor */ + char m_name[34]; + /*! Id of the zone the actor currently is in */ + uint32_t m_zoneId; + /*! Id of the actor */ + uint32_t m_id; + /*! Type of the actor */ + ActorType m_type; + /*! Ptr to the ZoneObj the actor belongs to */ + ZonePtr m_pCurrentZone; + /*! Last tick time for the actor ( in ms ) */ + uint64_t m_lastTickTime; + /*! Last time the actor performed an autoAttack ( in ms ) */ + uint64_t m_lastAttack; + /*! Last time the actor was updated ( in ms ) */ + uint64_t m_lastUpdate; + /*! Current stance of the actor */ + Stance m_currentStance; + /*! Current staus of the actor */ + ActorStatus m_status; + /*! Max HP of the actor ( based on job / class ) */ + uint32_t m_maxHp; + /*! Max MP of the actor ( based on job / class ) */ + uint32_t m_maxMp; + /*! Current HP of the actor */ + uint32_t m_hp; + /*! Current MP of the actor */ + uint32_t m_mp; + /*! Current TP of the actor */ + uint16_t m_tp; + /*! Current GP of the actor */ + uint16_t m_gp; + /*! Additional look info of the actor */ + uint8_t m_customize[26]; + /*! Current class of the actor */ + Common::ClassJob m_class; + /*! Id of the currently selected target actor */ + uint64_t m_targetId; + /*! Ptr to a queued action */ + Action::ActionPtr m_pCurrentAction; + /*! Container for status effects */ + StatusEffect::StatusEffectContainerPtr m_pStatusEffectContainer; + + struct + { + uint32_t max_mp = 0; + uint32_t max_hp = 0; + + uint32_t str = 0; + uint32_t dex = 0; + uint32_t vit = 0; + uint32_t inte = 0; + uint32_t mnd = 0; + uint32_t pie = 0; + + uint32_t parry = 0; + uint32_t attack = 0; + uint32_t defense = 0; + uint32_t accuracy = 0; + uint32_t spellSpeed = 0; + uint32_t magicDefense = 0; + uint32_t critHitRate = 0; + uint32_t resistSlash = 0; + uint32_t resistPierce = 0; + uint32_t resistBlunt = 0; + uint32_t attackPotMagic = 0; + uint32_t healingPotMagic = 0; + uint32_t determination = 0; + uint32_t skillSpeed = 0; + + uint32_t resistSlow = 0; + uint32_t resistSilence = 0; + uint32_t resistBlind = 0; + uint32_t resistPoison = 0; + uint32_t resistStun = 0; + uint32_t resistSleep = 0; + uint32_t resistBind = 0; + uint32_t resistHeavy = 0; + + uint32_t resistFire = 0; + uint32_t resistIce = 0; + uint32_t resistWind = 0; + uint32_t resistEarth = 0; + uint32_t resistLightning = 0; + uint32_t resistWater = 0; + + } m_baseStats; + +public: + Actor(); + + virtual ~Actor(); + + virtual void calculateStats() {}; + + uint32_t getId() const; + + void setPosition( const Common::FFXIVARR_POSITION3& pos ); + void setPosition( float x, float y, float z ); + + void setRotation( float rot ) + { + m_rot = rot; + } + + float getRotation() const + { + return m_rot; + } + + Common::FFXIVARR_POSITION3& getPos(); + + std::string getName() const; + + bool isPlayer() const; + + bool isMob() const; + + std::set< ActorPtr > getInRangeActors() const; + + bool face( const Common::FFXIVARR_POSITION3& p ); + + Stance getStance() const; + + void setStance( Stance stance ); + + uint32_t getHp() const; + + uint32_t getMp() const; + + uint16_t getTp() const; + + uint16_t getGp() const; + + Common::ClassJob getClass() const; + + uint8_t getClassAsInt() const; + + void setClass( Common::ClassJob classJob ); + + void setTargetId( uint64_t targetId ); + + uint64_t getTargetId() const; + + bool isAlive() const; + + virtual uint32_t getMaxHp() const; + + virtual uint32_t getMaxMp() const; + + void resetHp(); + + void resetMp(); + + void setHp( uint32_t hp ); + + void setMp( uint32_t mp ); + + void setGp( uint32_t gp ); + + void die(); + + ActorStatus getStatus() const; + + void setStatus( ActorStatus status ); + + virtual void autoAttack( ActorPtr pTarget ); + + virtual void spawn( PlayerPtr pTarget ) {} + virtual void despawn( ActorPtr pTarget ) {} + + virtual void onRemoveInRangeActor( ActorPtr pActor ) {} + virtual void onDeath() {}; + virtual void onDamageTaken( Actor& pSource ) {}; + virtual void onActionHostile( Core::Entity::ActorPtr pSource ) {}; + virtual void onActionFriendly( Actor& pSource ) {}; + virtual void onTick() {}; + + virtual void changeTarget( uint64_t targetId ); + virtual uint8_t getLevel() const; + virtual void sendStatusUpdate( bool toSelf = true ); + virtual void takeDamage( uint32_t damage ); + virtual bool checkAction(); + virtual void update( int64_t currTime ) {}; + + PlayerPtr getAsPlayer(); + BattleNpcPtr getAsBattleNpc(); + + Action::ActionPtr getCurrentAction() const; + + void setCurrentAction( Action::ActionPtr pAction ); + + ///// IN RANGE LOGIC ///// + + // check if another actor is in the actors in range set + bool isInRangeSet( ActorPtr pActor ) const; + + ActorPtr getClosestActor(); + + void sendToInRangeSet( Network::Packets::GamePacketPtr pPacket, bool bToSelf = false ); + + // add an actor to in range set + virtual void addInRangeActor( ActorPtr pActor ); + + // remove an actor from the in range set + void removeInRangeActor( ActorPtr pActor ); + + // return true if there is at least one actor in the in range set + bool hasInRangeActor() const; + + // clear the whole in range set, this does no cleanup + virtual void clearInRangeSet(); + + ZonePtr getCurrentZone() const; + + void setCurrentZone( ZonePtr currZone ); + + // get the current cell of a region the actor is in + Cell* getCell() const; + + // set the current cell + void setCell( Cell* pCell ); + + // add a status effect + void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); + + // TODO: Why did i even declare them publicly here?! + std::set< ActorPtr > m_inRangeActors; + std::set< PlayerPtr > m_inRangePlayers; + std::map< uint32_t, ActorPtr > m_inRangeActorMap; + + Core::Cell* m_pCell; + +}; + +} +} +#endif diff --git a/src/servers/Server_Zone/ActorControlPacket142.h b/src/servers/Server_Zone/ActorControlPacket142.h new file mode 100644 index 00000000..f39bfe8d --- /dev/null +++ b/src/servers/Server_Zone/ActorControlPacket142.h @@ -0,0 +1,49 @@ +#ifndef _ACTORCONTROL142_H +#define _ACTORCONTROL142_H + +#include +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class ActorControlPacket142 : + public GamePacketNew< FFXIVIpcActorControl142 > +{ +public: + ActorControlPacket142( uint32_t actorId, + uint16_t category, + uint32_t param1 = 0, + uint32_t param2 = 0, + uint32_t param3 = 0, + uint32_t param4 = 0, + uint32_t padding1 = 0 ) : + GamePacketNew< FFXIVIpcActorControl142 >( actorId, actorId ) + { + initialize( category, param1, param2, param3, param4 ); + }; + +private: + void initialize( uint16_t category, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4 ) + { + m_data.padding = 0; + m_data.category = category; + m_data.param1 = param1; + m_data.param2 = param2; + m_data.param3 = param3; + m_data.param4 = param4; + }; +}; + +} +} +} +} + +#endif /*_ACTORCONTROL142_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/ActorControlPacket143.h b/src/servers/Server_Zone/ActorControlPacket143.h new file mode 100644 index 00000000..adf49610 --- /dev/null +++ b/src/servers/Server_Zone/ActorControlPacket143.h @@ -0,0 +1,52 @@ +#ifndef _ACTORCONTROL143_H +#define _ACTORCONTROL143_H + +#include +#include +#include "Forwards.h" + + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class ActorControlPacket143 : + public GamePacketNew< FFXIVIpcActorControl143 > +{ +public: + ActorControlPacket143( uint32_t actorId, + uint16_t category, + uint32_t param1 = 0, + uint32_t param2 = 0, + uint32_t param3 = 0, + uint32_t param4 = 0, + uint32_t param5 = 0, + uint32_t padding1 = 0 ) : + GamePacketNew< FFXIVIpcActorControl143 >( actorId, actorId ) + { + initialize( category, param1, param2, param3, param4, param5 ); + }; + +private: + void initialize( uint16_t category, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4, uint32_t param5 ) + { + m_data.padding = 0; + m_data.category = category; + m_data.param1 = param1; + m_data.param2 = param2; + m_data.param3 = param3; + m_data.param4 = param4; + m_data.param5 = param5; + }; +}; + +} +} +} +} + +#endif /*_ACTORCONTROL143_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/ActorControlPacket144.h b/src/servers/Server_Zone/ActorControlPacket144.h new file mode 100644 index 00000000..819003f2 --- /dev/null +++ b/src/servers/Server_Zone/ActorControlPacket144.h @@ -0,0 +1,50 @@ +#ifndef _ACTORCONTROL144_H +#define _ACTORCONTROL144_H + +#include +#include + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class ActorControlPacket144 : + public GamePacketNew< FFXIVIpcActorControl144 > +{ +public: + ActorControlPacket144( uint32_t actorId, + uint16_t category, + uint32_t param1 = 0, + uint32_t param2 = 0, + uint32_t param3 = 0, + uint32_t param4 = 0, + uint64_t targetId = 0, + uint32_t padding1 = 0 ) : + GamePacketNew< FFXIVIpcActorControl144 >( actorId, actorId ) + { + initialize( category, param1, param2, param3, param4, targetId ); + }; + +private: + void initialize( uint16_t category, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4, uint64_t targetId ) + { + m_data.padding = 0; + m_data.category = category; + m_data.param1 = param1; + m_data.param2 = param2; + m_data.param3 = param3; + m_data.param4 = param4; + m_data.targetId = targetId; + }; +}; + +} +} +} +} + +#endif /*_ACTORCONTROL144_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/ActorSpawnPacket.h b/src/servers/Server_Zone/ActorSpawnPacket.h new file mode 100644 index 00000000..55bb4f17 --- /dev/null +++ b/src/servers/Server_Zone/ActorSpawnPacket.h @@ -0,0 +1,91 @@ +#ifndef _ACTORSPAWN_H +#define _ACTORSPAWN_H + +#include +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The packet sent to finish an event. +*/ +class ActorSpawnPacket : + public GamePacketNew +{ +public: + ActorSpawnPacket( Entity::PlayerPtr pPlayer, Entity::PlayerPtr pTarget ) : + GamePacketNew( pPlayer->getId(), pTarget->getId() ) + { + initialize( pPlayer, pTarget ); + }; + +private: + void initialize( Entity::PlayerPtr pPlayer, Entity::PlayerPtr pTarget ) + { + // TODO: temporary gm rank + m_data.gmRank = 0xff; + m_data.classJob = pPlayer->getClass(); + m_data.status = static_cast< uint8_t >( pPlayer->getStatus() ); + m_data.hPCurr = pPlayer->getHp(); + m_data.mPCurr = pPlayer->getMp(); + m_data.tPCurr = pPlayer->getTp(); + m_data.hPMax = pPlayer->getMaxHp(); + m_data.mPMax = pPlayer->getMaxMp(); + m_data.tPMax = 3000; + m_data.level = pPlayer->getLevel(); + memcpy( m_data.look, pPlayer->getLookArray(), 26 ); + m_data.mainWeaponModel = pPlayer->getModelMainWeapon(); + m_data.secWeaponModel = pPlayer->getModelSubWeapon(); + m_data.models[0] = pPlayer->getModelForSlot( Inventory::EquipSlot::Head ); + m_data.models[1] = pPlayer->getModelForSlot( Inventory::EquipSlot::Body ); + m_data.models[2] = pPlayer->getModelForSlot( Inventory::EquipSlot::Hands ); + m_data.models[3] = pPlayer->getModelForSlot( Inventory::EquipSlot::Legs ); + m_data.models[4] = pPlayer->getModelForSlot( Inventory::EquipSlot::Feet ); + strcpy( m_data.name, pPlayer->getName().c_str() ); + m_data.pos.x = pPlayer->getPos().x; + m_data.pos.y = pPlayer->getPos().y; + m_data.pos.z = pPlayer->getPos().z; + m_data.voice = pPlayer->getVoiceId(); + + m_data.rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + + m_data.statusIcon = pPlayer->getOnlineStatus(); + + if( pTarget == pPlayer ) + { + m_data.spawnIndex = 0x00; + } + else + { + m_data.spawnIndex = pTarget->getSpawnIdForActorId( pPlayer->getId() ); + } + // 0x20 == spawn hidden to be displayed by the spawneffect control + m_data.displayFlags = pPlayer->getStance(); + + if( pPlayer->getZoningType() != Common::ZoneingType::None ) + { + m_data.displayFlags |= 0x20; + } + + m_data.targetId = pPlayer->getTargetId(); + m_data.type = 1; + m_data.unknown_33 = 4; + //m_data.unknown_38 = 0x70; + //m_data.unknown_60 = 3; + //m_data.unknown_61 = 7; + + + }; +}; + +} +} +} +} + +#endif /*_ACTORSPAWN_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/BattleNpc.cpp b/src/servers/Server_Zone/BattleNpc.cpp new file mode 100644 index 00000000..93956510 --- /dev/null +++ b/src/servers/Server_Zone/BattleNpc.cpp @@ -0,0 +1,574 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Player.h" +#include "BattleNpc.h" + +#include "MoveActorPacket.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::Data::ExdData g_exdData; + +uint32_t Core::Entity::BattleNpc::m_nextID = 1149241694; + +Core::Entity::BattleNpc::BattleNpc() +{ + m_id = 0; + m_type = ActorType::BattleNpc; + m_status = ActorStatus::Idle; +} + +Core::Entity::BattleNpc::~BattleNpc() +{ + +} + +Core::Entity::BattleNpc::BattleNpc( uint32_t modelId, uint32_t nameid, const Common::FFXIVARR_POSITION3& spawnPos, + uint32_t sizeId, uint32_t type, uint32_t level, uint32_t behaviour, + uint32_t mobType ) +{ + BattleNpc::m_nextID++; + m_id = BattleNpc::m_nextID; + //strcpy( m_name, pBNpc->m_name.c_str() ); + + m_pos = spawnPos; + m_posOrigin = spawnPos; + + m_type = ActorType::BattleNpc; + + m_mode = MODE_IDLE; + + m_maxHp = 150; + m_maxMp = 100; + + m_baseStats.max_hp = m_maxHp; + m_baseStats.max_mp = m_maxMp; + + m_hp = m_maxHp; + m_mp = m_maxMp; + + m_currentStance = Stance::Passive; + + m_class = ClassJob::CLASS_GLADIATOR; + m_level = level > 0 ? level : 60; + + m_modelId = modelId; + m_nameId = nameid; + + m_behavior = behaviour; + + m_bnpcBaseId = sizeId; + + m_status = ActorStatus::Idle; + + m_pOwner = nullptr; + + m_mobType = mobType; + + //m_type = static_cast< Common::ActorType >( type ); + +} + + +// spawn this player for pTarget +void Core::Entity::BattleNpc::spawn( Core::Entity::PlayerPtr pTarget ) +{ + //GamePacketNew< FFXIVIpcActorSpawn > spawnPacket( getId(), pTarget->getId() ); + + //spawnPacket.data().unknown_0 = 0; + //spawnPacket.data().ownerId = m_pOwner == nullptr ? INVALID_GAME_OBJECT_ID : m_pOwner->getId(); + //spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF; + //spawnPacket.data().hPCurr = m_hp; + //spawnPacket.data().hPMax = m_baseStats.max_hp; + //spawnPacket.data().level = m_level; + ////spawnPacket.data().tPCurr = 1000; + //spawnPacket.data().model = m_modelId; + //spawnPacket.data().bnpcBaseId = m_bnpcBaseId; + //spawnPacket.data().nameId = m_nameId; + //spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); + //g_log.info(std::to_string(spawnPacket.data().spawnIndex) + " " + std::to_string(getId())); + //spawnPacket.data().status = static_cast< uint8_t >( m_status ); + //spawnPacket.data().mobAgressive = m_behavior; + //spawnPacket.data().type = static_cast< uint8_t >( m_type ); + //spawnPacket.data().mobTypeIcon = m_mobType; + //spawnPacket.data().unknown_33 = 5; + //spawnPacket.data().typeFlags = 4; + //spawnPacket.data().pos.x = m_pos.x; + //spawnPacket.data().pos.y = m_pos.y; + //spawnPacket.data().pos.z = m_pos.z; + //spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + ////spawnPacket.data().unknown_B0[11] = 1; + ////spawnPacket.data().unknown_B0[12] = 4; + ////spawnPacket.data().unknown_B0[14] = 20; + + //pTarget->queuePacket( spawnPacket ); + + GamePacketNew< FFXIVIpcNpcSpawn > spawnPacket( getId(), pTarget->getId() ); + + + spawnPacket.data().pos.x = m_pos.x; + spawnPacket.data().pos.y = m_pos.y; + spawnPacket.data().pos.z = m_pos.z; + + spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF; + spawnPacket.data().hPCurr = m_hp; + spawnPacket.data().hPMax = m_baseStats.max_hp; + spawnPacket.data().level = m_level; + + spawnPacket.data().subtype = 5; + spawnPacket.data().enemyType = 4; + + spawnPacket.data().modelChara = m_modelId; + spawnPacket.data().bNPCBase = m_bnpcBaseId; + spawnPacket.data().bNPCName = m_nameId; + spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); + + spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + + spawnPacket.data().type = static_cast< uint8_t >( m_type ); + + spawnPacket.data().state = static_cast< uint8_t >( m_status ); + + pTarget->queuePacket( spawnPacket ); +} + +// despawn +void Core::Entity::BattleNpc::despawn( Core::Entity::ActorPtr pTarget ) +{ + + auto pPlayer = pTarget->getAsPlayer(); + + pPlayer->freePlayerSpawnId( getId() ); + + ActorControlPacket143 controlPacket( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ); + pPlayer->queuePacket( controlPacket ); + +} + +uint8_t Core::Entity::BattleNpc::getLevel() const +{ + return m_level; +} + +Core::Entity::StateMode Core::Entity::BattleNpc::getMode() const +{ + return m_mode; +} + +void Core::Entity::BattleNpc::setMode( Core::Entity::StateMode mode ) +{ + m_mode = mode; +} + +uint8_t Core::Entity::BattleNpc::getbehavior() const +{ + return m_behavior; +} + +void Core::Entity::BattleNpc::hateListAdd( Core::Entity::ActorPtr pActor, int32_t hateAmount ) +{ + HateListEntry* hateEntry = new HateListEntry(); + hateEntry->m_hateAmount = hateAmount; + hateEntry->m_pActor = pActor; + + m_hateList.insert( hateEntry ); +} + +Core::Entity::ActorPtr Core::Entity::BattleNpc::hateListGetHighest() +{ + + auto it = m_hateList.begin(); + uint32_t maxHate = 0; + HateListEntry* entry = nullptr; + for( ; it != m_hateList.end(); ++it ) + { + if( ( *it )->m_hateAmount > maxHate ) + { + maxHate = ( *it )->m_hateAmount; + entry = *it; + } + } + + if( entry && maxHate != 0 ) + return entry->m_pActor; + + return nullptr; +} + +void Core::Entity::BattleNpc::setOwner( Core::Entity::PlayerPtr pPlayer ) +{ + m_pOwner = pPlayer; + + if( pPlayer != nullptr ) + { + GamePacketNew< FFXIVIpcActorOwner > setOwnerPacket( getId(), pPlayer->getId() ); + setOwnerPacket.data().type = 0x01; + setOwnerPacket.data().actorId = pPlayer->getId(); + sendToInRangeSet( setOwnerPacket ); + } + else + { + GamePacketNew< FFXIVIpcActorOwner > setOwnerPacket(getId(), INVALID_GAME_OBJECT_ID); + setOwnerPacket.data().type = 0x01; + setOwnerPacket.data().actorId = INVALID_GAME_OBJECT_ID; + sendToInRangeSet( setOwnerPacket ); + } + +} + +void Core::Entity::BattleNpc::sendPositionUpdate() +{ + MoveActorPacket movePacket( shared_from_this(), 0x3A, 0x00, 0, 0x5A ); + sendToInRangeSet( movePacket ); +} + +bool Core::Entity::BattleNpc::moveTo( Common::FFXIVARR_POSITION3& pos ) +{ + + if( Math::Util::distance( getPos().x, getPos().y, getPos().z, + pos.x, pos.y, pos.z ) <= 4 ) + // reached destination + return true; + + float rot = Math::Util::calcAngFrom(getPos().x, getPos().z, pos.x, pos.z); + float newRot = PI - rot + (PI / 2); + + face( pos ); + float angle = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI; + + float x = static_cast< float >( cosf(angle) * 1.1f ); + float y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision + float z = static_cast< float >( sinf(angle) * 1.1f ); + + Common::FFXIVARR_POSITION3 newPos; + + newPos.x = getPos().x + x; + newPos.y = y; + newPos.z = getPos().z + z; + + setPosition( newPos ); + + Common::FFXIVARR_POSITION3 tmpPos; + tmpPos.x = getPos().x + x; + tmpPos.y = y; + tmpPos.z = getPos().z + z; + + + angle = angle * 2; + setPosition( tmpPos ); + setRotation(newRot); + + sendPositionUpdate(); + + return false; + +} + +void Core::Entity::BattleNpc::aggro( Core::Entity::ActorPtr pActor ) +{ + + m_lastAttack = Util::getTimeMs(); + hateListUpdate( pActor, 1 ); + + changeTarget( pActor->getId() ); + setStance( Stance::Active ); + m_mode = MODE_COMBAT; + + if( pActor->isPlayer() ) + { + PlayerPtr tmpPlayer = pActor->getAsPlayer(); + tmpPlayer->queuePacket( ActorControlPacket142( getId(), 0, 1, 1 ) ); + tmpPlayer->onMobAggro( getAsBattleNpc() ); + } +} + +void Core::Entity::BattleNpc::deaggro( Core::Entity::ActorPtr pActor ) +{ + if( !hateListHasActor( pActor ) ) + hateListRemove( pActor ); + + if( pActor->isPlayer() ) + { + PlayerPtr tmpPlayer = pActor->getAsPlayer(); + tmpPlayer->onMobDeaggro( getAsBattleNpc() ); + } +} + +void Core::Entity::BattleNpc::hateListClear() +{ + auto it = m_hateList.begin(); + for( ; it != m_hateList.end(); ++it ) + { + if( isInRangeSet( ( *it )->m_pActor ) ) + deaggro( ( *it )->m_pActor ); + HateListEntry* tmpListEntry = ( *it ); + delete tmpListEntry; + } + m_hateList.clear(); +} + + +void Core::Entity::BattleNpc::hateListRemove( Core::Entity::ActorPtr pActor ) +{ + auto it = m_hateList.begin(); + for( ; it != m_hateList.end(); ++it ) + { + if( ( *it )->m_pActor == pActor ) + { + HateListEntry* pEntry = *it; + m_hateList.erase( it ); + delete pEntry; + if( pActor->isPlayer() ) + { + PlayerPtr tmpPlayer = pActor->getAsPlayer(); + tmpPlayer->onMobDeaggro( getAsBattleNpc() ); + } + return; + } + } +} + +bool Core::Entity::BattleNpc::hateListHasActor( Core::Entity::ActorPtr pActor ) +{ + auto it = m_hateList.begin(); + for( ; it != m_hateList.end(); ++it ) + { + if( ( *it )->m_pActor == pActor ) + return true; + } + return false; +} + +void Core::Entity::BattleNpc::resetPos() +{ + m_pos = m_posOrigin; +} + +uint32_t Core::Entity::BattleNpc::getNameId() const +{ + return m_nameId; +} + +void Core::Entity::BattleNpc::hateListUpdate( Core::Entity::ActorPtr pActor, int32_t hateAmount ) +{ + + auto it = m_hateList.begin(); + for( ; it != m_hateList.end(); ++it ) + { + if( ( *it )->m_pActor == pActor ) + { + ( *it )->m_hateAmount += hateAmount; + return; + } + } + + HateListEntry* hateEntry = new HateListEntry(); + hateEntry->m_hateAmount = hateAmount; + hateEntry->m_pActor = pActor; + m_hateList.insert( hateEntry ); +} + +void Core::Entity::BattleNpc::onDeath() +{ + //LuaManager->onMobDeath( this ); + + setTimeOfDeath( static_cast< uint32_t >( time( nullptr ) ) ); + setTargetId( INVALID_GAME_OBJECT_ID ); + m_currentStance = Stance::Passive; + m_mode = MODE_IDLE; + m_hp = 0; + setOwner( nullptr ); + + // todo: fully ghetto retarded exp reward pls fix + { + uint32_t minHate = -1; + uint32_t maxHate = 0; + uint32_t totalHate = 0; + for( auto& pHateEntry : m_hateList ) + { + if( pHateEntry->m_pActor->isPlayer() ) + { + if( pHateEntry->m_hateAmount < minHate ) + minHate = pHateEntry->m_hateAmount; + else if( pHateEntry->m_hateAmount > maxHate ) + maxHate = pHateEntry->m_hateAmount; + } + totalHate += pHateEntry->m_hateAmount; + } + + //uint32_t plsBeHatedThisMuchAtLeast = totalHate / ( maxHate + 2 ) / clamp( m_hateList.size(), 1.0f, 1.5f ); + + for( auto& pHateEntry : m_hateList ) + { + // todo: this is pure retarded + // todo: check for companion + if( pHateEntry->m_pActor->isPlayer() ) // && pHateEntry->m_hateAmount >= plsBeHatedThisMuchAtLeast ) + { + auto level = pHateEntry->m_pActor->getLevel(); + auto levelDiff = (int)this->m_level - (int)level; + auto cappedLevelDiff = Math::Util::clamp( levelDiff, 1, 6 ); + + auto expNeeded = g_exdData.m_paramGrowthInfoMap[m_level + cappedLevelDiff - 1].needed_exp; + int32_t exp = 0; + + // todo: arbitrary numbers pulled out of my ass + if( m_level <= 14 ) + exp = ( expNeeded / ( 100 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 9 ) ) + 1 ) ); + else if( m_level <= 24 ) + exp = ( expNeeded / ( 150 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 8 ) ) + 1 ) ); + else if( m_level <= 34 ) + exp = ( expNeeded / ( 350 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 7 ) ) + 1 ) ); + else if( m_level <= 44 ) + exp = ( expNeeded / ( 550 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 6 ) ) + 1 ) ); + else if( m_level <= 50 ) + exp = ( expNeeded / ( 750 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 5 ) ) + 1 ) ); + else + exp = ( expNeeded / ( 1200 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 4 ) ) + 1 ) ); + + + // todo: this is actually retarded, we need real rand() + srand( time( NULL ) ); + + auto pPlayer = pHateEntry->m_pActor->getAsPlayer(); + pPlayer->gainExp( exp ); + pPlayer->onMobKill( m_nameId ); + } + } + } + hateListClear(); +} + +void Core::Entity::BattleNpc::onActionHostile( Core::Entity::ActorPtr pSource ) +{ + + if( hateListGetHighest() == nullptr ) + aggro( pSource ); + + if( getClaimer() == nullptr ) + setOwner( pSource->getAsPlayer() ); +} + +Core::Entity::ActorPtr Core::Entity::BattleNpc::getClaimer() const +{ + return m_pOwner; +} + + +// HACK: this is highly experimental code, will have to be changed eventually +// since there are different types of mobs... (stationary, moving...) likely to be +// handled by scripts entirely. +void Core::Entity::BattleNpc::update( int64_t currTime ) +{ + + if( !isAlive() ) + { + m_status = ActorStatus::Idle; + m_mode = MODE_IDLE; + return; + } + + //m_pStatusEffectContainer->update(); + float distance = Math::Util::distance( m_pos.x, m_pos.y, m_pos.z, + m_posOrigin.x, m_posOrigin.y, m_posOrigin.z ); + + if( ( distance > 70 ) && m_mode != MODE_RETREAT ) + { + changeTarget( INVALID_GAME_OBJECT_ID ); + m_mode = MODE_RETREAT; + hateListClear(); + setOwner( nullptr ); + } + + switch( m_mode ) + { + + case MODE_RETREAT: + { + if( moveTo( m_posOrigin ) ) + m_mode = MODE_IDLE; + } + break; + + case MODE_IDLE: + { + ActorPtr pClosestActor = getClosestActor(); + + if( ( pClosestActor != nullptr ) && pClosestActor->isAlive() ) + { + distance = Math::Util::distance( getPos().x, getPos().y, getPos().z, + pClosestActor->getPos().x, + pClosestActor->getPos().y, + pClosestActor->getPos().z ); + + //if( distance < 8 && getbehavior() == 2 ) + // aggro( pClosestActor ); + } + } + break; + + case MODE_COMBAT: + { + ActorPtr pClosestActor = hateListGetHighest(); + + if( pClosestActor != nullptr && !pClosestActor->isAlive() ) + { + hateListRemove( pClosestActor ); + pClosestActor = hateListGetHighest(); + } + + if( pClosestActor != nullptr ) + { + distance = Math::Util::distance( getPos().x, getPos().y, getPos().z, + pClosestActor->getPos().x, + pClosestActor->getPos().y, + pClosestActor->getPos().z ); + + if( distance > 4 ) + moveTo( pClosestActor->getPos() ); + else + { + if( face( pClosestActor->getPos() ) ) + sendPositionUpdate(); + // in combat range. ATTACK! + autoAttack( pClosestActor ); + } + } + else + { + changeTarget( INVALID_GAME_OBJECT_ID ); + setStance( Stance::Passive ); + setOwner( nullptr ); + m_mode = MODE_RETREAT; + } + + } + break; + } + +} + +uint32_t Core::Entity::BattleNpc::getTimeOfDeath() const +{ + return m_timeOfDeath; +} + +void Core::Entity::BattleNpc::setTimeOfDeath( uint32_t tod ) +{ + m_timeOfDeath = tod; +} + diff --git a/src/servers/Server_Zone/BattleNpc.h b/src/servers/Server_Zone/BattleNpc.h new file mode 100644 index 00000000..65224fdf --- /dev/null +++ b/src/servers/Server_Zone/BattleNpc.h @@ -0,0 +1,113 @@ +#ifndef _BATTLENPC_H +#define _BATTLENPC_H + +#include "Actor.h" + +namespace Core { + +namespace Entity { + +enum StateMode +{ + MODE_COMBAT, + MODE_RETREAT, + MODE_IDLE, +}; + +typedef struct +{ + uint32_t m_hateAmount; + ActorPtr m_pActor; +} HateListEntry; + +// class for Mobs inheriting from Actor +class BattleNpc : public Actor +{ +public: + BattleNpc(); + ~BattleNpc(); + + BattleNpc( uint32_t modelId, uint32_t nameid, const Common::FFXIVARR_POSITION3& spawnPos, uint32_t sizeId = 0, uint32_t type = 2, uint32_t level = 0, uint32_t behaviour = 1, uint32_t mobType = 0 ); + + //BattleNpc( uint32_t modelId, + // uint32_t nameId, + // uint32_t bnpcBaseId, + // uint32_t level, + // const Common::FFXIVARR_POSITION3& spawnPos, + // uint32_t type = 2, uint32_t behaviour = 1, uint32_t mobType = 0 ); + + // send spawn packets to pTarget + void spawn( PlayerPtr pTarget ) override; + + // send despawn packets to pTarget + void despawn( ActorPtr pTarget ) override; + + uint8_t getLevel() const override; + + StateMode getMode() const; + + void setMode( StateMode mode ); + + uint8_t getbehavior() const; + + void hateListAdd( ActorPtr pActor, int32_t hateAmount ); + + void hateListUpdate( ActorPtr pActor, int32_t hateAmount ); + void hateListRemove( ActorPtr pActor ); + + bool hateListHasActor( ActorPtr pActor ); + + void resetPos(); + + uint32_t getNameId() const; + + void hateListClear(); + + ActorPtr hateListGetHighest(); + + void aggro( ActorPtr pActor ); + + void deaggro( ActorPtr pActor ); + + void setOwner( PlayerPtr pPlayer ); + + void onDeath() override; + + void onActionHostile( Core::Entity::ActorPtr pSource ) override; + + ActorPtr getClaimer() const; + + void sendPositionUpdate(); + + // return true if it reached the position + bool moveTo( Common::FFXIVARR_POSITION3& pos ); + + void update( int64_t currTime ) override; + + uint32_t getTimeOfDeath() const; + + void setTimeOfDeath( uint32_t tod ); + +private: + + static uint32_t m_nextID; + StateMode m_mode; + Common::FFXIVARR_POSITION3 m_posOrigin; + uint8_t m_level; + uint16_t m_modelId; + uint16_t m_nameId; + uint16_t m_bnpcBaseId; + uint8_t m_behavior; + uint32_t m_unk1; + uint32_t m_unk2; + std::set< HateListEntry* > m_hateList; + ActorPtr m_pOwner; + int32_t m_timeOfDeath; + uint32_t m_mobType; + +}; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/BattleNpcTemplate.cpp b/src/servers/Server_Zone/BattleNpcTemplate.cpp new file mode 100644 index 00000000..62c21ab1 --- /dev/null +++ b/src/servers/Server_Zone/BattleNpcTemplate.cpp @@ -0,0 +1,37 @@ +#include "BattleNpcTemplate.h" + +Core::Entity::BattleNpcTemplate::BattleNpcTemplate() +{ + +} + +Core::Entity::BattleNpcTemplate::BattleNpcTemplate( std::string templateName, uint32_t bnpcBaseId, + uint32_t bnpcNameId, uint32_t modelId, std::string aiName ) : + m_templateName( templateName ), + m_bnpcBaseId( bnpcBaseId ), + m_bnpcNameId( bnpcNameId ), + m_modelId( modelId ), + m_aiName( aiName ) +{ + +} + +Core::Entity::BattleNpcTemplate::~BattleNpcTemplate() +{ + +} + +uint32_t Core::Entity::BattleNpcTemplate::getBnpcBaseId() const +{ + return m_bnpcBaseId; +} + +uint32_t Core::Entity::BattleNpcTemplate::getBnpcNameId() const +{ + return m_bnpcNameId; +} + +uint32_t Core::Entity::BattleNpcTemplate::getModelId() const +{ + return m_modelId; +} diff --git a/src/servers/Server_Zone/BattleNpcTemplate.h b/src/servers/Server_Zone/BattleNpcTemplate.h new file mode 100644 index 00000000..07e8f5b9 --- /dev/null +++ b/src/servers/Server_Zone/BattleNpcTemplate.h @@ -0,0 +1,38 @@ +#ifndef _BATTLENPCTEMPLATE_H +#define _BATTLENPCTEMPLATE_H + +#include +#include + +namespace Core { +namespace Entity { + + /** + * \brief BattleNpcTemplate - Class which defines a template specific BNpcs can be created from + */ + class BattleNpcTemplate + { + public: + BattleNpcTemplate(); + BattleNpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); + + ~BattleNpcTemplate(); + + uint32_t getBnpcBaseId() const; + uint32_t getBnpcNameId() const; + uint32_t getModelId() const; + + private: + std::string m_templateName; + std::string m_aiName; + uint32_t m_bnpcBaseId = 0; + uint32_t m_bnpcNameId = 0; + uint32_t m_modelId = 0; + + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/CMakeLists.txt b/src/servers/Server_Zone/CMakeLists.txt new file mode 100644 index 00000000..7d4afbce --- /dev/null +++ b/src/servers/Server_Zone/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Sapphire_Zone) + +include_directories("../../libraries/external/ChaiScript-6.0.0/include/") +include_directories("../../libraries/sapphire/datReader/") +include_directories("../") + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") + +set(SERVER_COMMON_DIR ../Server_Common) +set(Boost_USE_STATIC_LIBS ON) + +if(UNIX) + include_directories("/usr/include/mysql/") + message(STATUS "Setting GCC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32") + + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + else() + if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib) + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + else() + message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!") + endif() + endif() +else() + add_definitions(-D_WIN32_WINNT=0x601) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL/") + message(STATUS "Setting MSVC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + message(STATUS "Using boost in /libraries/external") + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + else() + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR})) + set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR}) + set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR}) + else() + message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!") + endif() + endif() +endif() + + +include_directories(${Boost_INCLUDE_DIR}) + +link_directories(${BOOST_LIBRARYDIR}) +link_directories(${SERVER_COMMON_DIR}) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/zlib) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/") + +add_executable(server_zone ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) +add_dependencies(server_zone Common xivdat) + +set_target_properties(server_zone PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../../../bin/" +) + +if (UNIX) + target_link_libraries (server_zone Common xivdat pthread mysqlclient dl z) +else() + target_link_libraries (server_zone Common xivdat libmysql zlib1) +endif() + +target_link_libraries(server_zone ${Boost_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/src/servers/Server_Zone/Cell.cpp b/src/servers/Server_Zone/Cell.cpp new file mode 100644 index 00000000..2d58a389 --- /dev/null +++ b/src/servers/Server_Zone/Cell.cpp @@ -0,0 +1,171 @@ + +#include "Cell.h" + +#include "Actor.h" +#include "Forwards.h" +#include "Zone.h" +#include "BattleNpc.h" + +// TODO: the entire zone / areahandling is a bit outdated ( in parts i used this for the 1.0 iteration ) +// likely this could be greatly improved or redone +namespace Core +{ + + + Cell::Cell() + : m_bActive(false) + , m_bLoaded(false) + , m_playerCount(0) + , m_bUnloadPending(false) + { + m_bForcedActive = false; + } + + Cell::~Cell() + { + removeActors(); + } + + void Cell::init(uint32_t x, uint32_t y, ZonePtr pZone) + { + //Console->outDebOnly("[Region:%X] Initializing a new cell[%i/%i]", pRegion->getId(), x, y ); + m_pZone = pZone; + m_posX = x; + m_posY = y; + + m_actors.clear(); + } + + void Cell::loadActors(CellCache* pCC) + { + + m_bLoaded = true; + assert(pCC); + + for( auto entry : pCC->battleNpcCache ) + { + + entry->setCurrentZone( m_pZone ); + m_pZone->pushActor( entry ); + + } + } + + void Cell::addActor(Entity::ActorPtr pAct) + { + if(pAct->isPlayer()) + { + //Console->outDebOnly("[Region:%X] Adding player %i to cell[%i/%i]", m_pZone->getId(), pAct->getId(), m_posX, m_posY); + ++m_playerCount; + } + + m_actors.insert(pAct); + } + + void Cell::removeActor(Entity::ActorPtr pAct) + { + if(pAct->isPlayer()) + { + //->outDebOnly("[Region:%X] Removing player %i from cell[%i/%i]", m_pZone->getId(), pAct->getId(), m_posX, m_posY); + --m_playerCount; + } + + m_actors.erase(pAct); + } + + void Cell::setActivity(bool state) + { + if(!m_bActive && state) + { + // Move all objects to active set. + for(auto itr = m_actors.begin(); itr != m_actors.end(); ++itr) + { + + } + + if(m_bUnloadPending) + { + cancelPendingUnload(); + } + + } + else if(m_bActive && !state) + { + // Move all objects from active set. + for(auto itr = m_actors.begin(); itr != m_actors.end(); ++itr) + { + + } + + + } + + m_bActive = state; + + } + void Cell::removeActors() + { + //uint32_t ltime = getMSTime(); + + m_actors.clear(); + + //This time it's simpler! We just remove everything + Entity::ActorPtr pAct; //do this outside the loop! + for(auto itr = m_actors.begin(); itr != m_actors.end();) + { + pAct = (*itr); + itr++; + + if(!pAct) + { + continue; + } + + if(m_bUnloadPending) + { + + } + + } + + m_playerCount = 0; + m_bLoaded = false; + } + + + + void Cell::queueUnloadPending() + { + if(m_bUnloadPending) + { + return; + } + + m_bUnloadPending = true; + + } + + void Cell::cancelPendingUnload() + { + + if(!m_bUnloadPending) + { + return; + } + + } + + void Cell::unload() + { + + assert(m_bUnloadPending); + if(m_bActive) + { + return; + } + + removeActors(); + m_bUnloadPending = false; + } + +} \ No newline at end of file diff --git a/src/servers/Server_Zone/Cell.h b/src/servers/Server_Zone/Cell.h new file mode 100644 index 00000000..ede57a6f --- /dev/null +++ b/src/servers/Server_Zone/Cell.h @@ -0,0 +1,128 @@ + +#ifndef _CELL_H +#define _CELL_H + +#include + +#include "Forwards.h" +#include + +namespace Core { + + + struct CellCache + { + std::vector< Entity::BattleNpcPtr > battleNpcCache; + }; + + typedef std::set< Entity::ActorPtr > ActorSet; + + class Cell + { + friend class Zone; + + private: + bool m_bForcedActive; + uint16_t m_posX; + uint16_t m_posY; + ActorSet m_actors; + bool m_bActive; + bool m_bLoaded; + bool m_bUnloadPending; + + uint16_t m_playerCount; + ZonePtr m_pZone; + + public: + Cell(); + ~Cell(); + + void init(uint32_t x, uint32_t y, ZonePtr pZone); + + void addActor(Entity::ActorPtr pAct); + + void removeActor(Entity::ActorPtr pAct); + + void loadActors(CellCache* pCC); + + bool hasActor(Entity::ActorPtr pAct) + { + return (m_actors.find(pAct) != m_actors.end()); + } + + bool hasPlayers() const + { + return ((m_playerCount > 0) ? true : false); + } + + size_t getActorCount() const + { + return m_actors.size(); + } + + void removeActors(); + + ActorSet::iterator begin() + { + return m_actors.begin(); + } + + ActorSet::iterator end() + { + return m_actors.end(); + } + + void setActivity(bool state); + + bool isActive() const + { + return m_bActive; + } + + bool isLoaded() const + { + return m_bLoaded; + } + + uint32_t getPlayerCount() const + { + return m_playerCount; + } + + bool isUnloadPending() const + { + return m_bUnloadPending; + } + + void setUnloadPending(bool up) + { + m_bUnloadPending = up; + } + + void queueUnloadPending(); + void cancelPendingUnload(); + void unload(); + + void setPermanentActivity(bool val) + { + m_bForcedActive = val; + } + + bool isForcedActive() const + { + return m_bForcedActive; + } + + uint16_t getPosX() const + { + return m_posX; + } + + uint16_t getPosY() const + { + return m_posY; + } + }; + +} +#endif diff --git a/src/servers/Server_Zone/CellHandler.h b/src/servers/Server_Zone/CellHandler.h new file mode 100644 index 00000000..253f4823 --- /dev/null +++ b/src/servers/Server_Zone/CellHandler.h @@ -0,0 +1,178 @@ +#ifndef _CELLHANDLER_H +#define _CELLHANDLER_H + +#define TilesCount 32 +#define TileSize 250.0f +#define _minY (-TilesCount*TileSize/2) +#define _minX (-TilesCount*TileSize/2) + +#define _maxY (TilesCount*TileSize/2) +#define _maxX (TilesCount*TileSize/2) + +#define CellsPerTile 4 +#define _cellSize (TileSize/CellsPerTile) +#define _sizeX (TilesCount*CellsPerTile) +#define _sizeY (TilesCount*CellsPerTile) + +#define GetRelatCoord(Coord,CellCoord) ((_maxX-Coord)-CellCoord*_cellSize) +namespace Core { + +class Zone; + +template +class CellHandler +{ +public: + CellHandler(); + ~CellHandler(); + + T* getCell( uint32_t x, uint32_t y ); + T* getCellByCoords( float x, float y ); + T* create( uint32_t x, uint32_t y ); + T* createByCoords( float x, float y ); + void remove( uint32_t x, uint32_t y ); + + bool allocated( uint32_t x, uint32_t y ) + { + return m_pCells[x][y] != nullptr; + } + + static uint32_t getPosX( float x ); + static uint32_t getPosY( float y ); + +protected: + void _init(); + + + T *** m_pCells; + +}; + +template +CellHandler::CellHandler() +{ + + _init(); +} + + +template +void CellHandler::_init() +{ + m_pCells = new T**[_sizeX]; + + assert( m_pCells ); + for( uint32_t i = 0; i < _sizeX; i++ ) + { + m_pCells[i] = nullptr; + } + +} + +template +CellHandler::~CellHandler() +{ + if( m_pCells ) + { + for( uint32_t i = 0; i < _sizeX; i++ ) + { + if( !m_pCells[i] ) + { + continue; + } + + for( uint32_t j = 0; j < _sizeY; j++ ) + { + if( m_pCells[i][j] ) + { + delete m_pCells[i][j]; + } + } + delete[] m_pCells[i]; + } + delete[] m_pCells; + } +} + +template +T* CellHandler::create( uint32_t x, uint32_t y ) +{ + if( x >= _sizeX || y >= _sizeY ) + { + return nullptr; + } + + if( !m_pCells[x] ) + { + m_pCells[x] = new T*[_sizeY]; + memset( m_pCells[x], 0, sizeof( T* )*_sizeY ); + } + + assert( m_pCells[x][y] == nullptr ); + + T *cls = new T; + m_pCells[x][y] = cls; + + return cls; +} + +template +T* CellHandler::createByCoords( float x, float y ) +{ + return create( getPosX( x ), getPosY( y ) ); +} + +template +void CellHandler::remove( uint32_t x, uint32_t y ) +{ + if( x >= _sizeX || y >= _sizeY ) + { + return; + } + + if( !m_pCells[x] ) + { + return; + } + + assert( m_pCells[x][y] != nullptr ); + + T *cls = m_pCells[x][y]; + m_pCells[x][y] = nullptr; + + delete cls; +} + +template +T* CellHandler::getCell( uint32_t x, uint32_t y ) +{ + if( !m_pCells[x] ) + { + return nullptr; + } + + return m_pCells[x][y]; +} + +template +T* CellHandler::getCellByCoords( float x, float y ) +{ + return getCell( getPosX( x ), getPosY( y ) ); +} + +template +uint32_t CellHandler::getPosX( float x ) +{ + assert( ( x >= _minX ) && ( x <= _maxX ) ); + return ( uint32_t ) ( ( _maxX - x ) / _cellSize ); +} + +template +uint32_t CellHandler::getPosY( float y ) +{ + assert( ( y >= _minY ) && ( y <= _maxY ) ); + return ( uint32_t ) ( ( _maxY - y ) / _cellSize ); +} + +} +#endif diff --git a/src/servers/Server_Zone/CharaPc.cpp b/src/servers/Server_Zone/CharaPc.cpp new file mode 100644 index 00000000..f93462ad --- /dev/null +++ b/src/servers/Server_Zone/CharaPc.cpp @@ -0,0 +1,561 @@ +#include "CharaPc.h" + +uint8_t Core::Entity::CharaDetail::getClass() const +{ + return m_class; +} + +void Core::Entity::CharaDetail::setClass( uint8_t class_ ) +{ + m_class = class_; +} + +bool Core::Entity::CharaDetail::getBIsNewGame() const +{ + return m_bIsNewGame; +} + +void Core::Entity::CharaDetail::setBIsNewGame( bool isNewGame ) +{ + m_bIsNewGame = isNewGame; +} + +uint8_t Core::Entity::CharaDetail::getMakeValid() const +{ + return m_makeValid; +} + +void Core::Entity::CharaDetail::setMakeValid( uint8_t makeValid ) +{ + m_makeValid = makeValid; +} + +uint8_t Core::Entity::CharaDetail::getLegacyEmployEnable() const +{ + return m_legacyEmployEnable; +} + +void Core::Entity::CharaDetail::setLegacyEmployEnable( uint8_t legacyEmployEnable ) +{ + m_legacyEmployEnable = legacyEmployEnable; +} + +uint8_t Core::Entity::CharaDetail::getLegacyCompleteFlag() const +{ + return m_legacyCompleteFlag; +} + +void Core::Entity::CharaDetail::setLegacyCompleteFlag( uint8_t legacyCompleteFlag ) +{ + m_legacyCompleteFlag = legacyCompleteFlag; +} + +uint32_t Core::Entity::CharaDetail::getRemakeFlag() const +{ + return m_remakeFlag; +} + +void Core::Entity::CharaDetail::setRemakeFlag( uint32_t remakeFlag ) +{ + m_remakeFlag = remakeFlag; +} + +uint32_t Core::Entity::CharaDetail::getTotalPlayTime() const +{ + return m_totalPlayTime; +} + +void Core::Entity::CharaDetail::setTotalPlayTime( uint32_t totalPlayTime ) +{ + m_totalPlayTime = totalPlayTime; +} + +float Core::Entity::CharaDetail::getTotalPlayTimeSecond() const +{ + return m_totalPlayTimeSecond; +} + +void Core::Entity::CharaDetail::setTotalPlayTimeSecond( float totalPlayTimeSecond ) +{ + m_totalPlayTimeSecond = totalPlayTimeSecond; +} + +uint8_t Core::Entity::CharaDetail::getInvisible() const +{ + return m_invisible; +} + +void Core::Entity::CharaDetail::setInvisible( uint8_t invisible ) +{ + m_invisible = invisible; +} + +uint8_t Core::Entity::CharaDetail::getFirstClass() const +{ + return m_firstClass; +} + +void Core::Entity::CharaDetail::setFirstClass( uint8_t firstClass ) +{ + m_firstClass = firstClass; +} + +uint8_t Core::Entity::CharaDetail::getHomePoint() const +{ + return m_homePoint; +} + +void Core::Entity::CharaDetail::setHomePoint( uint8_t homePoint ) +{ + m_homePoint = homePoint; +} + +uint32_t Core::Entity::CharaDetail::getRestPoint() const +{ + return m_restPoint; +} + +void Core::Entity::CharaDetail::setRestPoint( uint32_t restPoint ) +{ + m_restPoint = restPoint; +} + +float Core::Entity::CharaDetail::getRentalTimer() const +{ + return m_rentalTimer; +} + +void Core::Entity::CharaDetail::setRentalTimer( float rentalTimer ) +{ + m_rentalTimer = rentalTimer; +} + +float Core::Entity::CharaDetail::getBuddyUpdateTimer() const +{ + return m_buddyUpdateTimer; +} + +void Core::Entity::CharaDetail::setBuddyUpdateTimer( float buddyUpdateTimer ) +{ + m_buddyUpdateTimer = buddyUpdateTimer; +} + +float Core::Entity::CharaDetail::getBuddyTimer() const +{ + return m_buddyTimer; +} + +void Core::Entity::CharaDetail::setBuddyTimer( float buddyTimer ) +{ + m_buddyTimer = buddyTimer; +} + +uint8_t Core::Entity::CharaDetail::getBuddyRank() const +{ + return m_buddyRank; +} + +void Core::Entity::CharaDetail::setBuddyRank( uint8_t buddyRank ) +{ + m_buddyRank = buddyRank; +} + +uint8_t Core::Entity::CharaDetail::getBuddyAdditionSkillPoint() const +{ + return m_buddyAdditionSkillPoint; +} + +void Core::Entity::CharaDetail::setBuddyAdditionSkillPoint( uint8_t buddyAdditionSkillPoint ) +{ + m_buddyAdditionSkillPoint = buddyAdditionSkillPoint; +} + +uint32_t Core::Entity::CharaDetail::getBuddyExp() const +{ + return m_buddyExp; +} + +void Core::Entity::CharaDetail::setBuddyExp( uint32_t buddyExp ) +{ + m_buddyExp = buddyExp; +} + +uint32_t Core::Entity::CharaDetail::getBuddyCommand() const +{ + return m_buddyCommand; +} + +void Core::Entity::CharaDetail::setBuddyCommand( uint32_t buddyCommand ) +{ + m_buddyCommand = buddyCommand; +} + +uint32_t Core::Entity::CharaDetail::getBuddyHp() const +{ + return m_buddyHp; +} + +void Core::Entity::CharaDetail::setBuddyHp( uint32_t buddyHp ) +{ + m_buddyHp = buddyHp; +} + +uint8_t Core::Entity::CharaDetail::getGuardianDeity() const +{ + return m_guardianDeity; +} + +void Core::Entity::CharaDetail::setGuardianDeity( uint8_t guardianDeity ) +{ + m_guardianDeity = guardianDeity; +} + +uint8_t Core::Entity::CharaDetail::getBirthMonth() const +{ + return m_birthMonth; +} + +void Core::Entity::CharaDetail::setBirthMonth( uint8_t birthMonth ) +{ + m_birthMonth = birthMonth; +} + +uint8_t Core::Entity::CharaDetail::getBirthday() const +{ + return m_birthday; +} + +void Core::Entity::CharaDetail::setBirthday( uint8_t birthday ) +{ + m_birthday = birthday; +} + +uint8_t Core::Entity::CharaDetail::getStartTown() const +{ + return m_startTown; +} + +void Core::Entity::CharaDetail::setStartTown( uint8_t startTown ) +{ + m_startTown = startTown; +} + +uint16_t Core::Entity::CharaDetail::getActiveTitle() const +{ + return m_activeTitle; +} + +void Core::Entity::CharaDetail::setActiveTitle( uint16_t activeTitle ) +{ + m_activeTitle = activeTitle; +} + +uint16_t Core::Entity::CharaDetail::getEquippedMannequin() const +{ + return m_equippedMannequin; +} + +void Core::Entity::CharaDetail::setEquippedMannequin( uint16_t equippedMannequin ) +{ + m_equippedMannequin = equippedMannequin; +} + +uint32_t Core::Entity::CharaDetail::getTomeStoneCounter() const +{ + return m_tomeStoneCounter; +} + +void Core::Entity::CharaDetail::setTomeStoneCounter( uint32_t tomeStoneCounter ) +{ + m_tomeStoneCounter = tomeStoneCounter; +} + +float Core::Entity::CharaDetail::getTomeStomeTimer() const +{ + return m_tomeStomeTimer; +} + +void Core::Entity::CharaDetail::setTomeStomeTimer( float tomeStomeTimer ) +{ + m_tomeStomeTimer = tomeStomeTimer; +} + +uint16_t Core::Entity::CharaDetail::getConfigFlags() const +{ + return m_configFlags; +} + +void Core::Entity::CharaDetail::setConfigFlags( uint16_t configFlags ) +{ + m_configFlags = configFlags; +} + +uint8_t Core::Entity::CharaDetail::getGmIcon() const +{ + return m_gmIcon; +} + +void Core::Entity::CharaDetail::setGmIcon( uint8_t gmIcon ) +{ + m_gmIcon = gmIcon; +} + +uint8_t Core::Entity::CharaDetail::getGmBind() const +{ + return m_gmBind; +} + +void Core::Entity::CharaDetail::setGmBind( uint8_t gmBind ) +{ + m_gmBind = gmBind; +} + +uint32_t Core::Entity::CharaDetail::getCatchCount() const +{ + return m_catchCount; +} + +void Core::Entity::CharaDetail::setCatchCount( uint32_t catchCount ) +{ + m_catchCount = catchCount; +} + +uint32_t Core::Entity::CharaDetail::getUseBaitCatalogId() const +{ + return m_useBaitCatalogId; +} + +void Core::Entity::CharaDetail::setUseBaitCatalogId( uint32_t useBaitCatalogId ) +{ + m_useBaitCatalogId = useBaitCatalogId; +} + +uint8_t Core::Entity::CharaDetail::getOpeningSequence() const +{ + return m_openingSequence; +} + +void Core::Entity::CharaDetail::setOpeningSequence( uint8_t openingSequence ) +{ + m_openingSequence = openingSequence; +} + +uint8_t Core::Entity::CharaDetail::getLeveTicketNum() const +{ + return m_leveTicketNum; +} + +void Core::Entity::CharaDetail::setLeveTicketNum( uint8_t leveTicketNum ) +{ + m_leveTicketNum = leveTicketNum; +} + +uint32_t Core::Entity::CharaDetail::getLeveTicketLastGetTime() const +{ + return m_leveTicketLastGetTime; +} + +void Core::Entity::CharaDetail::setLeveTicketLastGetTime( uint32_t leveTicketLastGetTime ) +{ + m_leveTicketLastGetTime = leveTicketLastGetTime; +} + +uint16_t Core::Entity::CharaDetail::getLeveAssignmentSeed() const +{ + return m_leveAssignmentSeed; +} + +void Core::Entity::CharaDetail::setLeveAssignmentSeed( uint16_t leveAssignmentSeed ) +{ + m_leveAssignmentSeed = leveAssignmentSeed; +} + +uint8_t Core::Entity::CharaDetail::getLeveAssignmentCount() const +{ + return m_leveAssignmentCount; +} + +void Core::Entity::CharaDetail::setLeveAssignmentCount( uint8_t leveAssignmentCount ) +{ + m_leveAssignmentCount = leveAssignmentCount; +} + +uint16_t Core::Entity::CharaDetail::getGuildleveFactionCreditBr() const +{ + return m_guildleveFactionCreditBr; +} + +void Core::Entity::CharaDetail::setGuildleveFactionCreditBr( uint16_t guildleveFactionCreditBr ) +{ + m_guildleveFactionCreditBr = guildleveFactionCreditBr; +} + +uint16_t Core::Entity::CharaDetail::getGuildleveFactionCreditAz() const +{ + return m_guildleveFactionCreditAz; +} + +void Core::Entity::CharaDetail::setGuildleveFactionCreditAz( uint16_t guildleveFactionCreditAz ) +{ + m_guildleveFactionCreditAz = guildleveFactionCreditAz; +} + +uint16_t Core::Entity::CharaDetail::getGuildleveFactionCreditHo() const +{ + return m_guildleveFactionCreditHo; +} + +void Core::Entity::CharaDetail::setGuildleveFactionCreditHo( uint16_t guildleveFactionCreditHo ) +{ + m_guildleveFactionCreditHo = guildleveFactionCreditHo; +} + +uint8_t Core::Entity::CharaDetail::getGrandCompany() const +{ + return m_grandCompany; +} + +void Core::Entity::CharaDetail::setGrandCompany( uint8_t grandCompany ) +{ + m_grandCompany = grandCompany; +} + +uint8_t Core::Entity::CharaDetail::getGearsetMaxNum() const +{ + return m_gearsetMaxNum; +} + +void Core::Entity::CharaDetail::setGearsetMaxNum( uint8_t gearsetMaxNum ) +{ + m_gearsetMaxNum = gearsetMaxNum; +} + +uint32_t Core::Entity::CharaDetail::getContentJoinTime() const +{ + return m_contentJoinTime; +} + +void Core::Entity::CharaDetail::setContentJoinTime( uint32_t contentJoinTime ) +{ + m_contentJoinTime = contentJoinTime; +} + +uint32_t Core::Entity::CharaDetail::getPathId() const +{ + return m_pathId; +} + +void Core::Entity::CharaDetail::setPathId( uint32_t pathId ) +{ + m_pathId = pathId; +} + +uint16_t Core::Entity::CharaDetail::getStepIndex() const +{ + return m_stepIndex; +} + +void Core::Entity::CharaDetail::setStepIndex( uint16_t stepIndex ) +{ + m_stepIndex = stepIndex; +} + +uint32_t Core::Entity::CharaDetail::getPathIdBackUp() const +{ + return m_pathIdBackUp; +} + +void Core::Entity::CharaDetail::setPathIdBackUp( uint32_t pathIdBackUp ) +{ + m_pathIdBackUp = pathIdBackUp; +} + +uint16_t Core::Entity::CharaDetail::getStepIndexBackUp() const +{ + return m_stepIndexBackUp; +} + +void Core::Entity::CharaDetail::setStepIndexBackUp( uint16_t stepIndexBackUp ) +{ + m_stepIndexBackUp = stepIndexBackUp; +} + +uint8_t Core::Entity::CharaDetail::getGcSupplySeed() const +{ + return m_gcSupplySeed; +} + +void Core::Entity::CharaDetail::setGcSupplySeed( uint8_t gcSupplySeed ) +{ + m_gcSupplySeed = gcSupplySeed; +} + +uint32_t Core::Entity::CharaDetail::getGcSupplyTime() const +{ + return m_gcSupplyTime; +} + +void Core::Entity::CharaDetail::setGcSupplyTime( uint32_t gcSupplyTime ) +{ + m_gcSupplyTime = gcSupplyTime; +} + +uint32_t Core::Entity::CharaDetail::getLayerSet() const +{ + return m_layerSet; +} + +void Core::Entity::CharaDetail::setLayerSet( uint32_t layerSet ) +{ + m_layerSet = layerSet; +} + +uint16_t Core::Entity::CharaDetail::getGcChangeTime() const +{ + return m_gcChangeTime; +} + +void Core::Entity::CharaDetail::setGcChangeTime( uint16_t gcChangeTime ) +{ + m_gcChangeTime = gcChangeTime; +} + +uint8_t Core::Entity::CharaDetail::getFateFlag() const +{ + return m_fateFlag; +} + +void Core::Entity::CharaDetail::setFateFlag( uint8_t fateFlag ) +{ + m_fateFlag = fateFlag; +} + +uint8_t Core::Entity::CharaDetail::getContentRaidCounter() const +{ + return m_contentRaidCounter; +} + +void Core::Entity::CharaDetail::setContentRaidCounter( uint8_t contentRaidCounter ) +{ + m_contentRaidCounter = contentRaidCounter; +} + +uint16_t Core::Entity::CharaDetail::getContentRaidTimer() const +{ + return m_contentRaidTimer; +} + +void Core::Entity::CharaDetail::setContentRaidTimer( uint16_t contentRaidTimer ) +{ + m_contentRaidTimer = contentRaidTimer; +} + +uint64_t Core::Entity::CharaDetail::getId() const +{ + return m_id; +} + +void Core::Entity::CharaDetail::setId( uint64_t id ) +{ + m_id = id; +} diff --git a/src/servers/Server_Zone/CharaPc.h b/src/servers/Server_Zone/CharaPc.h new file mode 100644 index 00000000..cbf013f1 --- /dev/null +++ b/src/servers/Server_Zone/CharaPc.h @@ -0,0 +1,299 @@ +#ifndef _CHARA_H_ +#define _CHARA_H_ + +#include +#include + +#include "Forwards.h" +#include +#include + +namespace Core +{ + namespace Entity + { + + class CharaDetail + { + + private: + + // CHARAPC data ------------------------------------------------------------------------------------ + uint8_t m_class; + bool m_bIsNewGame; + uint8_t m_makeValid; + + struct RetainerInfo + { + uint32_t retainerId; + char retainerName[32]; + uint32_t createUnixTime; + bool isActive; + bool isRename; + uint8_t status; + } m_retainerInfo[8]; + + uint8_t m_legacyEmployEnable; + char m_platform[32]; + uint8_t m_legacyCompleteFlag; + uint32_t m_remakeFlag; + uint32_t m_totalPlayTime; + float m_totalPlayTimeSecond; + + uint8_t m_invisible; + uint8_t m_firstClass; + uint8_t m_homePoint; + uint8_t m_favoritePoint[3]; + + uint32_t m_restPoint; + float m_rentalTimer; + float m_buddyUpdateTimer; + float m_buddyTimer; + char m_buddyName[21]; + uint8_t m_possession[8]; + uint8_t m_buddyEquip[3]; + uint8_t m_buddyRank; + uint8_t m_buddyAdditionSkillPoint; + uint8_t m_buddySkillLine[3]; + uint32_t m_buddyExp; + uint32_t m_buddyCommand; + uint32_t m_buddyHp; + + uint8_t m_guardianDeity; + uint8_t m_birthMonth; + uint8_t m_birthday; + uint8_t m_startTown; + uint8_t m_mountList[11]; + uint16_t m_activeTitle; + uint8_t m_titleList[32]; + uint8_t m_reward[16]; + + struct HuntingLogInfo + { + uint8_t killCount[40]; + uint64_t completeFlags; + uint8_t currentRank; + uint32_t isNewFlags; + } m_huntingLogInfo[10]; + + uint8_t m_aetheryteList[12]; + + uint8_t m_howTo[32]; + uint8_t m_minions[32]; + uint8_t m_cutScene[64]; + uint16_t m_equippedMannequin; + uint32_t m_tomeStoneCounter; + float m_tomeStomeTimer; + + uint16_t m_configFlags; + uint8_t m_gmIcon; + uint8_t m_gmBind; + + uint32_t m_catchCount; + uint8_t m_isFishCaught[50]; + uint8_t m_isSpotVisited[25]; + uint8_t m_lastFishSize[36]; + uint8_t m_lastFishId[36]; + uint32_t m_useBaitCatalogId; + + uint8_t m_gatheringHistoryPointId[8]; + uint8_t m_gatheringDivisionOpenFlags[40]; + uint8_t m_gatheringItemGetFlags[60]; + + uint8_t m_recipeDevisionOpenFlags[80]; + uint8_t m_recipeCreateFlags[250]; + + uint8_t m_questCompleteFlags[200]; + uint8_t m_legacyQuestCompleteFlags[40]; + uint8_t m_legacyCompleteFlags[200]; + + struct LeveInfo + { + uint8_t leves[28]; + } m_leveInfo[16]; + + uint8_t m_openingSequence; + + // I really do not know what this is for + //`Type` TINYINT( 3 ) UNSIGNED, + //`Index` TINYINT( 3 ) UNSIGNED, + //Type_0 TINYINT( 3 ) UNSIGNED, + //Index_0 TINYINT( 3 ) UNSIGNED, + //Type_1 TINYINT( 3 ) UNSIGNED, + //Index_1 TINYINT( 3 ) UNSIGNED, + //Type_2 TINYINT( 3 ) UNSIGNED, + //Index_2 TINYINT( 3 ) UNSIGNED, + //Type_3 TINYINT( 3 ) UNSIGNED, + //Index_3 TINYINT( 3 ) UNSIGNED, + + uint8_t m_leveTicketNum; + uint32_t m_leveTicketLastGetTime; + uint16_t m_leveAssignmentSeed; + uint8_t m_leveAssignmentCount; + + uint16_t m_guildleveFactionCreditBr; + uint16_t m_guildleveFactionCreditAz; + uint16_t m_guildleveFactionCreditHo; + + uint8_t m_grandCompany; + uint8_t m_grandCompanyRank[3]; + uint8_t m_gearsetMaxNum; + + uint8_t m_discoveryWork[414]; + + uint32_t m_contentJoinTime; + uint8_t m_contentRaidClearFlags[28]; + uint8_t m_contentDungeonClearFlags[18]; + uint8_t m_contentGuildOrderClearFlags[10]; + uint8_t m_contentBossBattleClearFlags[6]; + uint8_t m_contentColosseumClearFlags[2]; + uint8_t m_contentRaidAttainFlags[28]; + uint8_t m_contentDungeonAttainFlag[18]; + uint8_t m_contentGuildOrderAttainFlags[10]; + uint8_t m_contentBossBattleAttainFlags[6]; + uint8_t m_contentColosseumAttainFlags[2]; + + // related to currently active choco taxi?? + uint32_t m_pathId; + uint16_t m_stepIndex; + uint32_t m_pathIdBackUp; + + uint16_t m_stepIndexBackUp; + + uint8_t m_chocoboTaxiStandFlags[8]; + uint8_t m_companion[16]; + + uint8_t m_cabinet[32]; + uint8_t m_contentFinderPenalties[2]; + uint8_t m_gcSupplyItemFlags[4]; + uint8_t m_gcSupplySeed; + uint32_t m_gcSupplyTime; + uint8_t m_gcSupplyClassLevel[11]; + + uint32_t m_layerSet; + uint8_t m_trophyAcquisitionFlags[8]; + uint16_t m_gcChangeTime; + uint8_t m_fateFlag; + uint8_t m_contentRaidCounter; + uint16_t m_contentRaidTimer; + + uint64_t m_id; + public: + uint8_t getClass() const; + void setClass( uint8_t class_ ); + bool getBIsNewGame() const; + void setBIsNewGame( bool isNewGame ); + uint8_t getMakeValid() const; + void setMakeValid( uint8_t makeValid ); + uint8_t getLegacyEmployEnable() const; + void setLegacyEmployEnable( uint8_t legacyEmployEnable ); + uint8_t getLegacyCompleteFlag() const; + void setLegacyCompleteFlag( uint8_t legacyCompleteFlag ); + uint32_t getRemakeFlag() const; + void setRemakeFlag( uint32_t remakeFlag ); + uint32_t getTotalPlayTime() const; + void setTotalPlayTime( uint32_t totalPlayTime ); + float getTotalPlayTimeSecond() const; + void setTotalPlayTimeSecond( float totalPlayTimeSecond ); + uint8_t getInvisible() const; + void setInvisible( uint8_t invisible ); + uint8_t getFirstClass() const; + void setFirstClass( uint8_t firstClass ); + uint8_t getHomePoint() const; + void setHomePoint( uint8_t homePoint ); + uint32_t getRestPoint() const; + void setRestPoint( uint32_t restPoint ); + float getRentalTimer() const; + void setRentalTimer( float rentalTimer ); + float getBuddyUpdateTimer() const; + void setBuddyUpdateTimer( float buddyUpdateTimer ); + float getBuddyTimer() const; + void setBuddyTimer( float buddyTimer ); + uint8_t getBuddyRank() const; + void setBuddyRank( uint8_t buddyRank ); + uint8_t getBuddyAdditionSkillPoint() const; + void setBuddyAdditionSkillPoint( uint8_t buddyAdditionSkillPoint ); + uint32_t getBuddyExp() const; + void setBuddyExp( uint32_t buddyExp ); + uint32_t getBuddyCommand() const; + void setBuddyCommand( uint32_t buddyCommand ); + uint32_t getBuddyHp() const; + void setBuddyHp( uint32_t buddyHp ); + uint8_t getGuardianDeity() const; + void setGuardianDeity( uint8_t guardianDeity ); + uint8_t getBirthMonth() const; + void setBirthMonth( uint8_t birthMonth ); + uint8_t getBirthday() const; + void setBirthday( uint8_t birthday ); + uint8_t getStartTown() const; + void setStartTown( uint8_t startTown ); + uint16_t getActiveTitle() const; + void setActiveTitle( uint16_t activeTitle ); + uint16_t getEquippedMannequin() const; + void setEquippedMannequin( uint16_t equippedMannequin ); + uint32_t getTomeStoneCounter() const; + void setTomeStoneCounter( uint32_t tomeStoneCounter ); + float getTomeStomeTimer() const; + void setTomeStomeTimer( float tomeStomeTimer ); + uint16_t getConfigFlags() const; + void setConfigFlags( uint16_t configFlags ); + uint8_t getGmIcon() const; + void setGmIcon( uint8_t gmIcon ); + uint8_t getGmBind() const; + void setGmBind( uint8_t gmBind ); + uint32_t getCatchCount() const; + void setCatchCount( uint32_t catchCount ); + uint32_t getUseBaitCatalogId() const; + void setUseBaitCatalogId( uint32_t useBaitCatalogId ); + uint8_t getOpeningSequence() const; + void setOpeningSequence( uint8_t openingSequence ); + uint8_t getLeveTicketNum() const; + void setLeveTicketNum( uint8_t leveTicketNum ); + uint32_t getLeveTicketLastGetTime() const; + void setLeveTicketLastGetTime( uint32_t leveTicketLastGetTime ); + uint16_t getLeveAssignmentSeed() const; + void setLeveAssignmentSeed( uint16_t leveAssignmentSeed ); + uint8_t getLeveAssignmentCount() const; + void setLeveAssignmentCount( uint8_t leveAssignmentCount ); + uint16_t getGuildleveFactionCreditBr() const; + void setGuildleveFactionCreditBr( uint16_t guildleveFactionCreditBr ); + uint16_t getGuildleveFactionCreditAz() const; + void setGuildleveFactionCreditAz( uint16_t guildleveFactionCreditAz ); + uint16_t getGuildleveFactionCreditHo() const; + void setGuildleveFactionCreditHo( uint16_t guildleveFactionCreditHo ); + uint8_t getGrandCompany() const; + void setGrandCompany( uint8_t grandCompany ); + uint8_t getGearsetMaxNum() const; + void setGearsetMaxNum( uint8_t gearsetMaxNum ); + uint32_t getContentJoinTime() const; + void setContentJoinTime( uint32_t contentJoinTime ); + uint32_t getPathId() const; + void setPathId( uint32_t pathId ); + uint16_t getStepIndex() const; + void setStepIndex( uint16_t stepIndex ); + uint32_t getPathIdBackUp() const; + void setPathIdBackUp( uint32_t pathIdBackUp ); + uint16_t getStepIndexBackUp() const; + void setStepIndexBackUp( uint16_t stepIndexBackUp ); + uint8_t getGcSupplySeed() const; + void setGcSupplySeed( uint8_t gcSupplySeed ); + uint32_t getGcSupplyTime() const; + void setGcSupplyTime( uint32_t gcSupplyTime ); + uint32_t getLayerSet() const; + void setLayerSet( uint32_t layerSet ); + uint16_t getGcChangeTime() const; + void setGcChangeTime( uint16_t gcChangeTime ); + uint8_t getFateFlag() const; + void setFateFlag( uint8_t fateFlag ); + uint8_t getContentRaidCounter() const; + void setContentRaidCounter( uint8_t contentRaidCounter ); + uint16_t getContentRaidTimer() const; + void setContentRaidTimer( uint16_t contentRaidTimer ); + uint64_t getId() const; + void setId( uint64_t id ); + + // CHARAPC data END ------------------------------------------------------------------------------------ + }; + } +} +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/ChatPacket.h b/src/servers/Server_Zone/ChatPacket.h new file mode 100644 index 00000000..a1353281 --- /dev/null +++ b/src/servers/Server_Zone/ChatPacket.h @@ -0,0 +1,41 @@ +#ifndef _CHATPACKET_H +#define _CHATPACKET_H + +#include +#include +#include "Forwards.h" + + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Chat packet. +*/ +class ChatPacket : + public GamePacketNew< FFXIVIpcChat > +{ +public: + ChatPacket( Entity::PlayerPtr player, Common::ChatType chatType, const std::string& msg ) : + GamePacketNew( player->getId(), player->getId() ) + { + initialize( player, chatType, msg ); + }; + +private: + void initialize( Entity::PlayerPtr player, Common::ChatType chatType, const std::string& msg ) + { + m_data.chatType = chatType; + strcpy( m_data.name, player->getName().c_str() ); + strcpy( m_data.msg, msg.c_str() ); + }; +}; + +} +} +} +} + +#endif /*_CHATPACKET_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/Event.cpp b/src/servers/Server_Zone/Event.cpp new file mode 100644 index 00000000..272084b3 --- /dev/null +++ b/src/servers/Server_Zone/Event.cpp @@ -0,0 +1,73 @@ +#include "Event.h" + +Core::Event::Event::Event( uint64_t actorId, uint32_t eventId, uint8_t eventType, uint8_t eventParam2, uint32_t eventParam3 ) + : m_actorId( actorId ), + m_eventId( eventId ), + m_playedScene( false ) +{ + + m_param1 = static_cast< uint16_t >( eventId ); + m_param2 = static_cast< uint16_t >( eventId >> 16 ); + + m_eventType = eventType; + m_eventParam2 = eventParam2; + m_eventParam3 = eventParam3; + + m_callback = nullptr; +} + +uint64_t Core::Event::Event::getActorId() const +{ + return m_actorId; +} + +uint32_t Core::Event::Event::getId() const +{ + return m_eventId; +} + +uint32_t Core::Event::Event::getParam1() const +{ + return m_param1; +} + +uint16_t Core::Event::Event::getParam2() const +{ + return m_param2; +} + +uint8_t Core::Event::Event::getEventType() const +{ + return m_eventType; +} + +uint32_t Core::Event::Event::getEventParam2() const +{ + return m_eventParam2; +} + +uint32_t Core::Event::Event::getEventParam3() const +{ + return m_eventParam3; +} + +Core::Scripting::EventReturnCallback Core::Event::Event::getEventReturnCallback() const +{ + return m_callback; +} + +void Core::Event::Event::setEventReturnCallback( Core::Scripting::EventReturnCallback callback ) +{ + m_callback = callback; +} + + +bool Core::Event::Event::hasPlayedScene() const +{ + return m_playedScene; +} + +void Core::Event::Event::setPlayedScene( bool playedScene ) +{ + m_playedScene = playedScene; +} diff --git a/src/servers/Server_Zone/Event.h b/src/servers/Server_Zone/Event.h new file mode 100644 index 00000000..184d4d71 --- /dev/null +++ b/src/servers/Server_Zone/Event.h @@ -0,0 +1,78 @@ +#ifndef _EVENT_H +#define _EVENT_H + +#include "Forwards.h" + +namespace Core { + namespace Event { + + class Event + { + public: + Event( uint64_t actorId, uint32_t eventId, uint8_t eventType, uint8_t eventParam2, uint32_t eventParam3 ); + + ~Event() {} + + uint64_t getActorId() const; + + uint32_t getId() const; + + uint32_t getParam1() const; + + uint16_t getParam2() const; + + uint8_t getEventType() const; + + uint32_t getEventParam2() const; + + uint32_t getEventParam3() const; + + bool hasPlayedScene() const; + + void setPlayedScene( bool playedScene ); + + Scripting::EventReturnCallback getEventReturnCallback() const; + + void setEventReturnCallback( Scripting::EventReturnCallback callback ); + + enum EventType : uint8_t + { + Talk = 1, + Emote = 2, + DistanceBelow = 3, + DistanceOver = 4, + BattleReward = 5, + Craft = 6, + Nest = 7, + Item = 8, + Drop = 9, + WithinRange = 10, + OutsideRange = 11, + GameStart = 12, + GameProgress = 13, + EnterTerritory = 15, + GameComeBack = 17, + ActionResult = 18, + MateriaCraft = 19, + Fishing = 20, + UI = 21, + Housing = 22, + Say = 23, + TableGame = 24, + }; + + protected: + uint64_t m_actorId; + uint32_t m_eventId; + uint32_t m_param1; + uint16_t m_param2; + uint8_t m_eventType; + uint8_t m_eventParam2; + uint32_t m_eventParam3; + bool m_playedScene; + Scripting::EventReturnCallback m_callback; + }; + + } +} +#endif diff --git a/src/servers/Server_Zone/EventAction.cpp b/src/servers/Server_Zone/EventAction.cpp new file mode 100644 index 00000000..7f4b427b --- /dev/null +++ b/src/servers/Server_Zone/EventAction.cpp @@ -0,0 +1,141 @@ +#include +#include +#include + +#include "EventAction.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "Player.h" +#include "Event.h" + +extern Core::Logger g_log; +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; +using namespace Core::Network; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Action::EventAction::EventAction() +{ + m_actionType = Common::ActionType::Event; +} + +Core::Action::EventAction::EventAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, + ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ) +{ + m_additional = additional; + m_actionType = ActionType::Event; + m_eventId = eventId; + m_id = action; + m_castTime = g_exdData.m_EventActionInfoMap[action].castTime; // TODO: Add security checks. + m_onActionFinishClb = finishRef; + m_onActionInterruptClb = interruptRef; + m_pSource = pActor; + m_bInterrupt = false; +} + +Core::Action::EventAction::~EventAction() +{ + +} + +void Core::Action::EventAction::onStart() +{ + if( !m_pSource ) + return; + + m_startTime = Util::getTimeMs(); + + auto control = ActorControlPacket142( m_pSource->getId(), Common::ActorControlType::CastStart, + 1, m_id, 0x4000002F ); + + if( m_pSource->isPlayer() ) + { + m_pSource->sendToInRangeSet( control, true ); + m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::NoCombat ); + m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Occupied1 ); + m_pSource->getAsPlayer()->sendStateFlags(); + } + else + m_pSource->sendToInRangeSet( control ); +} + +void Core::Action::EventAction::onFinish() +{ + if( !m_pSource ) + return; + + try + { + auto pEvent = m_pSource->getAsPlayer()->getEvent( m_eventId ); + + pEvent->setPlayedScene( false ); + + if( m_onActionFinishClb ) + m_onActionFinishClb( *m_pSource->getAsPlayer(), m_eventId, m_additional ); + + auto control = ActorControlPacket142( m_pSource->getId(), Common::ActorControlType::CastStart, 0, m_id ); + + + if( !pEvent->hasPlayedScene() ) + m_pSource->getAsPlayer()->eventFinish( m_eventId, 1 ); + else + pEvent->setPlayedScene( false ); + + if( m_pSource->isPlayer() ) + { + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::NoCombat ); + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + m_pSource->getAsPlayer()->sendStateFlags(); + m_pSource->sendToInRangeSet( control, true ); + } + else + m_pSource->sendToInRangeSet( control ); + } + catch( std::exception& e ) + { + g_log.error( e.what() ); + } + +} + +void Core::Action::EventAction::onInterrupt() +{ + if( !m_pSource ) + return; + + try + { + + auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, + 0x219, 0x04, m_id ); + + if( m_pSource->isPlayer() ) + { + auto control1 = ActorControlPacket143( m_pSource->getId(), ActorControlType::FreeEventPos, m_eventId ); + + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::NoCombat ); + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 ); + m_pSource->getAsPlayer()->sendStateFlags(); + m_pSource->sendToInRangeSet( control ); + m_pSource->sendToInRangeSet( control1 ); + + m_pSource->getAsPlayer()->queuePacket( control1 ); + m_pSource->getAsPlayer()->queuePacket( control ); + m_pSource->getAsPlayer()->eventFinish( m_eventId, 1 ); + + } + else + m_pSource->sendToInRangeSet( control ); + + if( m_onActionInterruptClb ) + m_onActionInterruptClb( *m_pSource->getAsPlayer(), m_eventId, m_additional ); + + } + catch( std::exception& e ) + { + g_log.error( e.what() ); + } + +} diff --git a/src/servers/Server_Zone/EventAction.h b/src/servers/Server_Zone/EventAction.h new file mode 100644 index 00000000..7e956d93 --- /dev/null +++ b/src/servers/Server_Zone/EventAction.h @@ -0,0 +1,37 @@ +#ifndef _EVENTACTION_H_ +#define _EVENTACTION_H_ + +#include + +#include "Forwards.h" +#include "Action.h" + +namespace Core { namespace Action { + + class EventAction : public Action + { + + public: + EventAction(); + ~EventAction(); + + EventAction( Entity::ActorPtr pActor, uint32_t eventId, uint16_t action, + ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ); + + void onStart() override; + void onFinish() override; + void onInterrupt() override; + + private: + uint32_t m_eventId; + uint64_t m_additional; + + ActionCallback m_onActionFinishClb; + ActionCallback m_onActionInterruptClb; + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/EventFinishPacket.h b/src/servers/Server_Zone/EventFinishPacket.h new file mode 100644 index 00000000..5dd6f520 --- /dev/null +++ b/src/servers/Server_Zone/EventFinishPacket.h @@ -0,0 +1,44 @@ +#ifndef _EVENTFINISH_H +#define _EVENTFINISH_H + +#include + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The packet sent to finish an event. +*/ +class EventFinishPacket : public GamePacketNew< FFXIVIpcEventFinish > +{ +public: + EventFinishPacket( uint32_t playerId, + uint32_t eventId, + uint8_t param1, + uint32_t param3 ) : + GamePacketNew< FFXIVIpcEventFinish >( playerId, playerId ) + { + initialize( eventId, param1, param3 ); + }; + +private: + void initialize( uint32_t eventId, + uint8_t param1, + uint32_t param3 ) + { + m_data.eventId = eventId; + m_data.param1 = param1; + m_data.param2 = 1; + m_data.param3 = param3; + + }; +}; + +} +} +} +} + +#endif /*_EVENTFINISH_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/EventHelper.cpp b/src/servers/Server_Zone/EventHelper.cpp new file mode 100644 index 00000000..b91a0e96 --- /dev/null +++ b/src/servers/Server_Zone/EventHelper.cpp @@ -0,0 +1,77 @@ +#include "EventHelper.h" +#include "Event.h" +#include + +#include + +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; + +std::string Core::Event::getEventName( uint32_t eventId ) +{ + uint16_t eventType = eventId >> 16; + + switch( eventType ) + { + case EventType::Quest: + { + auto questInfo = g_exdData.getQuestInfo( eventId ); + if( questInfo ) + { + std::string name = questInfo->name_intern; + std::size_t pos = name.find_first_of( "_" ); + + return questInfo->name_intern.substr( 0, pos ); + } + } + break; + case EventType::CustomTalk: + { + auto customTalkInfo = g_exdData.getCustomTalkInfo( eventId ); + if( customTalkInfo ) + { + std::string name = customTalkInfo->name_intern; + std::size_t pos = name.find_first_of( "_" ); + + return customTalkInfo->name_intern.substr( 0, pos ); + } + + } + break; + case EventType::Opening: + { + auto openingInfo = g_exdData.getOpeningInfo( eventId ); + if( openingInfo ) + return openingInfo->name; + } + break; + case EventType::Aetheryte: + { + auto aetherInfo = g_exdData.getAetheryteInfo( eventId & 0xFFFF ); + if( aetherInfo->isAetheryte ) + return "Aetheryte"; + return "Aethernet"; + + } + case EventType::ChocoPort: + { + return "ChocoboTaxi"; + } + default: + { + return ""; + + } + } + return ""; +} + +uint32_t Core::Event::mapEventActorToRealActor( uint32_t eventActorId ) +{ + auto levelInfo = g_exdData.getLevelInfo( eventActorId ); + if( levelInfo ) + return levelInfo->actor_id; + + return 0; +} diff --git a/src/servers/Server_Zone/EventHelper.h b/src/servers/Server_Zone/EventHelper.h new file mode 100644 index 00000000..e8c531f2 --- /dev/null +++ b/src/servers/Server_Zone/EventHelper.h @@ -0,0 +1,16 @@ +#ifndef _EVENTHELPER_H +#define _EVENTHELPER_H + +#include +#include + +namespace Core { + namespace Event { + + std::string getEventName( uint32_t eventId ); + + uint32_t mapEventActorToRealActor( uint32_t eventActorId ); + + } +} +#endif diff --git a/src/servers/Server_Zone/EventItemAction.cpp b/src/servers/Server_Zone/EventItemAction.cpp new file mode 100644 index 00000000..12ac1e14 --- /dev/null +++ b/src/servers/Server_Zone/EventItemAction.cpp @@ -0,0 +1,122 @@ +#include "EventItemAction.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include +#include +#include +#include +#include "Player.h" + +extern Core::Logger g_log; + +using namespace Core::Common; +using namespace Core::Network; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Action::EventItemAction::EventItemAction() +{ + m_actionType = Common::ActionType::Event; +} + +Core::Action::EventItemAction::EventItemAction( Entity::ActorPtr pActor, uint32_t eventId, uint32_t action, + ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ) +{ + m_additional = additional; + m_actionType = ActionType::Event; + m_eventId = eventId; + m_id = action; + // TODO: read the cast time from the action itself + m_castTime = 3000; + m_onActionFinishClb = finishRef; + m_onActionInterruptClb = interruptRef; + m_pSource = pActor; + m_bInterrupt = false; +} + +Core::Action::EventItemAction::~EventItemAction() +{ + +} + +void Core::Action::EventItemAction::onStart() +{ + if( !m_pSource ) + return; + + m_startTime = Util::getTimeMs(); + + GamePacketNew< FFXIVIpcActorCast > castPacket( m_pSource->getId() ); + + castPacket.data().action_id = 1; + castPacket.data().unknown = 3; + castPacket.data().unknown_1 = m_id; + castPacket.data().cast_time = 3.0f; + castPacket.data().target_id = m_pSource->getId(); + + m_pSource->sendToInRangeSet( castPacket, true ); + m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + +} + +void Core::Action::EventItemAction::onFinish() +{ + if( !m_pSource ) + return; + + try + { + GamePacketNew< FFXIVIpcEffect > effectPacket( m_pSource->getId() ); + effectPacket.data().targetId = static_cast< uint32_t >( m_additional ); + effectPacket.data().actionAnimationId = 1; +// effectPacket.data().unknown_3 = 3; + effectPacket.data().actionTextId = m_id; + effectPacket.data().unknown_5 = 2; + effectPacket.data().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( m_pSource->getRotation() ); + effectPacket.data().effectTarget = static_cast< uint32_t >( m_additional ); + + m_pSource->getAsPlayer()->unsetStateFlag( Common::PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + m_pSource->sendToInRangeSet( effectPacket, true ); + + if( m_onActionFinishClb ) + m_onActionFinishClb( *m_pSource->getAsPlayer(), m_eventId, m_additional ); + } + catch( std::exception& e ) + { + g_log.error( e.what() ); + } + +} + +void Core::Action::EventItemAction::onInterrupt() +{ + if( !m_pSource ) + return; + + try + { + + auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, + 0x219, 0x04, m_id ); + if( m_pSource->isPlayer() ) + { + m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting ); + m_pSource->getAsPlayer()->sendStateFlags(); + m_pSource->sendToInRangeSet( control, true ); + } + else + m_pSource->sendToInRangeSet( control ); + + if( m_onActionInterruptClb ) + m_onActionInterruptClb( *m_pSource->getAsPlayer(), m_eventId, m_additional ); + + } + catch( std::exception& e ) + { + g_log.error( e.what() ); + } + +} diff --git a/src/servers/Server_Zone/EventItemAction.h b/src/servers/Server_Zone/EventItemAction.h new file mode 100644 index 00000000..c367c13f --- /dev/null +++ b/src/servers/Server_Zone/EventItemAction.h @@ -0,0 +1,35 @@ +#ifndef _EVENTITEMACTION_H_ +#define _EVENTITEMACTION_H_ + +#include "Forwards.h" +#include "Action.h" + +namespace Core { namespace Action { + + class EventItemAction : public Action + { + + public: + EventItemAction(); + ~EventItemAction(); + + EventItemAction( Entity::ActorPtr pActor, uint32_t eventId, uint32_t action, + ActionCallback finishRef, ActionCallback interruptRef, uint64_t additional ); + + void onStart() override; + void onFinish() override; + void onInterrupt() override; + + private: + uint32_t m_eventId; + uint64_t m_additional; + + ActionCallback m_onActionFinishClb; + ActionCallback m_onActionInterruptClb; + + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/EventPlayPacket.h b/src/servers/Server_Zone/EventPlayPacket.h new file mode 100644 index 00000000..bc48b6ff --- /dev/null +++ b/src/servers/Server_Zone/EventPlayPacket.h @@ -0,0 +1,55 @@ +#ifndef _EVENTPLAY_H +#define _EVENTPLAY_H + +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The packet sent to play an event. +*/ +class EventPlayPacket : public GamePacketNew< FFXIVIpcEventPlay > +{ +public: + EventPlayPacket( uint32_t playerId, + uint64_t actorId, + uint32_t eventId, + uint16_t scene, + uint32_t flags, + uint8_t param3, + uint32_t param4 = 0, + uint32_t param5 = 0 ) : + GamePacketNew< FFXIVIpcEventPlay >( playerId, playerId ) + { + initialize( actorId, eventId, scene, flags, param3, param4, param5 ); + }; + +private: + void initialize( uint64_t actorId, + uint32_t eventId, + uint16_t scene, + uint32_t flags, + uint8_t param3, + uint32_t param4, + uint32_t param5 ) + { + m_data.actorId = actorId; + m_data.eventId = eventId; + m_data.scene = scene; + m_data.flags = flags; + m_data.param3 = param3; + m_data.param4 = param4; + m_data.param5 = param5; + }; +}; + +} +} +} +} + +#endif /*_EVENTPLAY_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/EventStartPacket.h b/src/servers/Server_Zone/EventStartPacket.h new file mode 100644 index 00000000..e65baa97 --- /dev/null +++ b/src/servers/Server_Zone/EventStartPacket.h @@ -0,0 +1,50 @@ +#ifndef _EVENTSTART_H +#define _EVENTSTART_H + +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The packet sent to start an event. +*/ +class EventStartPacket : public GamePacketNew< FFXIVIpcEventStart > +{ +public: + EventStartPacket( uint32_t playerId, + uint64_t actorId, + uint32_t eventId, + uint8_t param1 = 0, + uint8_t param2 = 0, + uint32_t param3 = 0 ) : + GamePacketNew< FFXIVIpcEventStart >( playerId, playerId ) + { + initialize( actorId, eventId, param1, param2, param3 ); + }; + +private: + void initialize( uint64_t actorId, + uint32_t eventId, + uint8_t param1, + uint8_t param2, + uint32_t param3 ) + { + m_data.actorId = actorId; + m_data.eventId = eventId; + m_data.param1 = param1; + m_data.param2 = param2; + m_data.param3 = param3; + + }; +}; + +} +} +} +} + +#endif /*_EVENTSTART_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/Forwards.h b/src/servers/Server_Zone/Forwards.h new file mode 100644 index 00000000..87ec8bd2 --- /dev/null +++ b/src/servers/Server_Zone/Forwards.h @@ -0,0 +1,75 @@ +#ifndef _FORWARDS_H +#define _FORWARDS_H + +#include +#include + +#define TYPE_FORWARD( x ) \ +class x; \ +typedef boost::shared_ptr< x > x ## Ptr; \ +typedef std::vector< x > x ## PtrList; + +namespace Core +{ + TYPE_FORWARD( Cell ); + TYPE_FORWARD( Zone ); + TYPE_FORWARD( Item ); + TYPE_FORWARD( ItemContainer ); + TYPE_FORWARD( Inventory ); + TYPE_FORWARD( Session ); + TYPE_FORWARD( XMLConfig ); + TYPE_FORWARD( ZonePosition ) + + namespace StatusEffect + { + TYPE_FORWARD( StatusEffect ); + TYPE_FORWARD( StatusEffectContainer ); + } + + namespace Entity + { + TYPE_FORWARD( Actor ); + TYPE_FORWARD( Player ); + TYPE_FORWARD( BattleNpc ); + TYPE_FORWARD( BattleNpcTemplate ); + } + + namespace Event + { + TYPE_FORWARD( Event ); + } + + namespace Action + { + TYPE_FORWARD( Action ); + TYPE_FORWARD( ActionTeleport ); + TYPE_FORWARD( ActionCast ); + TYPE_FORWARD( EventAction ); + } + + namespace Network + { + TYPE_FORWARD( Hive ); + TYPE_FORWARD( Acceptor ); + TYPE_FORWARD( Connection ); + TYPE_FORWARD( GameConnection ); + TYPE_FORWARD( SessionConnection ); + TYPE_FORWARD( CustomMsgClientConnection ); + + namespace Packets + { + TYPE_FORWARD( GamePacket ); + } + } + + namespace Scripting + { + typedef std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint16_t ) > EventReturnCallback; + } + + typedef std::function< void( Entity::Player&, uint32_t, uint64_t ) > ActionCallback; + +} + + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/GameCommand.h b/src/servers/Server_Zone/GameCommand.h new file mode 100644 index 00000000..9f58f514 --- /dev/null +++ b/src/servers/Server_Zone/GameCommand.h @@ -0,0 +1,67 @@ +#ifndef _GAMECOMMAND_H_ +#define _GAMECOMMAND_H_ + +#include + +#include "Player.h" +#include "Forwards.h" + +namespace Core { + + class GameCommandHandler; + + // CGameCommand is used to define in game text command callbacks + // TODO it should probably be renamed to something more intuitive + // TODO the command identifier, currently '@' should probably be defined in here aswell so it is easily replaced + class GameCommand + { + public: + + typedef void (GameCommandHandler::*pFunc)(char *, Entity::PlayerPtr, boost::shared_ptr); + + // String for the command + std::string m_commandName; + + // command callback + pFunc m_pFunc; + + // helptext + std::string m_helpText; + + // userlevel needed to execute the command + Common::UserLevel m_userLevel; + + GameCommand(const std::string& n, pFunc functionPtr, const std::string& hText, Common::UserLevel uLevel) + { + m_commandName = n; + m_pFunc = functionPtr; + m_helpText = hText; + m_userLevel = uLevel; + } + + ~GameCommand() + { + + } + + const std::string& getName() const + { + return m_commandName; + } + + const std::string& getHelpText() const + { + return m_helpText; + } + + Common::UserLevel getUserLevel() const + { + return m_userLevel; + } + + }; + +} + + +#endif diff --git a/src/servers/Server_Zone/GameCommandHandler.cpp b/src/servers/Server_Zone/GameCommandHandler.cpp new file mode 100644 index 00000000..59318d6b --- /dev/null +++ b/src/servers/Server_Zone/GameCommandHandler.cpp @@ -0,0 +1,511 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "GameCommand.h" +#include "GameCommandHandler.h" + +#include "ServerNoticePacket.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "InitUIPacket.h" +#include "GameConnection.h" + + +#include "Player.h" +#include "BattleNpc.h" + +#include "Zone.h" + +#include "Globals.h" +#include "ServerZone.h" + +#include "StatusEffect.h" +#include "Session.h" +#include + + +// instanciate and initialize commands +Core::GameCommandHandler::GameCommandHandler() +{ + + // Push all commands onto the register map + registerCommand( "set", &GameCommandHandler::set, "Loads and injects a premade Packet.", Common::UserLevel::all ); + registerCommand( "get", &GameCommandHandler::get, "Loads and injects a premade Packet.", Common::UserLevel::all ); + registerCommand( "add", &GameCommandHandler::add, "Loads and injects a premade Packet.", Common::UserLevel::all ); + //registerCommand( "debug", &GameCommandHandler::debug, "Loads and injects a premade Packet.", Common::UserLevel::all ); + registerCommand( "inject", &GameCommandHandler::injectPacket, "Loads and injects a premade Packet.", Common::UserLevel::all ); + registerCommand( "script_reload", &GameCommandHandler::scriptReload, "Loads and injects a premade Packet.", Common::UserLevel::all ); + registerCommand( "nudge", &GameCommandHandler::nudge, "Nudges you forward/up/down", Common::UserLevel::all ); + +} + +// clear all loaded commands +Core::GameCommandHandler::~GameCommandHandler() +{ + for( auto it = m_commandMap.begin(); it != m_commandMap.end(); ++it ) + ( *it ).second.reset(); +} + +// add a command set to the register map +void Core::GameCommandHandler::registerCommand( const std::string& n, Core::GameCommand::pFunc functionPtr, + const std::string& hText, Core::Common::UserLevel uLevel ) +{ + m_commandMap[std::string( n )] = boost::make_shared( n, functionPtr, hText, uLevel ); +} + +// try to retrieve the command in question, execute if found +void Core::GameCommandHandler::execCommand( char * data, Core::Entity::PlayerPtr pPlayer ) +{ + + // define callback pointer + void ( GameCommandHandler::*pf )( char *, Entity::PlayerPtr, boost::shared_ptr< GameCommand > ); + + std::string commandString; + + // check if the command has parameters + std::string tmpCommand = std::string( data ); + std::size_t pos = tmpCommand.find_first_of( " " ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + commandString = tmpCommand.substr( 0, pos ); + else + // no parameters, just get the command + commandString = tmpCommand; + + // try to retrieve the command + auto it = m_commandMap.find( commandString ); + + if( it == m_commandMap.end() ) + // no command found, do something... or not + g_log.error( "[" + std::to_string( pPlayer->getId() ) + "] " + "Command not found" ); + // TODO Notify the client of the failed command + else + { + // command found, call the callback function and pass parameters if present. + pf = ( *it ).second->m_pFunc; + ( this->*pf )( data, pPlayer, ( *it ).second ); + return; + } + + +} + + + +/////////////////////////////////////////////////////////////////////////////////////// +// Definition of the commands +/////////////////////////////////////////////////////////////////////////////////////// + +void Core::GameCommandHandler::scriptReload( char * data, Core::Entity::PlayerPtr pPlayer, + boost::shared_ptr command ) +{ + g_scriptMgr.reload(); +} + +void Core::GameCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr command ) +{ + std::string subCommand = ""; + std::string params = ""; + + // check if the command has parameters + std::string tmpCommand = std::string( data + command->getName().length() + 1 ); + + std::size_t pos = tmpCommand.find_first_of( " " ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + subCommand = tmpCommand.substr( 0, pos ); + else + // no subcommand given + subCommand = tmpCommand; + + if( command->getName().length() + 1 + pos + 1 < strlen( data ) ) + params = std::string( data + command->getName().length() + 1 + pos + 1 ); + + g_log.debug( "[" + std::to_string( pPlayer->getId() ) + "] " + + "subCommand " + subCommand + " params: " + params ); + + + if( ( ( subCommand == "pos" ) || ( subCommand == "posr" ) ) && ( params != "" ) ) + { + int32_t posX; + int32_t posY; + int32_t posZ; + + sscanf( params.c_str(), "%d %d %d", &posX, &posY, &posZ ); + + if( ( posX == 0xcccccccc ) || ( posY == 0xcccccccc ) || ( posZ == 0xcccccccc ) ) + { + pPlayer->sendUrgent( "Syntaxerror." ); + return; + } + + if( subCommand == "pos" ) + pPlayer->setPosition( static_cast< float >( posX ), + static_cast< float >( posY ), + static_cast< float >( posZ ) ); + else + pPlayer->setPosition( pPlayer->getPos().x + static_cast< float >( posX ), + pPlayer->getPos().y + static_cast< float >( posY ), + pPlayer->getPos().z + static_cast< float >( posZ ) ); + + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcActorSetPos > + setActorPosPacket( pPlayer->getId() ); + setActorPosPacket.data().x = pPlayer->getPos().x; + setActorPosPacket.data().y = pPlayer->getPos().y; + setActorPosPacket.data().z = pPlayer->getPos().z; + pPlayer->queuePacket( setActorPosPacket ); + + } + else if( ( subCommand == "zone" ) && ( params != "" ) ) + { + int32_t zoneId; + sscanf( params.c_str(), "%i", &zoneId ); + + if( zoneId < 1 ) + pPlayer->sendUrgent( "Zone id out of range." ); + else + { + pPlayer->setPosition( pPlayer->getPos() ); + pPlayer->performZoning( zoneId, pPlayer->getPos(), 0); + } + + } + else if( ( subCommand == "hp" ) && ( params != "" ) ) + { + int32_t hp; + sscanf( params.c_str(), "%i", &hp ); + + pPlayer->setHp( hp ); + + auto control = Network::Packets::Server::ActorControlPacket142( pPlayer->getId(), Common::ActorControlType::HpSetStat, 1, pPlayer->getHp() ); + + pPlayer->sendToInRangeSet( control, true ); + + } + else if( ( subCommand == "quest" ) && ( params != "" ) ) + setQuestHandler( const_cast< char* >( params.c_str() ), pPlayer, command ); + + else if( ( subCommand == "tele" ) && ( params != "" ) ) + { + int32_t aetheryteId; + sscanf( params.c_str(), "%i", &aetheryteId ); + + pPlayer->teleport( aetheryteId ); + } + + else if( ( subCommand == "discovery" ) && ( params != "" ) ) + { + int32_t map_id; + int32_t discover_id; + sscanf( params.c_str(), "%i %i", &map_id, &discover_id ); + + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcDiscovery > discoveryPacket( pPlayer->getId() ); + discoveryPacket.data().map_id = map_id; + discoveryPacket.data().map_part_id = discover_id; + pPlayer->queuePacket( discoveryPacket ); + } + + else if( ( subCommand == "discovery_pos" ) && ( params != "" ) ) + { + int32_t map_id; + int32_t discover_id; + int32_t pos_id; + sscanf( params.c_str(), "%i %i %i", &pos_id, &map_id, &discover_id ); + + std::string query2 = "UPDATE IGNORE `dbdiscoveryref` SET `discover_id` = '" + std::to_string( discover_id ) + + "' WHERE `dbdiscoveryref`.`id` = " + std::to_string( pos_id ) + ";"; + + std::string query1 = "INSERT IGNORE INTO `dbdiscoveryref` (`id`, `map_id`, `discover_id`) VALUES ('" + std::to_string( pos_id ) + + "', '" + std::to_string( map_id ) + + "', '" + std::to_string( discover_id ) + "')"; + + g_database.execute( query1.c_str() ); + g_database.execute( query2.c_str() ); + + } + + else if( subCommand == "discovery_reset" ) + { + pPlayer->resetDiscovery(); + pPlayer->queuePacket( Network::Packets::Server::InitUIPacket( pPlayer ) ); + } + + +} + +void Core::GameCommandHandler::setQuestHandler( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr command ) +{ + std::string tmpCommand( data ); + std::string subCommand; + + std::size_t pos = tmpCommand.find_first_of( " " ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + subCommand = tmpCommand.substr( 0, pos ); + else + { + // no subcommand given + subCommand = tmpCommand; + return; + } + + std::string params( data + pos ); + + if( subCommand == "seq" ) + { + int32_t questId; + int32_t sequence; + sscanf( params.c_str(), "%i %i", &questId, &sequence ); + + if( sequence == -1 ) + pPlayer->removeQuest( questId ); + else + pPlayer->updateQuest( questId, sequence ); + } + else if( subCommand == "var" ) + { + int32_t questId; + int32_t varIdx; + int32_t varVal; + sscanf( params.c_str(), "%i %i %i", &questId, &varIdx, &varVal ); + + //pPlayer->updateQuestVar( questId, varIdx, varVal ); + } +} + +void Core::GameCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr command ) +{ + std::string subCommand; + std::string params = ""; + + // check if the command has parameters + std::string tmpCommand = std::string( data + command->getName().length() + 1 ); + + std::size_t pos = tmpCommand.find_first_of( " " ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + subCommand = tmpCommand.substr( 0, pos ); + else + { + // no subcommand given + subCommand = tmpCommand; + return; + } + + if( command->getName().length() + 1 + pos + 1 < strlen( data ) ) + params = std::string( data + command->getName().length() + 1 + pos + 1 ); + + g_log.debug( "[" + std::to_string( pPlayer->getId() ) + "] " + + "subCommand " + subCommand + " params: " + params ); + + + if( ( subCommand == "item" ) && ( params != "" ) ) + { + int32_t catalogId; + int32_t amount; + + sscanf( params.c_str(), "%d %d", &catalogId, &amount ); + + if( amount < 1 || amount > 99 ) + { + amount = 1; + } + + if( ( catalogId == 0xcccccccc ) ) + { + pPlayer->sendUrgent( "Syntaxerror." ); + return; + } + + if( !pPlayer->addItem( -1, catalogId, amount ) ) + pPlayer->sendUrgent( "Item " + std::to_string( catalogId ) + " not found..." ); + + } + else if( subCommand == "exp" ) + { + int32_t amount; + + sscanf( params.c_str(), "%d", &amount ); + + pPlayer->gainExp( amount ); + } + else if( subCommand == "status" ) + { + int32_t id; + int32_t duration; + + sscanf( params.c_str(), "%d %d", &id, &duration ); + + StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pPlayer, pPlayer, duration, 3000 ) ); + pPlayer->addStatusEffect( effect ); + } + else if( subCommand == "spawn" ) + { + int32_t model, name; + + sscanf( params.c_str(), "%d %d", &model, &name ); + + Entity::BattleNpcPtr pBNpc( new Entity::BattleNpc( model, name, pPlayer->getPos() ) ); + + auto pZone = pPlayer->getCurrentZone(); + pBNpc->setCurrentZone( pZone ); + pZone->pushActor( pBNpc ); + + } + else if( subCommand == "op" ) + { + // temporary research packet + int32_t opcode; + sscanf( params.c_str(), "%x", &opcode ); + Network::Packets::GamePacketPtr pPe( new Network::Packets::GamePacket( opcode, 0x30, pPlayer->getId(), pPlayer->getId() ) ); + pPlayer->queuePacket( pPe ); + } + else if( subCommand == "actrl" ) + + { + + // temporary research packet + + int32_t opcode; + int32_t param1; + int32_t param2; + int32_t param3; + int32_t param4; + int32_t param5; + int32_t param6; + int32_t playerId; + + sscanf( params.c_str(), "%x %x %x %x %x %x %x %x", &opcode, ¶m1, ¶m2, ¶m3, ¶m4, ¶m5, ¶m6, &playerId ); + + pPlayer->sendNotice( "Injecting ACTOR_CONTROL " + std::to_string( opcode ) ); + + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcActorControl143 > actorControl( playerId, pPlayer->getId() ); + actorControl.data().category = opcode; + actorControl.data().param1 = param1; + actorControl.data().param2 = param2; + actorControl.data().param3 = param3; + actorControl.data().param4 = param4; + actorControl.data().param5 = param5; + actorControl.data().param6 = param6; + pPlayer->queuePacket( actorControl ); + + + /*sscanf(params.c_str(), "%x %x %x %x %x %x %x", &opcode, ¶m1, ¶m2, ¶m3, ¶m4, ¶m5, ¶m6, &playerId); + + Network::Packets::Server::ServerNoticePacket noticePacket(pPlayer, "Injecting ACTOR_CONTROL " + std::to_string(opcode)); + + pPlayer->queuePacket(noticePacket); + + Network::Packets::Server::ActorControlPacket143 controlPacket(pPlayer, opcode, + param1, param2, param3, param4, param5, param6, playerId); + pPlayer->queuePacket(controlPacket);*/ + + } + + +} + +void Core::GameCommandHandler::get( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr command ) +{ + std::string subCommand; + std::string params = ""; + + // check if the command has parameters + std::string tmpCommand = std::string( data + command->getName().length() + 1 ); + + std::size_t pos = tmpCommand.find_first_of( " " ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + subCommand = tmpCommand.substr( 0, pos ); + else + // no subcommand given + subCommand = tmpCommand; + + if( command->getName().length() + 1 + pos + 1 < strlen( data ) ) + params = std::string( data + command->getName().length() + 1 + pos + 1 ); + + g_log.debug( "[" + std::to_string( pPlayer->getId() ) + "] " + + "subCommand " + subCommand + " params: " + params ); + + + if( ( subCommand == "pos" ) ) + { + + int16_t map_id = g_exdData.m_zoneInfoMap[pPlayer->getCurrentZone()->getId()].map_id; + + pPlayer->sendNotice( "Pos:\n" + + std::to_string( pPlayer->getPos().x ) + "\n" + + std::to_string( pPlayer->getPos().y ) + "\n" + + std::to_string( pPlayer->getPos().z ) + "\n" + + std::to_string( pPlayer->getRotation() ) + "\nMapId: " + + std::to_string( map_id ) + "\nZoneID: " + + std::to_string( pPlayer->getCurrentZone()->getId() ) + "\n" ); + } + +} + +void Core::GameCommandHandler::injectPacket( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr< Core::GameCommand > command ) +{ + auto pSession = g_serverZone.getSession( pPlayer->getId() ); + if( pSession ) + pSession->getZoneConnection()->injectPacket( data + 7, pPlayer ); +} + +void Core::GameCommandHandler::nudge( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ) +{ + std::string subCommand; + + // check if the command has parameters + std::string tmpCommand = std::string( data + command->getName().length() + 1 ); + + std::size_t spos = tmpCommand.find_first_of( " " ); + + auto& pos = pPlayer->getPos(); + + int32_t offset = 0; + char direction[20]; + memset( direction, 0, 20 ); + + sscanf( tmpCommand.c_str(), "%d %s", &offset, direction ); + + + if( direction[0] == 'u' || direction[0] == '+' ) + { + pos.y += offset; + pPlayer->sendNotice( "nudge: Placing up " + std::to_string( offset ) + " yalms" ); + } + else if( direction[0] == 'd' || direction[0] == '-' ) + { + pos.y -= offset; + pPlayer->sendNotice( "nudge: Placing down " + std::to_string( offset ) + " yalms" ); + + } + else + { + float angle = pPlayer->getRotation() + ( PI / 2 ); + pos.x -= offset * cos( angle ); + pos.z += offset * sin( angle ); + pPlayer->sendNotice( "nudge: Placing forward " + std::to_string( offset ) + " yalms" ); + } + if( offset != 0 ) + { + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcActorSetPos > + setActorPosPacket( pPlayer->getId() ); + setActorPosPacket.data().x = pPlayer->getPos().x; + setActorPosPacket.data().y = pPlayer->getPos().y; + setActorPosPacket.data().z = pPlayer->getPos().z; + setActorPosPacket.data().r16 = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + pPlayer->queuePacket( setActorPosPacket ); + } +} diff --git a/src/servers/Server_Zone/GameCommandHandler.h b/src/servers/Server_Zone/GameCommandHandler.h new file mode 100644 index 00000000..01255b19 --- /dev/null +++ b/src/servers/Server_Zone/GameCommandHandler.h @@ -0,0 +1,45 @@ +#ifndef _GAMECOMMANDHANDLER_H_ +#define _GAMECOMMANDHANDLER_H_ + +#include + +#include "GameCommand.h" +#include +#include "Forwards.h" + +namespace Core { + + +// handler for in game commands +class GameCommandHandler +{ +private: + // container mapping command string to command object + std::map > m_commandMap; + +public: + GameCommandHandler(); + ~GameCommandHandler(); + + // register command to command map + void registerCommand( const std::string& n, GameCommand::pFunc, const std::string& hText, Common::UserLevel uLevel ); + + // execute command if registered + void execCommand( char * data, Entity::PlayerPtr pPlayer ); + + // command handler callbacks + void set( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + void get( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + void add( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + //void debug( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + void scriptReload( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + + void setQuestHandler( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + + void injectPacket( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + void nudge( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr command ); + +}; + +} +#endif diff --git a/src/servers/Server_Zone/GameConnection.cpp b/src/servers/Server_Zone/GameConnection.cpp new file mode 100644 index 00000000..cca0745a --- /dev/null +++ b/src/servers/Server_Zone/GameConnection.cpp @@ -0,0 +1,401 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GameConnection.h" + +#include "ServerZone.h" + +#include "Session.h" +#include "Zone.h" + +#include "InitUIPacket.h" + +#include "GameCommandHandler.h" + +#include "Player.h" + +#include "Forwards.h" + +extern Core::GameCommandHandler g_gameCommandMgr; +extern Core::Logger g_log; +extern Core::ServerZone g_serverZone; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Network::GameConnection::GameConnection( Core::Network::HivePtr pHive, + Core::Network::AcceptorPtr pAcceptor ) + : Connection( pHive ) + , m_pAcceptor( pAcceptor ) + , m_conType( ConnectionType::None ) +{ + auto setHandler = [=]( uint16_t opcode, std::string handlerName, GameConnection::Handler pHandler ) + { + m_packetHandlerMap[opcode] = pHandler; + m_packetHandlerStrMap[opcode] = handlerName; + }; + + setHandler( ClientIpcType::PingHandler, "PingHandler", &GameConnection::pingHandler ); + setHandler( ClientIpcType::InitHandler, "InitHandler", &GameConnection::initHandler ); + setHandler( ClientIpcType::ChatHandler, "ChatHandler", &GameConnection::chatHandler ); + + setHandler( ClientIpcType::FinishLoadingHandler, "FinishLoadingHandler", &GameConnection::finishLoadingHandler ); + + setHandler( ClientIpcType::PlayTimeHandler, "PlayTimeHandler", &GameConnection::playTimeHandler ); + setHandler( ClientIpcType::LogoutHandler, "LogoutHandler", &GameConnection::logoutHandler ); + + setHandler( ClientIpcType::SocialListHandler, "SocialListHandler", &GameConnection::socialListHandler ); + setHandler( ClientIpcType::SetSearchInfoHandler, "SetSearchInfoHandler", &GameConnection::setSearchInfoHandler ); + setHandler( ClientIpcType::ReqSearchInfoHandler, "ReqSearchInfoHandler", &GameConnection::reqSearchInfoHandler ); + + setHandler( ClientIpcType::BlackListHandler, "BlackListHandler", &GameConnection::blackListHandler ); + + setHandler( ClientIpcType::LinkshellListHandler, "LinkshellListHandler", &GameConnection::linkshellListHandler ); + + setHandler( ClientIpcType::FcInfoReqHandler, "FcInfoReqHandler", &GameConnection::fcInfoReqHandler ); + + setHandler( ClientIpcType::ZoneLineHandler, "ZoneLineHandler", &GameConnection::zoneLineHandler ); + setHandler( ClientIpcType::ActionHandler, "ActionHandler", &GameConnection::actionHandler ); + + setHandler( ClientIpcType::DiscoveryHandler, "DiscoveryHandler", &GameConnection::discoveryHandler ); + + setHandler( ClientIpcType::SkillHandler, "SkillHandler", &GameConnection::skillHandler ); + + setHandler( ClientIpcType::GMCommand1, "GMCommand1", &GameConnection::gm1Handler ); + setHandler( ClientIpcType::GMCommand2, "GMCommand2", &GameConnection::gm2Handler ); + + setHandler( ClientIpcType::UpdatePositionHandler,"UpdatePositionHandler", &GameConnection::updatePositionHandler ); + + setHandler( ClientIpcType::InventoryModifyHandler,"InventoryModifyHandler", &GameConnection::inventoryModifyHandler ); + + setHandler( ClientIpcType::TalkEventHandler, "EventHandler", &GameConnection::eventHandler ); + setHandler( ClientIpcType::EmoteEventHandler, "EventHandler", &GameConnection::eventHandler ); + setHandler( ClientIpcType::WithinRangeEventHandler, "EventHandler", &GameConnection::eventHandler ); + setHandler( ClientIpcType::OutOfRangeEventHandler, "EventHandler", &GameConnection::eventHandler ); + setHandler( ClientIpcType::EnterTeriEventHandler, "EventHandler", &GameConnection::eventHandler ); + + setHandler( ClientIpcType::ReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandler ); + setHandler( ClientIpcType::TradeReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandler ); +} + +Core::Network::GameConnection::~GameConnection() +{ + +} + + +// overwrite the parents onConnect for our game socket needs +void Core::Network::GameConnection::OnAccept( const std::string & host, uint16_t port ) +{ + GameConnectionPtr connection( new GameConnection( m_hive, m_pAcceptor ) ); + m_pAcceptor->Accept( connection ); + + g_log.info( "Connect from " + m_socket.remote_endpoint().address().to_string() ); +} + + +void Core::Network::GameConnection::OnDisconnect() +{ + g_log.debug( "DISCONNECT" ); + m_pSession = nullptr; +} + +void Core::Network::GameConnection::OnRecv( std::vector< uint8_t > & buffer ) +{ + Packets::FFXIVARR_PACKET_HEADER ipcHeader; + std::vector< Packets::FFXIVARR_PACKET_RAW > packetList; + + Network::Util::bufferToPacketList( buffer, ipcHeader, packetList ); + + handlePackets( ipcHeader, packetList ); + +} + +void Core::Network::GameConnection::OnError( const boost::system::error_code & error ) +{ + g_log.debug( "ERROR" ); +} + +void Core::Network::GameConnection::queueInPacket( Core::Network::Packets::GamePacketPtr inPacket ) +{ + m_inQueue.push( inPacket ); +} + +void Core::Network::GameConnection::queueOutPacket( Core::Network::Packets::GamePacketPtr outPacket ) +{ + m_outQueue.push( outPacket ); +} + +void Core::Network::GameConnection::handleGamePacket( Core::Network::Packets::GamePacketPtr pPacket ) +{ + if( !m_pSession ) + return; + + auto it = m_packetHandlerMap.find( pPacket->getSubType() ); + + if( it != m_packetHandlerMap.end() ) + { + auto name = m_packetHandlerStrMap[pPacket->getSubType()]; + // dont display packet notification if it is a ping or pos update, don't want the spam + if( pPacket->getSubType() != ClientIpcType::PingHandler + && pPacket->getSubType() != ClientIpcType::UpdatePositionHandler ) + g_log.debug( "[" + std::to_string( m_pSession->getId() ) + "] Handling packet : " + name + "( " + + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( pPacket->getSubType() & 0xFFFF ) ) + " )" ); + + ( this->*( it->second ) )( pPacket, m_pSession->getPlayer() ); + } + else + { + g_log.debug( "[" + std::to_string( m_pSession->getId() ) + "] Undefined packet : Unknown ( " + + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( pPacket->getSubType() & 0xFFFF ) ) + " )" ); + g_log.debug( pPacket->toString() ); + } + + +} + +void Core::Network::GameConnection::sendPackets( Packets::PacketContainer * pPacket ) +{ + //g_log.Log(LoggingSeverity::info, pPacket->toString()); + std::vector< uint8_t > sendBuffer; + + pPacket->fillSendBuffer( sendBuffer ); + Send( sendBuffer ); +} + +void Core::Network::GameConnection::processInQueue() +{ + // handle the incoming game packets + while( auto pPacket = m_inQueue.pop() ) + { + handleGamePacket( pPacket ); + } +} + +void Core::Network::GameConnection::processOutQueue() +{ + + if( m_outQueue.size() < 1 ) + return; + + int32_t totalSize = 0; + + // create a new packet container + PacketContainer pRP = PacketContainer(); + + // get next packet off the queue + while( auto pPacket = m_outQueue.pop() ) + { + if( pPacket->getSize() == 0 ) + { + g_log.debug( "end of packet set" ); + break; + } + + pRP.addPacket( *pPacket ); + totalSize += pPacket->getSize(); + } + + if( totalSize > 0 ) + sendPackets( &pRP ); + +} + +void Core::Network::GameConnection::sendSinglePacket( Packets::GamePacket * pPacket ) +{ + PacketContainer pRP = PacketContainer(); + pRP.addPacket( *pPacket ); + sendPackets( &pRP ); +} + +void Core::Network::GameConnection::injectPacket( const std::string& packetpath, Core::Entity::PlayerPtr pPlayer ) +{ + + char packet[0x11570]; + memset( packet, 0, 0x11570 ); + + // get the packet name / path from the command arguments + FILE *fp = nullptr; + fp = fopen( packetpath.c_str(), "rb" ); + if( fp == nullptr ) + { + g_log.error( "Packet " + packetpath + " not found!" ); + return; + } + + // read the packet into the buffer + fseek( fp, 0, SEEK_END ); + int32_t size = ftell( fp ); + rewind( fp ); + fread( packet, sizeof( char ), size, fp ); + fclose( fp ); + + // cycle through the packet entries and queue each one + for( int k = 0x18; k < size;) + { + uint32_t tmpId = pPlayer->getId(); + // replace ids in the entryheader if needed + if( !memcmp( packet + k + 0x04, packet + k + 0x08, 4 ) ) + { + memcpy( packet + k + 0x04, &tmpId, 4 ); + memcpy( packet + k + 0x08, &tmpId, 4 ); + } + else + memcpy( packet + k + 0x08, &tmpId, 4 ); + + uint16_t pSize = *reinterpret_cast< uint16_t* >( packet + k ); + // queue packet to the session + if( pSize == 0 ) + return; + + queueOutPacket( GamePacketPtr( new GamePacket( packet + k, pSize, false ) ) ); + k += ( pSize ); + } +} + +void Core::Network::GameConnection::handlePackets( const Core::Network::Packets::FFXIVARR_PACKET_HEADER& ipcHeader, + const std::vector& packetData ) +{ + + // if a session is set, update the last time it recieved a game packet + if( m_pSession ) + m_pSession->updateLastDataTime(); + + for( auto inPacket : packetData ) + { + + + switch( inPacket.segHdr.type ) + { + case 1: + { + char* id = ( char* ) &( inPacket.data[4] ); + uint32_t playerId = boost::lexical_cast< uint32_t >( id ); + auto pCon = boost::static_pointer_cast< GameConnection, Connection >( shared_from_this() ); + + // try to retrieve the session for this id + auto session = g_serverZone.getSession( playerId ); + + if( !session ) + { + g_log.info( "[" + std::string( id ) + "] Session not registered, creating" ); + // return; + g_serverZone.createSession( playerId ); + session = g_serverZone.getSession( playerId ); + } + + // if not set, set the session for this connection + if( !m_pSession && session ) + m_pSession = session; + + // main connection, assinging it to the session + if( ipcHeader.connectionType == 1 ) + { + g_log.info( "[" + std::string( id ) + "] Setting session for zone connection" ); + session->setZoneConnection( pCon ); + } + // chat connection, assinging it to the session + else if( ipcHeader.connectionType == 2 ) + { + g_log.info( "[" + std::string( id ) + "] Setting session for chat connection" ); + // session->setClientChatConnection( pCon ); + } + + GamePacket pPe( 0x00, 0x18, 0, 0, 0x07 ); + pPe.setValAt< uint32_t >( 0x10, 0xE0000005 ); + pPe.setValAt< uint32_t >( 0x14, static_cast< uint32_t >( time( nullptr ) ) ); + sendSinglePacket( &pPe ); + + pPe = GamePacket( 0x00, 0x38, 0, 0, 0x02 ); + pPe.setValAt< uint32_t >( 0x10, playerId ); + sendSinglePacket( &pPe ); + + break; + + } + case 3: // game packet + { + auto pPacket = new GamePacket( inPacket ); + queueInPacket( Packets::GamePacketPtr( pPacket ) ); + break; + } + case 7: // keep alive + { + uint32_t id = *( uint32_t* ) &inPacket.data[0]; + uint32_t timeStamp = *( uint32_t* ) &inPacket.data[4]; + + GamePacket pPe( 0x00, 0x18, 0, 0, 0x08 ); + pPe.setValAt< uint32_t >( 0x10, id ); + pPe.setValAt< uint32_t >( 0x14, timeStamp ); + sendSinglePacket( &pPe ); + + break; + } + case 8: + { + break; + } + } + + + //// try to retrieve the session for this id + //auto session = g_serverZone.getSession( inPacket.segHdr.source_actor ); + //auto pCon = boost::static_pointer_cast< GameConnection, Connection >( shared_from_this() ); + + //// check if this is a zoning notification + //if( *reinterpret_cast< uint16_t* >( &inPacket.data[2] ) == 0x9999 ) + //{ + + // // if we already have a session in this connection, reload the player + // if( session ) + // g_serverZone.updateSession( inPacket.segHdr.source_actor ); + // else + // { + // // if not, create a new session + // g_serverZone.createSession( inPacket.segHdr.source_actor ); + // session = g_serverZone.getSession( inPacket.segHdr.source_actor ); + // } + + // // set the zoneingType for the player so the correct animation can be played + // auto pPlayer = session->getPlayer(); + // ZoneingType zoneType = static_cast< ZoneingType >( *reinterpret_cast< uint16_t* >( &inPacket.data[18] ) ); + // switch( zoneType ) + // { + // case ZoneingType::Teleport: + // pPlayer->setTeleporting( true ); + // break; + // case ZoneingType::Return: + // pPlayer->setReturning( true ); + // break; + // default: + // break; + // } + // // place this connection in the session + // session->setZoneConnection( pCon ); + // // actually perform the zoning + // session->getPlayer()->setZone( *reinterpret_cast< uint16_t* >( &inPacket.data[16] ) ); + //} + //else + //{ + // if( !session ) + // { + // g_serverZone.createSession( inPacket.segHdr.source_actor ); + // session = g_serverZone.getSession( inPacket.segHdr.source_actor ); + // session->setZoneConnection( pCon ); + // } + + // queueInPacket( GamePacketPtr( new GamePacket( inPacket ) ) ); + //} + + //// if not set, set the session for this connection + //if( !m_pSession && session ) + // m_pSession = session; + } +} diff --git a/src/servers/Server_Zone/GameConnection.h b/src/servers/Server_Zone/GameConnection.h new file mode 100644 index 00000000..35191ac5 --- /dev/null +++ b/src/servers/Server_Zone/GameConnection.h @@ -0,0 +1,113 @@ + +#ifndef GAMECONNECTION_H +#define GAMECONNECTION_H + +#include +#include +#include + +#include + +#include + +#include "Forwards.h" + +#define DECLARE_HANDLER( x ) void x( Packets::GamePacketPtr pInPacket, Entity::PlayerPtr pPlayer ) + +namespace Core { +namespace Network { + +enum struct ConnectionType : uint8_t +{ + Zone, + Chat, + None +}; + +class GameConnection : public Connection +{ + +private: + typedef void ( GameConnection::* Handler )( Packets::GamePacketPtr pInPacket, Entity::PlayerPtr pPlayer ); + + typedef std::map< uint16_t, Handler > HandlerMap; + typedef std::map< uint16_t, std::string > HandlerStrMap; + + AcceptorPtr m_pAcceptor; + + // handler for game packets (main type 0x03) + HandlerMap m_packetHandlerMap; + HandlerStrMap m_packetHandlerStrMap; + + SessionPtr m_pSession; + + LockedQueue< Packets::GamePacketPtr > m_inQueue; + LockedQueue< Packets::GamePacketPtr > m_outQueue; + +public: + ConnectionType m_conType; + + GameConnection( HivePtr pHive, AcceptorPtr pAcceptor ); + + ~GameConnection(); + + // overwrite the parents onConnect for our game socket needs + void OnAccept( const std::string & host, uint16_t port ) override; + + void OnDisconnect() override; + + void OnRecv( std::vector< uint8_t > & buffer ) override; + + void OnError( const boost::system::error_code & error ) override; + + void handlePackets( const Packets::FFXIVARR_PACKET_HEADER& ipcHeader, + const std::vector& packetData ); + + void queueInPacket( Packets::GamePacketPtr inPacket ); + void queueOutPacket( Packets::GamePacketPtr outPacket ); + + void processInQueue(); + void processOutQueue(); + + void handleGamePacket( Packets::GamePacketPtr pPacket ); + + void sendPackets( Packets::PacketContainer * pPacket ); + + void sendSinglePacket( Packets::GamePacket * pPacket ); + + void injectPacket( const std::string& packetpath, Entity::PlayerPtr pPlayer ); + + DECLARE_HANDLER( initHandler ); + DECLARE_HANDLER( finishLoadingHandler ); + DECLARE_HANDLER( blackListHandler ); + DECLARE_HANDLER( socialListHandler ); + DECLARE_HANDLER( linkshellListHandler ); + DECLARE_HANDLER( playTimeHandler ); + DECLARE_HANDLER( pingHandler ); + DECLARE_HANDLER( fcInfoReqHandler ); + DECLARE_HANDLER( setSearchInfoHandler ); + DECLARE_HANDLER( reqSearchInfoHandler ); + DECLARE_HANDLER( updatePositionHandler ); + DECLARE_HANDLER( chatHandler ); + DECLARE_HANDLER( zoneLineHandler ); + DECLARE_HANDLER( actionHandler ); + DECLARE_HANDLER( inventoryModifyHandler ); + DECLARE_HANDLER( discoveryHandler ); + DECLARE_HANDLER( eventHandler ); + DECLARE_HANDLER( logoutHandler ); + DECLARE_HANDLER( skillHandler ); + + DECLARE_HANDLER( gm1Handler ); + DECLARE_HANDLER( gm2Handler ); + + + +}; + + + +} +} + + +#endif diff --git a/src/servers/Server_Zone/Globals.h b/src/servers/Server_Zone/Globals.h new file mode 100644 index 00000000..12261f48 --- /dev/null +++ b/src/servers/Server_Zone/Globals.h @@ -0,0 +1,18 @@ +#ifndef _GLOBALS_H +#define _GLOBALS_H + +#include "ZoneMgr.h" +#include +#include +#include "ServerZone.h" +#include "ScriptManager.h" +#include + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::ServerZone g_serverZone; +extern Core::ZoneMgr g_zoneMgr; +extern Core::Scripting::ScriptManager g_scriptMgr; +extern Core::Data::ExdData g_exdData; + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/InitUIPacket.h b/src/servers/Server_Zone/InitUIPacket.h new file mode 100644 index 00000000..44d033f6 --- /dev/null +++ b/src/servers/Server_Zone/InitUIPacket.h @@ -0,0 +1,87 @@ +#ifndef _CORE_NETWORK_PACKETS_INITUIPACKET_H +#define _CORE_NETWORK_PACKETS_INITUIPACKET_H + +#include +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Client UI Initialization packet. This must be sent to the client +* once upon connection to configure the UI. +*/ +class InitUIPacket : public GamePacketNew< FFXIVIpcInitUI > +{ +public: + InitUIPacket( Entity::PlayerPtr player ) : + GamePacketNew< FFXIVIpcInitUI >( player->getId(), player->getId() ) + { + initialize( player ); + }; + +private: + void initialize( Entity::PlayerPtr player ) + { + m_data.contentId = player->getContentId(); + + // TODO: Support rested experience. + m_data.restedExp = 0; + //m_data.padding = 0x100; + m_data.charId = player->getId(); + m_data.race = player->getLookAt( Common::CharaLook::Race ); + m_data.tribe = player->getLookAt( Common::CharaLook::Tribe ); + m_data.gender = player->getLookAt( Common::CharaLook::Gender ); + m_data.currentClass = static_cast< Common::ClassJob >( player->getClass() ); + m_data.currentJob = static_cast< Common::ClassJob >( player->getClass() ); + m_data.deity = static_cast< Common::GuardianDeity >( player->getGuardianDeity() ); + m_data.namedayMonth = player->getBirthMonth(); + m_data.namedayDay = player->getBirthDay(); + // TODO: Support grand company status. + m_data.grandCompany = static_cast< Common::GrandCompany >( player->getStartTown() ); + //m_data.gcRank = GCRank::None; + + // TODO: Support starting city. + //m_data.startCity = Town::Gridania; + m_data.homepoint = player->getHomepoint(); + + memset( &m_data.name[0], 0, sizeof( m_data.name ) ); + + sprintf( &m_data.name[0], player->getName().c_str() ); + + memcpy( m_data.aetheryte, player->getAetheryteArray(), sizeof ( m_data.aetheryte ) ); + + // Set the class levels and exp. + for( uint8_t i = 0; i < 25; i++ ) + { + m_data.levels[i] = player->getClassArray()[i]; + m_data.exp[i] = player->getExpArray()[i]; + } + + memcpy( m_data.unlockBitmask, player->getUnlockBitmask(), sizeof( m_data.unlockBitmask ) ); + + memcpy( m_data.discovery, player->getDiscoveryBitmask(), sizeof( m_data.discovery ) ); + + memcpy( m_data.howto, player->getHowToArray(), sizeof( m_data.howto ) ); + + + //memset( m_data.unlockBitmask, 0xFF, sizeof( m_data.unlockBitmask ) ); + //memset( m_data.unknown_0344, 0xFF, sizeof( m_data.unknown_0344 ) ); + //memset( m_data.unknown_F4, 0xFF, sizeof( m_data.unknown_F4 ) ); + //m_data.pos.x = player->getPos().getX(); + //m_data.pos.y = player->getPos().getY(); + //m_data.pos.z = player->getPos().getZ(); + + }; +}; + +} +} +} +} + +#endif /*_CORE_NETWORK_PACKETS_CINITUIPACKET_H*/ diff --git a/src/servers/Server_Zone/Inventory.cpp b/src/servers/Server_Zone/Inventory.cpp new file mode 100644 index 00000000..e6c6e47a --- /dev/null +++ b/src/servers/Server_Zone/Inventory.cpp @@ -0,0 +1,883 @@ +#include +#include +#include +#include +#include + +#include "Inventory.h" + +#include "Player.h" + +#include "ItemContainer.h" +#include "Item.h" + +#include "ServerNoticePacket.h" + +#include + +#include "Forwards.h" +#include "ActorControlPacket143.h" + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; +using namespace Core::Network; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::Inventory::Inventory( Core::Entity::PlayerPtr pOwner ) +{ + + m_pOwner = pOwner; + + // shortcut for setting up inventory + // TODO: use a loop to set theese up? + auto setupContainer = []( InventoryMap& map, InventoryType type ) + { map[type] = ItemContainerPtr( new ItemContainer( type ) ); }; + + // main bags + setupContainer( m_inventoryMap, Bag0 ); + setupContainer( m_inventoryMap, Bag1 ); + setupContainer( m_inventoryMap, Bag2 ); + setupContainer( m_inventoryMap, Bag3 ); + + // gear set + setupContainer( m_inventoryMap, GearSet0 ); + + // gil contianer + setupContainer( m_inventoryMap, Currency ); + + // crystals?? + setupContainer( m_inventoryMap, Crystal ); + //m_inventoryMap[0x07D3] = ItemContainerPtr( new ItemContainer( UNKNOWN_0 ) ); + //m_inventoryMap[0x07D8] = ItemContainerPtr( new ItemContainer( UNKNOWN_1 ) ); + + // armory weapons - 0 + setupContainer( m_inventoryMap, ArmoryMain ); + + // armory offhand - 1 + setupContainer( m_inventoryMap, ArmoryOff ); + + //armory head - 2 + setupContainer( m_inventoryMap, ArmoryHead ); + + //armory body - 3 + setupContainer( m_inventoryMap, ArmoryBody ); + + //armory hand - 4 + setupContainer( m_inventoryMap, ArmoryHand ); + + //armory waist - 5 + setupContainer( m_inventoryMap, ArmoryWaist ); + + //armory legs - 6 + setupContainer( m_inventoryMap, ArmoryLegs ); + + //armory feet - 7 + setupContainer( m_inventoryMap, ArmoryFeet ); + + //neck + setupContainer( m_inventoryMap, ArmotyNeck ); + + //earring + setupContainer( m_inventoryMap, ArmoryEar ); + + //wrist + setupContainer( m_inventoryMap, ArmoryWrist ); + + //armory rings - 11 + setupContainer( m_inventoryMap, ArmoryRing ); + + //soul crystals - 13 + setupContainer( m_inventoryMap, ArmorySoulCrystal ); +} + + +Core::Inventory::~Inventory() +{ +} + +Core::Inventory::InvSlotPairVec Core::Inventory::getSlotsOfItemsInInventory( uint32_t catalogId ) +{ + InvSlotPairVec outVec; + for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) + { + auto inv = m_inventoryMap[i]; + for( auto item : inv->getItemMap() ) + { + if( item.second && item.second->getId() == catalogId ) + outVec.push_back( std::make_pair( i, item.first ) ); + } + } + return outVec; +} + +Core::Inventory::InvSlotPair Core::Inventory::getFreeBagSlot() +{ + for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) + { + int16_t freeSlot = m_inventoryMap[i]->getFreeSlot(); + + if( freeSlot != -1 ) + return std::make_pair( i, freeSlot ); + } + // no room in inventory + return std::make_pair( 0, -1 ); +} + +Core::ItemPtr Core::Inventory::getItemAt( uint16_t containerId, uint8_t slotId ) +{ + return m_inventoryMap[containerId]->getItem( slotId ); +} + +Core::ItemPtr Core::Inventory::createItem( uint32_t catalogId, uint8_t quantity ) +{ + auto itemInfo = g_exdData.getItemInfo( catalogId ); + + uint8_t itemAmount = quantity; + + if( itemInfo->stack_size == 1 ) + { + itemAmount = 1; + } + + if( !itemInfo ) + return nullptr; + + uint8_t flags = 0; + + std::string itemName( itemInfo->name ); + + ItemPtr pItem( new Item( catalogId ) ); + + pItem->setStackSize( itemAmount ); + pItem->setUId( g_database.getNextUId() ); + pItem->setModelIds( itemInfo->model_primary, itemInfo->model_secondary ); + pItem->setCategory( static_cast< ItemCategory >( itemInfo->ui_category ) ); + + g_database.query( " INSERT INTO charaglobalitem ( CharacterId, itemId, catalogId, stack, flags ) VALUES ( " + + std::to_string( m_pOwner->getId() ) + ", " + + std::to_string( pItem->getUId() ) + ", " + + std::to_string( pItem->getId() ) + ", " + + std::to_string( itemAmount ) + ", " + + std::to_string( flags ) + ");" ); + + return pItem; + +} + + +uint32_t Core::Inventory::getCurrency( CurrencyType type ) +{ + + auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return 0; + + return currItem->getStackSize(); + +} + +uint32_t Core::Inventory::getCrystal( CrystalType type ) +{ + + auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return 0; + + return currItem->getStackSize(); + +} + +bool Core::Inventory::addCrystal( CrystalType type, uint32_t amount ) +{ + auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + { + // TODO: map currency type to itemid + currItem = createItem( static_cast< uint8_t >( type ) + 1 ); + m_inventoryMap[Crystal]->setItem( static_cast< uint8_t >( type ) - 1, currItem ); + updateCrystalDb(); + } + + uint32_t currentAmount = currItem->getStackSize(); + + currItem->setStackSize( currentAmount + amount ); + + updateItemDb( currItem ); + + return true; + +} + +bool Core::Inventory::addCurrency( CurrencyType type, uint32_t amount ) +{ + auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + { + // TODO: map currency type to itemid + currItem = createItem( 1 ); + m_inventoryMap[Currency]->setItem( static_cast< uint8_t >( type ) - 1, currItem ); + updateCurrencyDb(); + } + + uint32_t currentAmount = currItem->getStackSize(); + + currItem->setStackSize( currentAmount + amount ); + + updateItemDb( currItem ); + + return true; + +} + +void Core::Inventory::updateCurrencyDb() +{ + int32_t firstItemPos = -1; + std::string query = "UPDATE charaitemcurrency SET "; + + for( int i = 0; i <= 11; i++ ) + { + auto currItem = m_inventoryMap[Currency]->getItem( i ); + + if( currItem ) + { + if( firstItemPos == -1 ) + firstItemPos = i; + + if( i > firstItemPos ) + query += ", "; + + query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem->getUId() ); + } + } + + query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ); + + auto curRes = g_database.query( query ); +} + + +void Core::Inventory::updateCrystalDb() +{ + int32_t firstItemPos = -1; + std::string query = "UPDATE charaitemcrystal SET "; + + for( int i = 0; i <= 11; i++ ) + { + auto currItem = m_inventoryMap[Crystal]->getItem( i ); + + if( currItem ) + { + if( firstItemPos == -1 ) + firstItemPos = i; + + if( i > firstItemPos ) + query += ", "; + + query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem->getUId() ); + } + } + + query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ); + + auto curRes = g_database.query( query ); +} + +void Core::Inventory::updateBagDb( InventoryType type ) +{ + std::string query = "UPDATE charaiteminventory SET "; + + for( int i = 0; i <= 34; i++ ) + { + auto currItem = m_inventoryMap[type]->getItem( i ); + + if( i > 0 ) + query += ", "; + + query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem ? currItem->getUId() : 0 ); + } + + query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + + " AND storageId = " + std::to_string( static_cast< uint16_t >( type ) ); + + auto curRes = g_database.query( query ); +} + +bool Core::Inventory::isArmory( uint16_t containerId ) +{ + return + containerId == ArmoryBody || + containerId == ArmoryEar || + containerId == ArmoryFeet || + containerId == ArmoryHand || + containerId == ArmoryHead || + containerId == ArmoryLegs || + containerId == ArmoryMain || + containerId == ArmoryOff || + containerId == ArmoryRing || + containerId == ArmoryWaist || + containerId == ArmoryWrist; +} + +uint16_t Core::Inventory::getArmoryToEquipSlot( uint8_t slotId ) +{ + switch( slotId ) + { + case Body: + return ArmoryBody; + + case Ear: + return ArmoryEar; + + case Feet: + return ArmoryFeet; + + case Hands: + return ArmoryHand; + + case Legs: + return ArmoryLegs; + + case MainHand: + return ArmoryMain; + + case OffHand: + return ArmoryOff; + + case Ring2: + case Ring1: + return ArmoryRing; + + case Waist: + return ArmoryWaist; + + case Wrist: + return ArmoryWrist; + } + + return 0; +} + + + +bool Core::Inventory::isEquipment( uint16_t containerId ) +{ + return containerId == GearSet0; +} + + +void Core::Inventory::updateMannequinDb( InventoryType type ) +{ + std::string query = "UPDATE charaitemgearset SET "; + + for( int i = 0; i <= 13; i++ ) + { + auto currItem = m_inventoryMap[type]->getItem( i ); + + if( i > 0 ) + query += ", "; + + query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem ? currItem->getUId() : 0 ); + } + + query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + + " AND storageId = " + std::to_string( static_cast< uint16_t >( type ) ); + + g_log.Log( LoggingSeverity::debug, query ); + auto curRes = g_database.query( query ); +} + + +void Core::Inventory::updateItemDb( Core::ItemPtr pItem ) const +{ + g_database.query( "UPDATE charaglobalitem SET stack = " + std::to_string( pItem->getStackSize() ) + " " + + // TODO: add other attributes + " WHERE itemId = " + std::to_string( pItem->getUId() ) ); +} + +bool Core::Inventory::removeCurrency( CurrencyType type, uint32_t amount ) +{ + + auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return false; + + uint32_t currentAmount = currItem->getStackSize(); + if( amount > currentAmount ) + currItem->setStackSize( 0 ); + else + currItem->setStackSize( currentAmount - amount ); + + updateItemDb( currItem ); + + return true; +} + +bool Core::Inventory::removeCrystal( CrystalType type, uint32_t amount ) +{ + + auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return false; + + uint32_t currentAmount = currItem->getStackSize(); + if( amount > currentAmount ) + currItem->setStackSize( 0 ); + else + currItem->setStackSize( currentAmount - amount ); + + updateItemDb( currItem ); + + return true; +} + +bool Core::Inventory::isObtainable( uint32_t catalogId, uint16_t quantity ) +{ + + return true; +} + + +int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity ) +{ + + auto itemInfo = g_exdData.getItemInfo( catalogId ); + + if( !itemInfo ) + { + return -1; + } + + int16_t rSlotId = -1; + + //if( itemInfo->stack_size > 1 ) + //{ + // auto itemList = this->getSlotsOfItemsInInventory( catalogId ); + // // TODO: this is a stacked item so we need to see if the item is already in inventory and + // // check how much free space we have on existing stacks before looking for empty slots. + //} + //else + { + auto freeSlot = this->getFreeBagSlot(); + inventoryId = freeSlot.first; + rSlotId = freeSlot.second; + + if( rSlotId == -1 ) + return -1; + } + + auto item = createItem( catalogId, quantity ); + + if( rSlotId != -1 ) + { + + m_inventoryMap[inventoryId]->setItem( rSlotId, item ); + + g_database.query( "UPDATE charaiteminventory SET container_" + std::to_string( rSlotId ) + " = " + std::to_string( item->getUId() ) + + " WHERE storageId = " + std::to_string( inventoryId ) + + " AND CharacterId = " + std::to_string( m_pOwner->getId() ) ); + + GamePacketNew< FFXIVIpcUpdateInventorySlot > invUpPacket( m_pOwner->getId() ); + invUpPacket.data().containerId = inventoryId; + invUpPacket.data().catalogId = catalogId; + invUpPacket.data().quantity = item->getStackSize(); + invUpPacket.data().hqFlag = item->isHq() ? 1 : 0; + invUpPacket.data().slot = rSlotId; + invUpPacket.data().condition = 30000; + m_pOwner->queuePacket( invUpPacket ); + + m_pOwner->queuePacket( ActorControlPacket143( m_pOwner->getId(), ItemObtainIcon, catalogId, item->getStackSize() ) ); + + } + + return rSlotId; + +} + +void Core::Inventory::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) +{ + + auto tmpItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); + auto& itemMap = m_inventoryMap[fromInventoryId]->getItemMap(); + + if( tmpItem == nullptr ) + return; + + itemMap[fromSlotId].reset(); + + m_inventoryMap[toInventoryId]->setItem( toSlot, tmpItem ); + + if( toInventoryId != GearSet0 ) + updateBagDb( static_cast< InventoryType >( toInventoryId ) ); + + if( fromInventoryId != GearSet0 && fromInventoryId != toInventoryId ) + updateBagDb( static_cast< InventoryType >( fromInventoryId ) ); + + if( static_cast< InventoryType >( toInventoryId ) == GearSet0 ) + { + m_pOwner->equipItem( static_cast< EquipSlot >( toSlot ), tmpItem, true ); + updateMannequinDb( static_cast< InventoryType >( toInventoryId ) ); + } + + if( static_cast< InventoryType >( fromInventoryId ) == GearSet0 ) + { + m_pOwner->unequipItem( static_cast< EquipSlot >( fromSlotId ), tmpItem ); + updateMannequinDb( static_cast< InventoryType >( fromInventoryId ) ); + } + + +} + +bool Core::Inventory::updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem ) +{ + auto containerType = getContainerType( containerId ); + + m_inventoryMap[containerId]->setItem( slotId, pItem ); + + switch( containerType ) + { + case Armory: + case CurrencyCrystal: + case Bag: + { + updateBagDb( static_cast< InventoryType >( containerId ) ); + break; + } + + case GearSet: + { + if( pItem ) + m_pOwner->equipItem( static_cast< EquipSlot >( slotId ), pItem, true ); + else + m_pOwner->unequipItem( static_cast< EquipSlot >( slotId ), pItem ); + + updateMannequinDb( static_cast< InventoryType >( containerId ) ); + break; + } + default: + break; + } + + return true; +} + +void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) +{ + auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); + auto toItem = m_inventoryMap[toInventoryId]->getItem( toSlot ); + auto& itemMap = m_inventoryMap[fromInventoryId]->getItemMap(); + + if( fromItem == nullptr || toItem == nullptr ) + return; + + // An item is being moved from bag0-3 to equippment, meaning + // the swapped out item will be placed in the matching armory. + if( isEquipment( toInventoryId ) + && !isEquipment( fromInventoryId ) + && !isArmory( fromInventoryId ) ) + { + updateContainer( fromInventoryId, fromSlotId, nullptr ); + fromInventoryId = getArmoryToEquipSlot( toSlot ); + fromSlotId = m_inventoryMap[fromInventoryId]->getFreeSlot(); + } + + auto containerTypeFrom = getContainerType( fromInventoryId ); + auto containerTypeTo = getContainerType( toInventoryId ); + + updateContainer( toInventoryId, toSlot, fromItem ); + updateContainer( fromInventoryId, fromSlotId, toItem ); +} + +void Core::Inventory::discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ) +{ + // i am not entirely sure how this should be generated or if it even is important for us... + uint32_t transactionId = 1; + + auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); + + m_inventoryMap[fromInventoryId]->removeItem( fromSlotId ); + updateContainer( fromInventoryId, fromSlotId, nullptr ); + + GamePacketNew< FFXIVIpcInventoryTransaction > invTransPacket( m_pOwner->getId() ); + invTransPacket.data().transactionId = transactionId; + invTransPacket.data().ownerId = m_pOwner->getId(); + invTransPacket.data().storageId = fromInventoryId; + invTransPacket.data().catalogId = fromItem->getId(); + invTransPacket.data().stackSize = fromItem->getStackSize(); + invTransPacket.data().slotId = fromSlotId; + invTransPacket.data().type = 7; + m_pOwner->queuePacket( invTransPacket ); + + GamePacketNew< FFXIVIpcInventoryTransactionFinish > invTransFinPacket( m_pOwner->getId() ); + invTransFinPacket.data().transactionId = transactionId; + invTransFinPacket.data().transactionId1 = transactionId; + m_pOwner->queuePacket( invTransFinPacket ); +} + +Core::ItemPtr Core::Inventory::loadItem( uint64_t uId ) +{ + // load actual item + auto itemRes = g_database.query( "SELECT catalogId, stack, flags FROM charaglobalitem WHERE itemId = " + std::to_string( uId ) + ";" ); + if( !itemRes ) + return nullptr; + + try + { + Db::Field *itemField = itemRes->fetch(); + auto itemInfo = g_exdData.getItemInfo( itemField[0].getUInt32() ); + bool isHq = itemField[2].getUInt8() == 1 ? true : false; + ItemPtr pItem( new Item( uId, + itemInfo->id, + itemInfo->model_primary, + itemInfo->model_secondary, + static_cast< ItemCategory >( itemInfo->ui_category ), + isHq ) ); + pItem->setStackSize( itemField[1].getUInt32() ); + + return pItem; + } + catch( ... ) + { + return nullptr; + } +} + +bool Core::Inventory::load() +{ + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // load active gearset + auto res = g_database.query( "SELECT storageId, container_0, container_1, container_2, container_3, " + "container_4, container_5, container_6, container_7, " + "container_8, container_9, container_10, container_11, " + "container_12, container_13 " + "FROM charaitemgearset " \ + "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ + "ORDER BY storageId ASC;" ); + if( !res ) + return false; + + Db::Field *field = res->fetch(); + + do + { + uint16_t storageId = field[0].getUInt16(); + + for( int i = 1; i <= 14; i++ ) + { + uint64_t uItemId = field[i].getUInt64(); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; + m_pOwner->equipItem( static_cast< EquipSlot >( i - 1 ), pItem, false ); + } + } while( res->nextRow() ); + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Load Bags + auto bagRes = g_database.query( "SELECT storageId, " + "container_0, container_1, container_2, container_3, container_4, " + "container_5, container_6, container_7, container_8, container_9, " + "container_10, container_11, container_12, container_13, container_14, " + "container_15, container_16, container_17, container_18, container_19, " + "container_20, container_21, container_22, container_23, container_24, " + "container_25, container_26, container_27, container_28, container_29, " + "container_30, container_31, container_32, container_33, container_34 " + "FROM charaiteminventory " \ + "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ + "ORDER BY storageId ASC;" ); + if( !bagRes ) + return false; + + Db::Field *bagField = bagRes->fetch(); + + do + { + uint16_t storageId = bagField[0].getUInt16(); + for( int i = 1; i <= 25; i++ ) + { + uint64_t uItemId = bagField[i].getUInt64(); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; + } + } while( bagRes->nextRow() ); + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Load Currency + auto curRes = g_database.query( "SELECT storageId, " + "container_0, container_1, container_2, container_3, container_4, " + "container_5, container_6, container_7, container_8, container_9, " + "container_10, container_11 " + "FROM charaitemcurrency " \ + "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ + "ORDER BY storageId ASC;" ); + if( !curRes ) + return false; + + Db::Field *curField = curRes->fetch(); + + do + { + uint16_t storageId = curField[0].getUInt16(); + for( int i = 1; i <= 12; i++ ) + { + uint64_t uItemId = curField[i].getUInt64(); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; + } + } while( curRes->nextRow() ); + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Load Crystals + auto crystalRes = g_database.query( "SELECT storageId, " + "container_0, container_1, container_2, container_3, container_4, " + "container_5, container_6, container_7, container_8, container_9, " + "container_10, container_11, container_12, container_13, container_14, " + "container_15, container_16, container_17 " + "FROM charaitemcrystal " \ + "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ + "ORDER BY storageId ASC;" ); + if( !crystalRes ) + return false; + + Db::Field *crystalField = crystalRes->fetch(); + + do + { + uint16_t storageId = crystalField[0].getUInt16(); + for( int i = 1; i <= 17; i++ ) + { + uint64_t uItemId = crystalField[i].getUInt64(); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; + } + } while( crystalRes->nextRow() ); + + return true; +} + + +void Core::Inventory::send() +{ + InventoryMap::iterator it; + + int count = 0; + for( it = m_inventoryMap.begin(); it != m_inventoryMap.end(); ++it, count++ ) + { + + auto pMap = it->second->getItemMap(); + auto itM = pMap.begin(); + + for( ; itM != pMap.end(); ++itM ) + { + if( !itM->second ) + return; + + if( it->second->getId() == InventoryType::Currency || it->second->getId() == InventoryType::Crystal ) + { + GamePacketNew< FFXIVIpcCurrencyCrystalInfo > currencyInfoPacket( m_pOwner->getId() ); + currencyInfoPacket.data().sequence = count; + currencyInfoPacket.data().catalogId = itM->second->getId(); + currencyInfoPacket.data().unknown = 1; + currencyInfoPacket.data().quantity = itM->second->getStackSize(); + currencyInfoPacket.data().containerId = it->second->getId(); + currencyInfoPacket.data().slot = 0; + m_pOwner->queuePacket( currencyInfoPacket ); + } + else + { + GamePacketNew< FFXIVIpcItemInfo > itemInfoPacket( m_pOwner->getId() ); + itemInfoPacket.data().sequence = count; + itemInfoPacket.data().containerId = it->second->getId(); + itemInfoPacket.data().slot = itM->first; + itemInfoPacket.data().quantity = itM->second->getStackSize(); + itemInfoPacket.data().catalogId = itM->second->getId(); + itemInfoPacket.data().condition = 30000; + itemInfoPacket.data().spiritBond = 0; + itemInfoPacket.data().hqFlag = itM->second->isHq() ? 1 : 0; + m_pOwner->queuePacket( itemInfoPacket ); + } + } + + GamePacketNew< FFXIVIpcContainerInfo > containerInfoPacket( m_pOwner->getId() ); + containerInfoPacket.data().sequence = count; + containerInfoPacket.data().numItems = it->second->getEntryCount(); + containerInfoPacket.data().containerId = it->second->getId(); + m_pOwner->queuePacket( containerInfoPacket ); + + + } + +} + +uint8_t Core::Inventory::getFreeSlotsInBags() +{ + uint8_t slots = 0; + for( uint8_t container : { 0, 1, 2, 3 } ) + { + slots += 25 - m_inventoryMap[container]->getEntryCount(); + } + return slots; +} + + +Core::Inventory::ContainerType Core::Inventory::getContainerType( uint32_t containerId ) +{ + if( containerId < 5 ) + { + return Bag; + } + else if( containerId < 2000 ) + { + return GearSet; + } + else if( containerId < 3200 ) + { + return CurrencyCrystal; + } + else if( containerId < 3600 ) + { + return Armory; + } + else + { + return Unknown; + } +} diff --git a/src/servers/Server_Zone/Inventory.h b/src/servers/Server_Zone/Inventory.h new file mode 100644 index 00000000..b7bf0044 --- /dev/null +++ b/src/servers/Server_Zone/Inventory.h @@ -0,0 +1,196 @@ +#ifndef INVENTORY_H_ +#define INVENTORY_H_ +#include +#include +#include "Forwards.h" + +namespace Core +{ + +class ItemContainer; + +typedef std::map< uint16_t, ItemContainerPtr > InventoryMap; +class Inventory +{ +public: + Inventory( Entity::PlayerPtr pOwner ); + ~Inventory(); + + enum ContainerType : uint16_t + { + Unknown = 0, + Bag = 1, + GearSet = 2, + CurrencyCrystal = 3, + Armory = 4 + }; + + enum InventoryType : uint16_t + { + Bag0 = 0, + Bag1 = 1, + Bag2 = 2, + Bag3 = 3, + + GearSet0 = 1000, + GearSet1 = 1001, + + Currency = 2000, + Crystal = 2001, + //UNKNOWN_0 = 2003, + KeyItem = 2004, + DamagedGear = 2007, + //UNKNOWN_1 = 2008, + + ArmoryOff = 3200, + ArmoryHead = 3201, + ArmoryBody = 3202, + ArmoryHand = 3203, + ArmoryWaist = 3204, + ArmoryLegs = 3205, + ArmoryFeet = 3206, + ArmotyNeck = 3207, + ArmoryEar = 3208, + ArmoryWrist = 3209, + ArmoryRing = 3300, + + ArmorySoulCrystal = 3400, + ArmoryMain = 3500, + + RetainerBag0 = 10000, + RetainerBag1 = 10001, + RetainerBag2 = 10002, + RetainerBag3 = 10003, + RetainerBag4 = 10004, + RetainerBag5 = 10005, + RetainerBag6 = 10006, + RetainerEquippedGear = 11000, + RetainerGil = 12000, + RetainerCrystal = 12001, + RetainerMarket = 12002, + + FreeCompanyBag0 = 20000, + FreeCompanyBag1 = 20001, + FreeCompanyBag2 = 20002, + FreeCompanyGil = 22000, + FreeCompanyCrystal = 22001 + }; + + enum CurrencyType : uint8_t + { + Gil = 0x01, + StormSeal = 0x02, + SerpentSeal = 0x03, + FlameSeal = 0x04, + TomestonePhilo = 0x05, + TomestoneMytho = 0x06, + WolfMark = 0x07, + TomestoneSold = 0x08, + AlliedSeal = 0x09, + TomestonePoet = 0x0A, + Mgp = 0x0B, + TomestoneLaw = 0x0C, + TomestoneEso = 0x0D, + TomestoneLore = 0x0E + }; + + enum CrystalType : uint8_t + { + FireShard = 0x01, + IceShard = 0x02, + WindShard = 0x03, + EarthShard = 0x04, + LightningShard = 0x05, + WaterShard = 0x06, + + FireCrystal = 0x07, + IceCrystal = 0x08, + WindCrystal = 0x09, + EarthCrystal = 0x0A, + LightningCrystal = 0x0B, + WaterCrystal = 0x0C, + + FireCluster = 0x0D, + IceCluster = 0x0E, + WindCluster = 0x0F, + EarthCluster = 0x10, + LightningCluster = 0x11, + WaterCluster = 0x12 + }; + + enum EquipSlot : uint8_t + { + MainHand = 0, + OffHand = 1, + Head = 2, + Body = 3, + Hands = 4, + Waist = 5, + Legs = 6, + Feet = 7, + Neck = 8, + Ear = 9, + Wrist = 10, + Ring1 = 11, + Ring2 = 12, + SoulCrystal = 13, + }; + typedef std::pair< uint16_t, int8_t > InvSlotPair; + typedef std::vector< InvSlotPair > InvSlotPairVec; + + InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); + InvSlotPair getFreeBagSlot(); + int16_t addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity = 1 ); + void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); + void swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); + void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ); + + ItemPtr createItem( uint32_t catalogId, uint8_t quantity = 1 ); + + ItemPtr loadItem( uint64_t uId ); + + ItemPtr getItemAt( uint16_t containerId, uint8_t slotId ); + + bool updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem ); + + /*! return the current amount of currency of type */ + uint32_t getCurrency( CurrencyType type ); + /*! add amount to the current of type */ + bool addCurrency( CurrencyType type, uint32_t amount ); + /*! remove amount from the currency of type */ + bool removeCurrency( CurrencyType type, uint32_t amount ); + + void updateCurrencyDb(); + void updateBagDb( InventoryType type ); + void updateMannequinDb( InventoryType type ); + void updateItemDb( ItemPtr pItem ) const; + + bool isArmory( uint16_t containerId ); + bool isEquipment( uint16_t containerId ); + uint16_t getArmoryToEquipSlot( uint8_t slotId ); + + /*! return the crystal amount of currency of type */ + uint32_t getCrystal( CrystalType type ); + /*! add amount to the crystal of type */ + bool addCrystal( CrystalType type, uint32_t amount ); + /*! remove amount from the crystals of type */ + bool removeCrystal( CrystalType type, uint32_t amount ); + bool isObtainable( uint32_t catalogId, uint16_t quantity ); + + void updateCrystalDb(); + + bool load(); + + void send(); + + uint8_t getFreeSlotsInBags(); + + ContainerType getContainerType( uint32_t containerId ); + +private: + Entity::PlayerPtr m_pOwner; + InventoryMap m_inventoryMap; +}; + +} +#endif diff --git a/src/servers/Server_Zone/Item.cpp b/src/servers/Server_Zone/Item.cpp new file mode 100644 index 00000000..e07083af --- /dev/null +++ b/src/servers/Server_Zone/Item.cpp @@ -0,0 +1,105 @@ +#include +#include +#include "Item.h" + +extern Core::Data::ExdData g_exdData; + +Core::Item::Item() +{ + +} + +Core::Item::Item( uint32_t catalogId ) : + m_id( catalogId ), + m_isHq( false ) +{ + +} + +Core::Item::Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t model2, Common::ItemCategory categoryId, bool isHq ) : + m_id( catalogId ), + m_uId( uId ), + m_category( categoryId ), + m_model1( model1 ), + m_model2( model2 ), + m_isHq( isHq ) +{ + auto itemInfo = g_exdData.getItemInfo( catalogId ); + m_delayMs = itemInfo->delayMs; +} + +Core::Item::~Item() +{ + +} + +uint16_t Core::Item::getDelay() const +{ + return m_delayMs; +} + +uint32_t Core::Item::getId() const +{ + return m_id; +} + +void Core::Item::setId( uint32_t id ) +{ + m_id = id; +} + +uint64_t Core::Item::getUId() const +{ + return m_uId; +} + +void Core::Item::setUId( uint64_t id ) +{ + m_uId = id; +} + +void Core::Item::setStackSize( uint32_t size ) +{ + m_stackSize = size; +} + +uint32_t Core::Item::getStackSize() const +{ + return m_stackSize; +} + +void Core::Item::setCategory( Common::ItemCategory categoryId ) +{ + m_category = categoryId; +} + +Core::Common::ItemCategory Core::Item::getCategory() const +{ + return m_category; +} + +void Core::Item::setModelIds( uint64_t model1, uint64_t model2 ) +{ + m_model1 = model1; + m_model2 = model2; +} + +uint64_t Core::Item::getModelId1() const +{ + return m_model1; +} + +uint64_t Core::Item::getModelId2() const +{ + return m_model2; +} + +bool Core::Item::isHq() const +{ + return m_isHq; +} + +void Core::Item::setHq( bool isHq ) +{ + m_isHq = isHq; +} diff --git a/src/servers/Server_Zone/Item.h b/src/servers/Server_Zone/Item.h new file mode 100644 index 00000000..886680a1 --- /dev/null +++ b/src/servers/Server_Zone/Item.h @@ -0,0 +1,66 @@ +#ifndef _ITEM_H_ +#define _ITEM_H_ + +#include + +namespace Core { + +class Item +{ + +public: + Item(); + Item( uint32_t catalogId ); + Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t model2, Common::ItemCategory categoryId, bool isHq = false ); + ~Item(); + + uint32_t getId() const; + + void setId( uint32_t id ); + + uint64_t getUId() const; + + void setUId( uint64_t id ); + + void setStackSize( uint32_t size ); + + uint32_t getStackSize() const; + + void setCategory( Common::ItemCategory categoryId ); + + Common::ItemCategory getCategory() const; + + void setModelIds( uint64_t model1, uint64_t model2 ); + + uint64_t getModelId1() const; + + uint64_t getModelId2() const; + + bool isHq() const; + + void setHq( bool isHq ); + uint16_t getDelay() const; + + +protected: + uint32_t m_id; + + uint64_t m_uId; + + Common::ItemCategory m_category; + + uint32_t m_stackSize; + std::vector< uint8_t > m_classJobList; + + uint64_t m_model1; + uint64_t m_model2; + + bool m_isHq; + + uint16_t m_delayMs; + +}; + +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/ItemContainer.cpp b/src/servers/Server_Zone/ItemContainer.cpp new file mode 100644 index 00000000..76d97eb9 --- /dev/null +++ b/src/servers/Server_Zone/ItemContainer.cpp @@ -0,0 +1,99 @@ +#include "Forwards.h" +#include "ItemContainer.h" + +#include +#include +#include + +#include "Player.h" + +#include "Item.h" + +extern Core::Logger g_log; +extern Core::Db::Database g_database; + +Core::ItemContainer::ItemContainer( uint16_t locationId ) : + m_id( locationId ), + m_size( 25 ) +{ + +} + +Core::ItemContainer::~ItemContainer() +{ + +} + +uint16_t Core::ItemContainer::getId() const +{ + return m_id; +} + +uint8_t Core::ItemContainer::getEntryCount() const +{ + return static_cast< uint8_t >( m_itemMap.size() ); +} + +void Core::ItemContainer::removeItem( uint8_t slotId ) +{ + ItemMap::iterator it = m_itemMap.find( slotId ); + + if( it != m_itemMap.end() ) + { + + + g_database.execute( "DELETE FROM charaglobalitem " \ + "WHERE itemId = %i ", + it->second->getUId() ); + + m_itemMap.erase( it ); + + g_log.debug( "Dropped item from slot " + std::to_string( slotId ) ); + } + else + { + g_log.debug( "Item could not be dropped from slot " + std::to_string( slotId ) ); + } +} + +Core::ItemMap & Core::ItemContainer::getItemMap() +{ + return m_itemMap; +} + +const Core::ItemMap & Core::ItemContainer::getItemMap() const +{ + return m_itemMap; +} + +int16_t Core::ItemContainer::getFreeSlot() +{ + for( unsigned char slotId = 0; slotId < m_size; slotId++ ) + { + ItemMap::iterator it = m_itemMap.find( slotId ); + if( it == m_itemMap.end() || + it->second == nullptr ) + return slotId; + } + return -1; +} + +Core::ItemPtr Core::ItemContainer::getItem( uint8_t slotId ) +{ + + if( ( slotId > m_size ) || ( slotId == -1 ) ) + { + g_log.error( "Slot out of range " + std::to_string( slotId ) ); + return nullptr; + } + + return m_itemMap[slotId]; +} + +void Core::ItemContainer::setItem( uint8_t slotId, Core::ItemPtr pItem ) +{ + if( ( slotId > m_size ) ) + return; + + m_itemMap[slotId] = pItem; +} diff --git a/src/servers/Server_Zone/ItemContainer.h b/src/servers/Server_Zone/ItemContainer.h new file mode 100644 index 00000000..7fe8b7aa --- /dev/null +++ b/src/servers/Server_Zone/ItemContainer.h @@ -0,0 +1,48 @@ +#pragma once +#ifndef _ITEMCONTAINER_H_ +#define _ITEMCONTAINER_H_ + +#include + +#include + +#include "Forwards.h" + +namespace Core +{ + + typedef std::map< uint8_t, ItemPtr > ItemMap; + + class ItemContainer + { + + public: + ItemContainer(uint16_t locationId); + ~ItemContainer(); + + uint16_t getId() const; + + uint8_t getEntryCount() const; + + void removeItem( uint8_t slotId ); + + ItemMap& getItemMap(); + + const ItemMap& getItemMap() const; + + ItemPtr getItem(uint8_t slotId); + + void setItem(uint8_t slotId, ItemPtr item); + + int16_t getFreeSlot(); + + private: + uint16_t m_id; + uint8_t m_size; + ItemMap m_itemMap; + Entity::PlayerPtr m_pOwner; + }; + +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/ModelEquipPacket.h b/src/servers/Server_Zone/ModelEquipPacket.h new file mode 100644 index 00000000..8e826c3a --- /dev/null +++ b/src/servers/Server_Zone/ModelEquipPacket.h @@ -0,0 +1,44 @@ +#ifndef _MODELEQUIPPACKET_H +#define _MODELEQUIPPACKET_H + +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The update model packet. +*/ +class ModelEquipPacket : + public GamePacketNew +{ +public: + ModelEquipPacket( Entity::PlayerPtr player ) : + GamePacketNew( player->getId(), player->getId() ) + { + initialize( player ); + }; + +private: + void initialize( Entity::PlayerPtr player ) + { + m_data.mainWeapon = player->getModelMainWeapon(); + m_data.offWeapon = player->getModelSubWeapon(); + m_data.models[0] = player->getModelForSlot( Inventory::EquipSlot::Head ); + m_data.models[1] = player->getModelForSlot( Inventory::EquipSlot::Body ); + m_data.models[2] = player->getModelForSlot( Inventory::EquipSlot::Hands ); + m_data.models[3] = player->getModelForSlot( Inventory::EquipSlot::Legs ); + m_data.models[4] = player->getModelForSlot( Inventory::EquipSlot::Feet ); + }; +}; + +} +} +} +} + +#endif /*_MODELEQUIPPACKET_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/MoveActorPacket.h b/src/servers/Server_Zone/MoveActorPacket.h new file mode 100644 index 00000000..c2d042c0 --- /dev/null +++ b/src/servers/Server_Zone/MoveActorPacket.h @@ -0,0 +1,51 @@ +#ifndef _MOVEACTORPACKET_H +#define _MOVEACTORPACKET_H + +#include +#include +#include +#include "Player.h" +#include "Forwards.h" + + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Client UI Initialization packet. This must be sent to the client +* once upon connection to configure the UI. +*/ +class MoveActorPacket : + public GamePacketNew +{ +public: + MoveActorPacket( Entity::ActorPtr actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) : + GamePacketNew( actor->getId(), actor->getId() ) + { + initialize( actor, unk1, unk2, unk3, unk4 ); + }; + +private: + void initialize( Entity::ActorPtr actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) + { + + m_data.rotation = Math::Util::floatToUInt8Rot( actor->getRotation() ); + m_data.unknown_1 = unk1; + m_data.unknown_2 = unk2; + m_data.unknown_3 = unk3; + m_data.unknown_4 = unk4; + m_data.posX = Math::Util::floatToUInt16( actor->getPos().x ); + m_data.posY = Math::Util::floatToUInt16( actor->getPos().y ); + m_data.posZ = Math::Util::floatToUInt16( actor->getPos().z ); + + }; +}; + +} +} +} +} + +#endif /*_MOVEACTORPACKET_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/PacketHandlers.cpp b/src/servers/Server_Zone/PacketHandlers.cpp new file mode 100644 index 00000000..fdd1f42f --- /dev/null +++ b/src/servers/Server_Zone/PacketHandlers.cpp @@ -0,0 +1,1134 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "GameConnection.h" + +#include "Session.h" +#include "Zone.h" +#include "ZonePosition.h" +#include "ServerZone.h" +#include "ZoneMgr.h" + +#include "InitUIPacket.h" +#include "PingPacket.h" +#include "MoveActorPacket.h" +#include "ChatPacket.h" +#include "ServerNoticePacket.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "ActorControlPacket144.h" +#include "EventStartPacket.h" +#include "EventFinishPacket.h" +#include "PlayerStateFlagsPacket.h" + + +#include "GameCommandHandler.h" + +#include "Player.h" +#include "Inventory.h" + +#include "Forwards.h" + +#include "EventHelper.h" + +#include "Action.h" +#include "ActionTeleport.h" + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::ServerZone g_serverZone; +extern Core::ZoneMgr g_zoneMgr; +extern Core::Data::ExdData g_exdData; +extern Core::GameCommandHandler g_gameCommandMgr; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +void Core::Network::GameConnection::fcInfoReqHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + GamePacketPtr pPe( new GamePacket( 0xDD, 0x78, pPlayer->getId(), pPlayer->getId() ) ); + pPe->setValAt< uint8_t >( 0x48, 0x01 ); + queueOutPacket( pPe ); +} + +void Core::Network::GameConnection::setSearchInfoHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t inval = pInPacket->getValAt< uint32_t >( 0x20 ); + uint32_t inval1 = pInPacket->getValAt< uint32_t >( 0x24 ); + uint64_t status = pInPacket->getValAt< uint64_t >( 0x20 ); + + uint8_t selectRegion = pInPacket->getValAt< uint8_t >( 0x31 ); + + pPlayer->setSearchInfo( selectRegion, 0, pInPacket->getStringAt( 0x32 ) ); + + pPlayer->setOnlineStatusMask( status ); + + if( pPlayer->isNewAdventurer() && !( inval & 0x01000000 ) ) + // mark player as not new adventurer anymore + pPlayer->setNewAdventurer( false ); + else if( inval & 0x01000000 ) + // mark player as new adventurer + pPlayer->setNewAdventurer( true ); + + GamePacketNew< FFXIVIpcSetOnlineStatus > statusPacket( pPlayer->getId() ); + statusPacket.data().onlineStatusFlags = status; + queueOutPacket( statusPacket ); + + GamePacketNew< FFXIVIpcSetSearchInfo > searchInfoPacket( pPlayer->getId() ); + searchInfoPacket.data().onlineStatusFlags = status; + searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion(); + sprintf( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() ); + queueOutPacket( searchInfoPacket ); + + pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon, + static_cast< uint8_t >( pPlayer->getOnlineStatus() ) ), + true ); +} + +void Core::Network::GameConnection::reqSearchInfoHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + GamePacketNew< FFXIVIpcInitSearchInfo > searchInfoPacket( pPlayer->getId() ); + searchInfoPacket.data().onlineStatusFlags = pPlayer->getOnlineStatusMask(); + searchInfoPacket.data().selectRegion = pPlayer->getSearchSelectRegion(); + sprintf( searchInfoPacket.data().searchMessage, pPlayer->getSearchMessage() ); + queueOutPacket( searchInfoPacket ); +} + +void Core::Network::GameConnection::linkshellListHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + GamePacketNew< FFXIVIpcLinkshellList > linkshellListPacket( pPlayer->getId() ); + queueOutPacket( linkshellListPacket ); +} + +// TODO: move this and the handlers to a gm command handler +enum GmCommand +{ + Pos = 0x0000, + Lv = 0x0001, + Race = 0x0002, + Tribe = 0x0003, + Sex = 0x0004, + Time = 0x0005, + Weather = 0x0006, + Call = 0x0007, + Inspect = 0x0008, + Speed = 0x0009, + Invis = 0x000D, + + Raise = 0x0010, + Kill = 0x000E, + Icon = 0x0012, + + Hp = 0x0064, + Mp = 0x0065, + Tp = 0x0066, + Gp = 0x0067, + + Item = 0x00C8, + Gil = 0x00C9, + Collect = 0x00CA, + + QuestAccept = 0x012C, + QuestCancel = 0x012D, + QuestComplete = 0x012E, + QuestIncomplete = 0x012F, + QuestSequence = 0x0130, + QuestInspect = 0x0131, + GC = 0x0154, + GCRank = 0x0155, + TeriInfo = 0x025D, + Jump = 0x025E, + JumpNpc = 0x025F, +}; +void Core::Network::GameConnection::gm1Handler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t commandId = pInPacket->getValAt< uint32_t >( 0x20 ); + uint32_t param1 = pInPacket->getValAt< uint32_t >( 0x24 ); + uint32_t param2 = pInPacket->getValAt< uint32_t >( 0x28 ); + uint32_t param3 = pInPacket->getValAt< uint32_t >( 0x38 ); + + g_log.debug( pPlayer->getName() + " used GM1 commandId: " + std::to_string( commandId ) + ", params: " + std::to_string( param1 ) + ", " + std::to_string( param2 ) + ", " + std::to_string( param3 ) ); + + Core::Entity::ActorPtr targetActor; + + if( pPlayer->getId() == param3 ) + { + targetActor = pPlayer; + } + else { + auto inRange = pPlayer->getInRangeActors(); + for( auto actor : inRange ) + { + if( actor->getId() == param3 ) + { + targetActor = actor; + } + } + } + + switch( commandId ) + { + case GmCommand::Kill: + { + targetActor->takeDamage( 9999999 ); + pPlayer->sendNotice( "Killed " + std::to_string( targetActor->getId() ) ); + break; + } + case GmCommand::QuestSequence: + { + targetActor->getAsPlayer()->updateQuest( param1, param2 ); + break; + } + case GmCommand::QuestComplete: + { + targetActor->getAsPlayer()->finishQuest( param1 ); + break; + } + case GmCommand::QuestAccept: + { + targetActor->getAsPlayer()->updateQuest( param1, 1 ); + break; + } + case GmCommand::QuestCancel: + { + targetActor->getAsPlayer()->removeQuest( param1 ); + break; + } + case GmCommand::QuestIncomplete: + { + targetActor->getAsPlayer()->unfinishQuest( param1 ); + break; + } + case GmCommand::Speed: + { + targetActor->getAsPlayer()->queuePacket( ActorControlPacket143( pPlayer->getId(), Flee, param1 ) ); + pPlayer->sendNotice( "Speed for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::Gil: + { + targetActor->getAsPlayer()->addCurrency( 1, param1 ); + pPlayer->sendNotice( "Added " + std::to_string( param1 ) + " Gil for " + targetActor->getAsPlayer()->getName() ); + break; + } + case GmCommand::Lv: + { + targetActor->getAsPlayer()->setLevel( param1 ); + pPlayer->sendNotice( "Level for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::Hp: + { + targetActor->getAsPlayer()->setHp( param1 ); + pPlayer->sendNotice( "Tp for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::Mp: + { + targetActor->getAsPlayer()->setMp( param1 ); + pPlayer->sendNotice( "Mp for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::Gp: + { + targetActor->getAsPlayer()->setHp( param1 ); + pPlayer->sendNotice( "Gp for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::Sex: + { + targetActor->getAsPlayer()->setLookAt( CharaLook::Gender, param1 ); + pPlayer->sendNotice( "Sex for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + targetActor->spawn( targetActor->getAsPlayer() ); + auto inRange = targetActor->getInRangeActors(); + for( auto actor : inRange ) + { + targetActor->getAsPlayer()->despawn( actor->getAsPlayer() ); + targetActor->getAsPlayer()->spawn( actor->getAsPlayer() ); + } + break; + } + case GmCommand::Race: + { + targetActor->getAsPlayer()->setLookAt( CharaLook::Race, param1 ); + pPlayer->sendNotice( "Race for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + targetActor->spawn( targetActor->getAsPlayer() ); + auto inRange = targetActor->getInRangeActors(); + for( auto actor : inRange ) + { + targetActor->getAsPlayer()->despawn( actor->getAsPlayer() ); + targetActor->getAsPlayer()->spawn( actor->getAsPlayer() ); + } + break; + } + case GmCommand::Tribe: + { + targetActor->getAsPlayer()->setLookAt( CharaLook::Tribe, param1 ); + pPlayer->sendNotice( "Tribe for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + targetActor->spawn( targetActor->getAsPlayer() ); + auto inRange = targetActor->getInRangeActors(); + for( auto actor : inRange ) + { + targetActor->getAsPlayer()->despawn( actor->getAsPlayer() ); + targetActor->getAsPlayer()->spawn( actor->getAsPlayer() ); + } + break; + } + case GmCommand::Item: + { + if( param2 < 1 || param2 > 99 ) + { + param2 = 1; + } + + if( ( param1 == 0xcccccccc ) ) + { + pPlayer->sendUrgent( "Syntaxerror." ); + return; + } + + if( !targetActor->getAsPlayer()->addItem( -1, param1, param2 ) ) + pPlayer->sendUrgent( "Item " + std::to_string( param1 ) + " not found..." ); + break; + } + case GmCommand::Weather: + { + targetActor->getAsPlayer()->getCurrentZone()->setWeatherOverride( param1 ); + pPlayer->sendNotice( "Weather in Zone \"" + targetActor->getAsPlayer()->getCurrentZone()->getName() + "\" of " + targetActor->getAsPlayer()->getName() + " set in range." ); + break; + } + case GmCommand::TeriInfo: + { + pPlayer->sendNotice( "ZoneId: " + std::to_string( pPlayer->getZoneId() ) + "\nName: " + pPlayer->getCurrentZone()->getName() + "\nInternalName: " + pPlayer->getCurrentZone()->getInternalName() + "\nPopCount: " + std::to_string( pPlayer->getCurrentZone()->getPopCount() ) + + "\nCurrentWeather:" + std::to_string( pPlayer->getCurrentZone()->getCurrentWeather() ) + "\nNextWeather:" + std::to_string( pPlayer->getCurrentZone()->getNextWeather() ) ); + break; + } + case GmCommand::Jump: + { + + auto inRange = pPlayer->getInRangeActors(); + for( auto actor : inRange ) + { + pPlayer->changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, targetActor->getRotation() ); + } + pPlayer->sendNotice( "Jumping to " + targetActor->getAsPlayer()->getName() + " in range." ); + break; + } + case GmCommand::Collect: + { + uint32_t gil = targetActor->getAsPlayer()->getCurrency( 1 ); + + if( gil < param1 ) + { + pPlayer->sendUrgent( "Player does not have enough Gil(" + std::to_string( gil ) + ")" ); + } + else + { + targetActor->getAsPlayer()->removeCurrency( 1, param1 ); + pPlayer->sendNotice( "Removed " + std::to_string( param1 ) + " Gil from " + targetActor->getAsPlayer()->getName() + "(" + std::to_string( gil ) + " before)" ); + } + break; + } + case GmCommand::Icon: + { + targetActor->getAsPlayer()->setOnlineStatusMask( param1 ); + + GamePacketNew< FFXIVIpcSetOnlineStatus > statusPacket( targetActor->getAsPlayer()->getId() ); + statusPacket.data().onlineStatusFlags = param1; + queueOutPacket( statusPacket ); + + GamePacketNew< FFXIVIpcSetSearchInfo > searchInfoPacket( targetActor->getAsPlayer()->getId() ); + searchInfoPacket.data().onlineStatusFlags = param1; + searchInfoPacket.data().selectRegion = targetActor->getAsPlayer()->getSearchSelectRegion(); + sprintf( searchInfoPacket.data().searchMessage, targetActor->getAsPlayer()->getSearchMessage() ); + targetActor->getAsPlayer()->queuePacket( searchInfoPacket ); + + targetActor->getAsPlayer()->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatusIcon, + static_cast< uint8_t >( pPlayer->getOnlineStatus() ) ), + true ); + pPlayer->sendNotice( "Icon for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( param1 ) ); + break; + } + case GmCommand::GC: + { + targetActor->getAsPlayer()->setGc( param1 ); + pPlayer->sendNotice( "GC for " + targetActor->getAsPlayer()->getName() + " was set to " + std::to_string( targetActor->getAsPlayer()->getGc() ) ); + break; + } + case GmCommand::GCRank: + { + targetActor->getAsPlayer()->setGcRankAt( targetActor->getAsPlayer()->getGc() - 1, param1 ); + pPlayer->sendNotice( "GC Rank for " + targetActor->getAsPlayer()->getName() + " for GC " + std::to_string( targetActor->getAsPlayer()->getGc()) + " was set to " + std::to_string( targetActor->getAsPlayer()->getGcRankArray()[targetActor->getAsPlayer()->getGc() - 1] ) ); + break; + } + + default: + pPlayer->sendUrgent( "GM1 Command not implemented: " + std::to_string( commandId ) ); + break; + } + +} + +void Core::Network::GameConnection::gm2Handler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t commandId = pInPacket->getValAt< uint32_t >( 0x20 ); + std::string param1 = pInPacket->getStringAt( 0x34 ); + + g_log.debug( pPlayer->getName() + " used GM2 commandId: " + std::to_string( commandId ) + ", params: " + param1 ); + + auto targetSession = g_serverZone.getSession( param1 ); + Core::Entity::ActorPtr targetActor; + + if( targetSession != nullptr ) + { + targetActor = targetSession->getPlayer(); + } + else + { + if( param1 == "self" ) + { + targetActor = pPlayer; + } + else + { + pPlayer->sendUrgent("Player " + param1 + " not found on this server."); + return; + } + } + + switch( commandId ) + { + case GmCommand::Raise: + { + targetActor->getAsPlayer()->resetHp(); + targetActor->getAsPlayer()->resetMp(); + targetActor->getAsPlayer()->setStatus( Entity::Actor::ActorStatus::Idle ); + targetActor->getAsPlayer()->setSyncFlag( Status ); + + targetActor->getAsPlayer()->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01, 0x01, 0, 113 ), true ); + targetActor->getAsPlayer()->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle ) ), true ); + pPlayer->sendNotice( "Raised " + targetActor->getAsPlayer()->getName()); + break; + } + case GmCommand::Jump: + { + if( targetActor->getAsPlayer()->getZoneId() != pPlayer->getZoneId() ) + { + pPlayer->setZone( targetActor->getAsPlayer()->getZoneId() ); + } + pPlayer->changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, targetActor->getRotation() ); + pPlayer->sendNotice( "Jumping to " + targetActor->getAsPlayer()->getName()); + break; + } + case GmCommand::Call: + { + if( targetActor->getAsPlayer()->getZoneId() != pPlayer->getZoneId() ) + { + targetActor->getAsPlayer()->setZone( pPlayer->getZoneId() ); + } + targetActor->getAsPlayer()->changePosition( pPlayer->getPos().x, pPlayer->getPos().y, pPlayer->getPos().z, pPlayer->getRotation() ); + pPlayer->sendNotice( "Calling " + targetActor->getAsPlayer()->getName() ); + break; + } + case GmCommand::Inspect: + { + pPlayer->sendNotice( "Name: " + targetActor->getAsPlayer()->getName() + "\nGil: " + std::to_string( targetActor->getAsPlayer()->getCurrency( 1 ) ) + "\nZone: " + targetActor->getAsPlayer()->getCurrentZone()->getName() + "(" + std::to_string( targetActor->getAsPlayer()->getZoneId() ) + ")" + + "\nClass: " + std::to_string( targetActor->getAsPlayer()->getClass() ) + "\nLevel: " + std::to_string( targetActor->getAsPlayer()->getLevel() ) + "\nExp: " + std::to_string( targetActor->getAsPlayer()->getExp() ) + "\nSearchMessage: " + targetActor->getAsPlayer()->getSearchMessage() + + "\nPlayTime: " + std::to_string( targetActor->getAsPlayer()->getPlayTime() ) ); + break; + } + + default: + pPlayer->sendUrgent( "GM2 Command not implemented: " + std::to_string( commandId ) ); + break; + } +} + +void Core::Network::GameConnection::updatePositionHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + // if the player is marked for zoning we no longer want to update his pos + if( pPlayer->isMarkedForZoning() ) + return; + + struct testMov + { + uint32_t specialMovement : 23; // 0x00490FDA + uint32_t strafe : 7; + uint32_t moveBackward : 1; + uint32_t strafeRight : 1; // if 0, strafe left. + } IPC_OP_019A; + + struct testMov1 + { + uint16_t bit1 : 1; // 0x00490FDA + uint16_t bit2 : 1; + uint16_t bit3 : 1; + uint16_t bit4 : 1; + uint16_t bit5 : 1; + uint16_t bit6 : 1; + uint16_t bit7 : 1; + uint16_t bit8 : 1; + uint16_t bit9 : 1; // 0x00490FDA + uint16_t bit10 : 1; + uint16_t bit11 : 1; + uint16_t bit12 : 1; + uint16_t bit13 : 1; + uint16_t bit14 : 1; + uint16_t bit15 : 1; + uint16_t bit16 : 1; + } IPC_OP_019AB; + + uint16_t flags = pInPacket->getValAt( 0x28 ); + memcpy( &IPC_OP_019AB, &flags, 2 ); + + uint32_t flags1 = pInPacket->getValAt( 0x24 ); + memcpy( &IPC_OP_019A, &flags1, 4 ); + + //g_log.Log(LoggingSeverity::debug, "" + boost::lexical_cast((int)IPC_OP_019AB.bit1) + // + boost::lexical_cast((int)IPC_OP_019AB.bit2) + // + boost::lexical_cast((int)IPC_OP_019AB.bit3) + // + boost::lexical_cast((int)IPC_OP_019AB.bit4) + // + boost::lexical_cast((int)IPC_OP_019AB.bit5) + // + boost::lexical_cast((int)IPC_OP_019AB.bit6) + // + boost::lexical_cast((int)IPC_OP_019AB.bit7) + // + boost::lexical_cast((int)IPC_OP_019AB.bit8) + // + boost::lexical_cast((int)IPC_OP_019AB.bit9) + // + boost::lexical_cast((int)IPC_OP_019AB.bit10) + // + boost::lexical_cast((int)IPC_OP_019AB.bit11) + // + boost::lexical_cast((int)IPC_OP_019AB.bit12) + // + boost::lexical_cast((int)IPC_OP_019AB.bit13) + // + boost::lexical_cast((int)IPC_OP_019AB.bit14) + // + boost::lexical_cast((int)IPC_OP_019AB.bit15) + // + boost::lexical_cast((int)IPC_OP_019AB.bit16) + // + " " + boost::lexical_cast((int)flags)); + + //g_log.Log(LoggingSeverity::debug, "\n" + boost::lexical_cast((int)IPC_OP_019A.specialMovement) + "\n" + // + boost::lexical_cast((int)IPC_OP_019A.strafe) + "\n" + // + boost::lexical_cast((int)IPC_OP_019A.moveBackward) + "\n" + // + boost::lexical_cast((int)IPC_OP_019A.strafeRight)); + + //g_log.Log(LoggingSeverity::debug, pInPacket->toString()); + + //pInPacket->debugPrint(); + + bool bPosChanged = false; + if( ( pPlayer->getPos().x != pInPacket->getValAt< float >( 0x2c ) ) || + ( pPlayer->getPos().y != pInPacket->getValAt< float >( 0x30 ) ) || + ( pPlayer->getPos().z != pInPacket->getValAt< float >( 0x34 ) ) ) + bPosChanged = true; + if( !bPosChanged && pPlayer->getRotation() == pInPacket->getValAt< float >( 0x20 ) ) + return; + + pPlayer->setRotation( pInPacket->getValAt< float >( 0x20 ) ); + pPlayer->setPosition( pInPacket->getValAt< float >( 0x2c ), + pInPacket->getValAt< float >( 0x30 ), + pInPacket->getValAt< float >( 0x34 ) ); + + pPlayer->setSyncFlag( PlayerSyncFlags::Position ); + + if( ( pPlayer->getCurrentAction() != nullptr ) && bPosChanged ) + pPlayer->getCurrentAction()->setInterrupted(); + + // if no one is in range, don't bother trying to send a position update + if( !pPlayer->hasInRangeActor() ) + return; + + uint8_t unk = pInPacket->getValAt< uint8_t >( 0x29 ); + + uint16_t moveType = pInPacket->getValAt< uint16_t >( 0x28 ); + + uint8_t unk1 = 0; + uint8_t unk2 = 0; + uint8_t unk3 = unk; + uint16_t unk4 = 0; + + // HACK: This part is hackish, we need to find out what all theese things really do. + switch( moveType ) + { + case MoveType::Strafe: + { + if( IPC_OP_019A.strafeRight == 1 ) + unk1 = 0xbf; + else + unk1 = 0x5f; + unk4 = 0x3C; + break; + } + + case 6: + { + unk1 = 0xFF; + unk2 = 0x06; + unk4 = 0x18; + break; + } + + // case MoveType::Land: + // { + // unk1 = 0x7F; + // //unk2 = 0x40; + // unk4 = 0x3C; + // break; + // } + + // case MoveType::Jump: + // { + // unk1 = 0x7F; + // if(unk == 0x01) + // { + // // unk2 = 0x20; + // //unk4 = 0x32; + // unk4 = 0x32; + // } + // else + // { + // // unk2 = 0xA0; + // unk4 = 0x3C; + // } + // + // break; + // } + // case MoveType::Fall: + // { + // unk1 = 0x7F; + // //unk2 = 0xA0; + // unk4 = 0x3C; + // + // break; + // } + default: + { + if( static_cast< int >( IPC_OP_019A.moveBackward ) ) + { + unk1 = 0xFF; + unk2 = 0x06; + unk4 = 0x18; // animation speed? + } + else + { + unk1 = 0x7F; + unk4 = 0x3C; // animation speed? + } + + break; + } + } + + MoveActorPacket movePacket( pPlayer, unk1, unk2, unk3, unk4 ); + pPlayer->sendToInRangeSet( movePacket ); + +} + + + +void Core::Network::GameConnection::zoneLineHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t zoneLineId = pInPacket->getValAt< uint32_t >( 0x20 ); + + auto pZone = pPlayer->getCurrentZone(); + + auto pLine = g_zoneMgr.getZonePosition( zoneLineId ); + + Common::FFXIVARR_POSITION3 targetPos; + uint32_t targetZone; + float rotation = 0.0f; + + if( pLine != nullptr ) + { + g_log.debug( "ZoneLine " + std::to_string( zoneLineId ) + " found." ); + targetPos = pLine->getTargetPosition(); + targetZone = pLine->getTargetZoneId(); + rotation = pLine->getTargetRotation(); + + GamePacketNew< FFXIVIpcPrepareZoning > preparePacket( pPlayer->getId() ); + preparePacket.data().targetZone = targetZone; + + //ActorControlPacket143 controlPacket( pPlayer, ActorControlType::DespawnZoneScreenMsg, + // 0x03, pPlayer->getId(), 0x01, targetZone ); + pPlayer->queuePacket( preparePacket ); + } + else + { + // No zoneline found, revert to last zone + g_log.debug( "ZoneLine " + std::to_string( zoneLineId ) + " not found." ); + targetPos.x = 0; + targetPos.y = 0; + targetPos.z = 0; + targetZone = pZone->getId(); + } + + pPlayer->performZoning( targetZone, targetPos, rotation); + pPlayer->setSyncFlag( PlayerSyncFlags::Position ); + +} + + + +void Core::Network::GameConnection::discoveryHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t ref_position_id = pInPacket->getValAt< uint32_t >( 0x20 ); + + auto pQR = g_database.query( "SELECT id, map_id, discover_id " + "FROM discoveryinfo " + "WHERE id = " + std::to_string( ref_position_id ) + ";" ); + + if( !pQR ) + { + pPlayer->sendNotice( "Discovery ref pos ID: " + std::to_string( ref_position_id ) + " not found. " ); + return; + } + + Db::Field *field = pQR->fetch(); + + GamePacketNew< FFXIVIpcDiscovery > discoveryPacket( pPlayer->getId() ); + discoveryPacket.data().map_id = field[1].getInt16(); + discoveryPacket.data().map_part_id = field[2].getInt16(); + + pPlayer->queuePacket( discoveryPacket ); + pPlayer->sendNotice( "Discovery ref pos ID: " + std::to_string( ref_position_id ) ); + + pPlayer->discover( field[1].getInt16(), field[2].getInt16() ); + +} + +void Core::Network::GameConnection::inventoryModifyHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint32_t seq = pInPacket->getValAt< uint32_t >( 0x20 ); + uint8_t action = pInPacket->getValAt< uint8_t >( 0x24 ); + uint8_t fromSlot = pInPacket->getValAt< uint8_t >( 0x30 ); + uint8_t toSlot = pInPacket->getValAt< uint8_t >( 0x44 ); + uint16_t fromContainer = pInPacket->getValAt< uint16_t >( 0x2C ); + uint16_t toContainer = pInPacket->getValAt< uint16_t >( 0x40 ); + + GamePacketNew< FFXIVIpcInventoryActionAck > ackPacket( pPlayer->getId() ); + ackPacket.data().sequence = seq; + ackPacket.data().type = 7; + pPlayer->queuePacket( ackPacket ); + + + g_log.debug( pInPacket->toString() ); + g_log.debug( "InventoryAction: " + std::to_string( action ) ); + + // TODO: other inventory operations need to be implemented + switch( action ) + { + + case 0x07: // discard item action + { + pPlayer->getInvetory()->discardItem( fromContainer, fromSlot ); + } + break; + + case 0x08: // move item action + { + pPlayer->getInvetory()->moveItem( fromContainer, fromSlot, toContainer, toSlot ); + } + break; + + case 0x09: // swap item action + { + pPlayer->getInvetory()->swapItem( fromContainer, fromSlot, toContainer, toSlot ); + } + break; + + case 0x0C: // merge stack action + { + + } + break; + + case 0x0A: // split stack action + { + + } + break; + + default: + + break; + } + + pPlayer->setSyncFlag( PlayerSyncFlags::Status ); + +} + + +void Core::Network::GameConnection::actionHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint16_t commandId = pInPacket->getValAt< uint16_t >( 0x20 ); + uint64_t param1 = pInPacket->getValAt< uint64_t >( 0x24 ); + uint32_t param11 = pInPacket->getValAt< uint32_t >( 0x24 ); + uint32_t param12 = pInPacket->getValAt< uint32_t >( 0x28 ); + uint32_t param2 = pInPacket->getValAt< uint32_t >( 0x2c ); + uint64_t param3 = pInPacket->getValAt< uint64_t >( 0x38 ); + + g_log.debug( "[" + std::to_string( m_pSession->getId() ) + "] Incoming action: " + + boost::str( boost::format( "%|04X|" ) % ( uint32_t ) ( commandId & 0xFFFF ) ) + + "\nparam1: " + boost::str( boost::format( "%|016X|" ) % ( uint64_t ) ( param1 & 0xFFFFFFFFFFFFFFF ) ) + + "\nparam2: " + boost::str( boost::format( "%|08X|" ) % ( uint32_t ) ( param2 & 0xFFFFFFFF ) ) + + "\nparam3: " + boost::str( boost::format( "%|016X|" ) % ( uint64_t ) ( param3 & 0xFFFFFFFFFFFFFFF ) ) + ); + + + //g_log.Log(LoggingSeverity::debug, "[" + std::to_string(m_pSession->getId()) + "] " + pInPacket->toString()); + + switch( commandId ) + { + case 0x01: + { + if( param11 == 1 ) + pPlayer->setStance( Entity::Actor::Stance::Active ); + else + pPlayer->setStance( Entity::Actor::Stance::Passive ); + + pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), 0, param11, 1 ) ); + + break; + } + case 0x02: + { + if (param11 == 1) + pPlayer->setAutoattack( true ); + else + pPlayer->setAutoattack( false ); + + pPlayer->sendToInRangeSet(ActorControlPacket142(pPlayer->getId(), 1, param11, 1)); + + break; + } + case 0x03: // Change target + { + + uint64_t targetId = pInPacket->getValAt< uint64_t >( 0x24 ); + pPlayer->changeTarget( targetId ); + break; + } + + case 0x133: // Update howtos seen + { + uint32_t howToId = static_cast< uint32_t >( param1 ); + pPlayer->updateHowtosSeen( howToId ); + break; + } + case 0x1F4: // emote + { + uint64_t targetId = pPlayer->getTargetId(); + uint32_t emoteId = pInPacket->getValAt< uint32_t >( 0x24 ); + + pPlayer->sendToInRangeSet( ActorControlPacket144( pPlayer->getId(), Emote, emoteId, 0, 0, 0, targetId ) ); + break; + } + case 0xC8: // return dead + { + pPlayer->setZoningType( Common::ZoneingType::Return ); + pPlayer->teleport( pPlayer->getHomepoint(), 3 ); + break; + } + case 0xC9: // Finish zoning + { + switch( pPlayer->getZoningType() ) + { + case ZoneingType::None: + pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01 ), true ); + break; + case ZoneingType::Teleport: + pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01, 0, 0, 110 ), true ); + break; + case ZoneingType::Return: + case ZoneingType::ReturnDead: + { + if( pPlayer->getStatus() == Entity::Actor::ActorStatus::Dead ) + { + pPlayer->resetHp(); + pPlayer->resetMp(); + pPlayer->setStatus( Entity::Actor::ActorStatus::Idle ); + pPlayer->setSyncFlag( Status ); + pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01, 0x01, 0, 111 ), true ); + pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle ) ), true ); + } + else + pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01, 0x00, 0, 111 ), true ); + } + break; + case ZoneingType::FadeIn: + break; + default: + break; + } + + pPlayer->setZoningType( Common::ZoneingType::None ); + + pPlayer->unsetStateFlag( PlayerStateFlag::BetweenAreas ); + pPlayer->unsetStateFlag( PlayerStateFlag::BetweenAreas1 ); + pPlayer->sendStateFlags(); + break; + } + + case 0xCA: // Teleport + { + // TODO: only register this action if enough gil is in possession + auto targetAetheryte = g_exdData.getAetheryteInfo( param11 ); + + if( targetAetheryte ) + { + auto fromAetheryte = g_exdData.getAetheryteInfo( g_exdData.m_zoneInfoMap[pPlayer->getZoneId()].aetheryte_index ); + + // calculate cost - does not apply for favorite points or homepoints neither checks for aether tickets + auto cost = ( sqrt( pow( fromAetheryte->map_coord_x - targetAetheryte->map_coord_x, 2 ) + + pow( fromAetheryte->map_coord_y - targetAetheryte->map_coord_y, 2 ) ) / 2 ) + 100; + + // cap at 999 gil + cost = cost > 999 ? 999 : cost; + + bool insufficientGil = pPlayer->getCurrency( Inventory::CurrencyType::Gil ) < cost; + // todo: figure out what param1 really does + pPlayer->queuePacket( ActorControlPacket143( pPlayer->getId(), TeleportStart, insufficientGil ? 2 : 0, param11 ) ); + + if( !insufficientGil ) + { + Action::ActionTeleportPtr pActionTeleport( new Action::ActionTeleport( pPlayer, param11, cost ) ); + pPlayer->setCurrentAction( pActionTeleport ); + } + } + break; + } + + } +} + + +void Core::Network::GameConnection::playTimeHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + GamePacketNew< FFXIVIpcPlayTime > playTimePacket( pPlayer->getId() ); + playTimePacket.data().playTimeInMinutes = pPlayer->getPlayTime() / 60; + pPlayer->queuePacket( playTimePacket ); +} + + +void Core::Network::GameConnection::initHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + // init handler means this is a login procedure + pPlayer->setIsLogin( true ); + + pPlayer->setZone( pPlayer->getZoneId() ); +} + + +void Core::Network::GameConnection::blackListHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint8_t count = pInPacket->getValAt< uint8_t >( 0x21 ); + + GamePacketNew< FFXIVIpcBlackList > blackListPacket( pPlayer->getId() ); + blackListPacket.data().sequence = count; + // TODO: Fill with actual blacklist data + //blackListPacket.data().entry[0].contentId = 1; + //sprintf( blackListPacket.data().entry[0].name, "Test Test" ); + queueOutPacket( blackListPacket ); + +} + + +void Core::Network::GameConnection::pingHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + int32_t inVal = pInPacket->getValAt< int32_t >( 0x20 ); + PingPacket pingPacket( pPlayer, inVal ); + queueOutPacket( pingPacket ); + + pPlayer->setLastPing( static_cast< uint32_t >( time( nullptr ) ) ); +} + + +void Core::Network::GameConnection::finishLoadingHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + // player is done zoning + pPlayer->setLoadingComplete( true ); + + // if this is a login event + if( pPlayer->isLogin() ) + { + // fire the onLogin Event + pPlayer->onLogin(); + pPlayer->setIsLogin( false ); + } + + // spawn the player for himself + pPlayer->spawn( pPlayer ); + + // notify the zone of a change in position to force an "inRangeActor" update + pPlayer->getCurrentZone()->changeActorPosition( pPlayer ); +} + +void Core::Network::GameConnection::socialListHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + + uint8_t type = pInPacket->getValAt< uint8_t >( 0x2A ); + uint8_t count = pInPacket->getValAt< uint8_t >( 0x2B ); + + if( type == 0x02 ) + { // party list + + GamePacketNew< FFXIVIpcSocialList > listPacket( pPlayer->getId() );; + + listPacket.data().type = 2; + listPacket.data().sequence = count; + + int entrysizes = sizeof( listPacket.data().entries ); + memset( listPacket.data().entries, 0, sizeof( listPacket.data().entries ) ); + + listPacket.data().entries[0].bytes[2] = pPlayer->getCurrentZone()->getId(); + listPacket.data().entries[0].bytes[3] = 0x80; + listPacket.data().entries[0].bytes[4] = 0x02; + listPacket.data().entries[0].bytes[6] = 0x3B; + listPacket.data().entries[0].bytes[11] = 0x10; + listPacket.data().entries[0].classJob = static_cast< uint8_t >( pPlayer->getClass() ); + listPacket.data().entries[0].contentId = pPlayer->getContentId(); + listPacket.data().entries[0].level = pPlayer->getLevel(); + listPacket.data().entries[0].zoneId = pPlayer->getCurrentZone()->getId(); + listPacket.data().entries[0].zoneId1 = 0x0100; + // TODO: no idea what this does + //listPacket.data().entries[0].one = 1; + + memcpy( listPacket.data().entries[0].name, pPlayer->getName().c_str(), strlen( pPlayer->getName().c_str() ) ); + + // TODO: actually store and read language from somewhere + listPacket.data().entries[0].bytes1[0] = 0x01;//flags (lang) + // TODO: these flags need to be figured out + //listPacket.data().entries[0].bytes1[1] = 0x00;//flags + listPacket.data().entries[0].onlineStatusMask = pPlayer->getOnlineStatusMask(); + + queueOutPacket( listPacket ); + + } + else if( type == 0x0b ) + { // friend list + + GamePacketNew< FFXIVIpcSocialList > listPacket( pPlayer->getId() ); + listPacket.data().type = 0x0B; + listPacket.data().sequence = count; + memset( listPacket.data().entries, 0, sizeof( listPacket.data().entries ) ); + + } + else if( type == 0x0e ) + { // player search result + // TODO: implement player search + /*CGamePacket * pPE = new CGamePacket(0xCC, 0x3A0, pPlayer->getId(), pPlayer->getId()); + pPE->setInt8At(0x2C, 0x0E); + pPE->setInt8At(0x2D, count); + + std::set tmpSet = m_playerSearchResult; + + std::set::iterator it; + + int i = 0x30; + for(it = tmpSet.begin(); it != tmpSet.end(); it++) + { + if((*it)->getId() == pPlayer->getId()) + { + continue; + } + pPE->setInt64At(i, (*it)->getContentId()); + pPE->setInt8At(i+0x16, 0x2); + pPE->setInt16At(i+0x0A, (*it)->getZone()->getLayoutId()); + pPE->setInt16At(i+0x0C, (*it)->getZone()->getLayoutId()); + pPE->setInt16At(i+0x0E, 0x46); + pPE->setInt16At(i+0x14, 0x100); + + pPE->setInt16At(i+0x1A, 0x8000); + pPE->setInt16At(i + 0x20, static_cast((*it)->getClass())); + pPE->setInt16At(i+0x22,(*it)->getLevel()); + pPE->setStringAt(i+0x26, (*it)->getName()); + i += 0x48; + } + + + pPlayer->queuePacket(pPE);*/ + + } + +} + +void Core::Network::GameConnection::chatHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + + std::string chatString( pInPacket->getStringAt( 0x3a ) ); + + uint32_t sourceId; + sourceId = pInPacket->getValAt< uint32_t >( 0x24 ); + + if( chatString.at( 0 ) == '@' ) + { + // execute game console command + g_gameCommandMgr.execCommand( const_cast< char * >( chatString.c_str() ) + 1, pPlayer ); + return; + } + + ChatType chatType = static_cast( pInPacket->getValAt< uint8_t >( 0x38 ) ); + + //ToDo, need to implement sending GM chat types. + ChatPacket chatPacket( pPlayer, chatType, chatString ); + + switch( chatType ) + { + case ChatType::Say: + { + pPlayer->getCurrentZone()->queueOutPacketForRange( pPlayer, 50, chatPacket ); + break; + } + case ChatType::Yell: + { + pPlayer->getCurrentZone()->queueOutPacketForRange(pPlayer, 6000, chatPacket); + break; + } + case ChatType::Shout: + { + pPlayer->getCurrentZone()->queueOutPacketForRange( pPlayer, 6000, chatPacket ); + break; + } + default: + { + pPlayer->getCurrentZone()->queueOutPacketForRange( pPlayer, 50, chatPacket ); + break; + } + } + +} + +// TODO: this handler needs to be improved for timed logout, also the session should be instantly removed +// currently we wait for the session to just time out after logout, this can be a problem is the user tries to +// log right back in. +// Also the packet needs to be converted to an ipc structure +void Core::Network::GameConnection::logoutHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + GamePacketNew< FFXIVIpcLogout > logoutPacket( pPlayer->getId() ); + logoutPacket.data().flags1 = 0x02; + logoutPacket.data().flags2 = 0x2000; + queueOutPacket( logoutPacket ); +} diff --git a/src/servers/Server_Zone/PacketHandlers2.cpp b/src/servers/Server_Zone/PacketHandlers2.cpp new file mode 100644 index 00000000..08ecf302 --- /dev/null +++ b/src/servers/Server_Zone/PacketHandlers2.cpp @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include + +#include + + +#include "GameConnection.h" + +#include "Session.h" +#include "Zone.h" +#include "ZonePosition.h" + +#include "InitUIPacket.h" +#include "PingPacket.h" +#include "MoveActorPacket.h" +#include "ChatPacket.h" +#include "ServerNoticePacket.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "ActorControlPacket144.h" +#include "EventStartPacket.h" +#include "EventFinishPacket.h" +#include "PlayerStateFlagsPacket.h" + + +#include "GameCommandHandler.h" + +#include "Player.h" +#include "Inventory.h" + +#include "Globals.h" + +#include "Forwards.h" + +#include "EventHelper.h" + +#include "Action.h" +#include "ActionTeleport.h" +#include "ActionCast.h" + + +extern Core::GameCommandHandler g_gameCommandMgr; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +void Core::Network::GameConnection::skillHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + + uint32_t action = pInPacket->getValAt< uint32_t >( 0x24 ); + uint32_t useCount = pInPacket->getValAt< uint32_t >( 0x28 ); + + uint64_t targetId = pInPacket->getValAt< uint64_t >( 0x30 ); + + + if( action < 1000000 ) // normal action + { + std::string actionIdStr = boost::str( boost::format( "%|04X|" ) % action ); + pPlayer->sendDebug( "---------------------------------------" ); + pPlayer->sendDebug( "ActionHandler ( " + actionIdStr + " | " + g_exdData.m_actionInfoMap[action].name + " )" ); + + if( action == 5 ) + { + auto currentAction = pPlayer->getCurrentAction(); + + // we should always have an action here, if not there is a bug + assert( currentAction ); + currentAction->onStart(); + } + else + { + Core::Entity::ActorPtr targetActor; + + auto inRange = pPlayer->getInRangeActors(); + for( auto actor : inRange ) + { + if( actor->getId() == targetId ) + { + targetActor = actor; + } + } + + if( targetActor ) + { + Action::ActionCastPtr pActionCast( new Action::ActionCast( pPlayer, targetActor, action ) ); + pPlayer->setCurrentAction(pActionCast); + pPlayer->sendDebug( "setCurrentAction()" ); + pPlayer->getCurrentAction()->onStart(); + } + } + } + else if( action < 2000000 ) // craft action + { + + } + else if( action < 3000000 ) // item action + { + auto info = g_exdData.getEventItemInfo( action ); + if( info ) + { + g_log.debug( info->name ); + g_scriptMgr.onEventItem( pPlayer, action, info->eventId, info->castTime, targetId ); + } + } + else if( action > 3000000 ) // unknown + { + + } + +} + +void Core::Network::GameConnection::eventHandler( Core::Network::Packets::GamePacketPtr pInPacket, + Core::Entity::PlayerPtr pPlayer ) +{ + uint16_t eventHandlerId = pInPacket->getValAt< uint16_t >( 0x12 ); + + // we need to abort the event in case it has not been scripted so the player wont be locked up + auto abortEventFunc = []( Core::Entity::PlayerPtr pPlayer, uint64_t actorId, uint32_t eventId ) + { + pPlayer->queuePacket( EventStartPacket( pPlayer->getId(), actorId, eventId, 1, 0, 0 ) ); + pPlayer->queuePacket( EventFinishPacket( pPlayer->getId(), eventId, 1, 0 ) ); + // this isn't ideal as it will also reset any other status that might be active + pPlayer->queuePacket( PlayerStateFlagsPacket( pPlayer, PlayerStateFlagList{} ) ); + }; + + std::string eventIdStr = boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( eventHandlerId & 0xFFFF ) ); + pPlayer->sendDebug( "---------------------------------------" ); + pPlayer->sendDebug( "EventHandler ( " + eventIdStr + " )" ); + + switch( eventHandlerId ) + { + + case ClientIpcType::TalkEventHandler: // Talk event + { + uint64_t actorId = pInPacket->getValAt< uint64_t >( 0x20 ); + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x28 ); + + if( !g_scriptMgr.onTalk( pPlayer, actorId, eventId ) ) + abortEventFunc( pPlayer, actorId, eventId ); + break; + } + + case ClientIpcType::EmoteEventHandler: // Emote event + { + uint64_t actorId = pInPacket->getValAt< uint64_t >( 0x20 ); + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x28 ); + uint16_t emoteId = pInPacket->getValAt< uint16_t >( 0x2C ); + + std::string eventName = Event::getEventName( eventId ); + + if( !g_scriptMgr.onEmote( pPlayer, actorId, eventId, emoteId ) ) + abortEventFunc( pPlayer, actorId, eventId ); + break; + } + + + case ClientIpcType::WithinRangeEventHandler: + { + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x24 ); + uint32_t eventParam1 = pInPacket->getValAt< uint32_t >( 0x20 ); + float x = pInPacket->getValAt< float >( 0x28 ); + float y = pInPacket->getValAt< float >( 0x2C ); + float z = pInPacket->getValAt< float >( 0x30 ); + + std::string eventName = Event::getEventName( eventId ); + + if( !g_scriptMgr.onWithinRange( pPlayer, eventId, eventParam1, x, y, z ) ) + abortEventFunc( pPlayer, 0, eventId ); + break; + } + + case ClientIpcType::OutOfRangeEventHandler: + { + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x24 ); + uint32_t eventParam1 = pInPacket->getValAt< uint32_t >( 0x20 ); + float x = pInPacket->getValAt< float >( 0x28 ); + float y = pInPacket->getValAt< float >( 0x2C ); + float z = pInPacket->getValAt< float >( 0x30 ); + + std::string eventName = Event::getEventName( eventId ); + + if( !g_scriptMgr.onOutsideRange( pPlayer, eventId, eventParam1, x, y, z ) ) + abortEventFunc( pPlayer, 0, eventId ); + break; + } + + case ClientIpcType::EnterTeriEventHandler: + { + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x20 ); + uint16_t eventParam1 = pInPacket->getValAt< uint16_t >( 0x24 ); + uint16_t eventParam2 = pInPacket->getValAt< uint16_t >( 0x26 ); + + std::string eventName = Event::getEventName( eventId ); + + if( !g_scriptMgr.onEnterTerritory( pPlayer, eventId, eventParam1, eventParam2 ) ) + abortEventFunc( pPlayer, 0, eventId ); + break; + } + + case ClientIpcType::ReturnEventHandler: + case ClientIpcType::TradeReturnEventHandler: + { + uint32_t eventId = pInPacket->getValAt< uint32_t >( 0x20 ); + uint16_t subEvent = pInPacket->getValAt< uint16_t >( 0x24 ); + uint16_t param1 = pInPacket->getValAt< uint16_t >( 0x26 ); + uint16_t param2 = pInPacket->getValAt< uint16_t >( 0x28 ); + uint16_t param3 = pInPacket->getValAt< uint16_t >( 0x2C ); + + std::string eventName = Event::getEventName( eventId ); + + if( !g_scriptMgr.onEventHandlerReturn( pPlayer, eventId, subEvent, param1, param2, param3 ) ) + abortEventFunc( pPlayer, 0, eventId ); + break; + } + + } + +} + + + + diff --git a/src/servers/Server_Zone/PingPacket.h b/src/servers/Server_Zone/PingPacket.h new file mode 100644 index 00000000..26dbd981 --- /dev/null +++ b/src/servers/Server_Zone/PingPacket.h @@ -0,0 +1,38 @@ +#ifndef _CORE_NETWORK_PACKETS_PINGPACKET_H +#define _CORE_NETWORK_PACKETS_PINGPACKET_H + +#include + +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class PingPacket : + public GamePacketNew +{ +public: + PingPacket( Entity::PlayerPtr player, int32_t inVal ) : + GamePacketNew( player->getId(), player->getId() ) + { + initialize( player, inVal ); + }; + +private: + void initialize( Entity::PlayerPtr player, int32_t inVal ) + { + m_data.timeInMilliseconds = 0x000014D00000000 + inVal; + }; +}; + +} +} +} +} + +#endif /*_CORE_NETWORK_PACKETS_CPINGPACKET_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/Player.cpp b/src/servers/Server_Zone/Player.cpp new file mode 100644 index 00000000..66872071 --- /dev/null +++ b/src/servers/Server_Zone/Player.cpp @@ -0,0 +1,1436 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Session.h" +#include "Player.h" +#include "BattleNpc.h" + + +#include "ZoneMgr.h" +#include "Zone.h" + +#include "ServerZone.h" + + +#include "GameConnection.h" + +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" +#include "InitUIPacket.h" +#include "ServerNoticePacket.h" +#include "ChatPacket.h" +#include "ModelEquipPacket.h" +#include "ActorSpawnPacket.h" +#include "UpdateHpMpTpPacket.h" +#include "PlayerStateFlagsPacket.h" +#include "PlayerSpawnPacket.h" + +#include "ScriptManager.h" + +#include "StatusEffectContainer.h" + +#include "Item.h" + +#include "Inventory.h" +#include "Event.h" +#include "Action.h" +#include "EventAction.h" +#include "EventItemAction.h" +#include "ZonePosition.h" +#include + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::ServerZone g_serverZone; +extern Core::ZoneMgr g_zoneMgr; +extern Core::Data::ExdData g_exdData; +extern Core::Scripting::ScriptManager g_scriptMgr; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +// player constructor +Core::Entity::Player::Player() : + m_lastWrite( 0 ), + m_lastPing( 0 ), + m_bIsLogin( false ), + m_contentId( 0 ), + m_modelMainWeapon( 0 ), + m_modelSubWeapon( 0 ), + m_homePoint( 0 ), + m_startTown( 0 ), + m_townWarpFstFlags( 0 ), + m_playTime( 0 ), + m_bInCombat( false ), + m_bLoadingComplete( false ), + m_bMarkedForZoning( false ), + m_zoningType( Common::ZoneingType::None ), + m_bAutoattack( false ) +{ + m_id = 0; + m_type = ActorType::Player; + m_currentStance = Stance::Passive; + m_onlineStatus = 0; + m_queuedZoneing = nullptr; + m_status = ActorStatus::Idle; + + memset( m_questTracking, 0, sizeof( m_questTracking ) ); + memset( m_name, 0, sizeof( m_name ) ); + memset( m_stateFlags, 0, sizeof( m_stateFlags ) ); + memset( m_searchMessage, 0, sizeof( m_searchMessage ) ); +} + +Core::Entity::Player::~Player() +{ + +} + +// TODO: add a proper calculation based on race / job / level / gear +uint32_t Core::Entity::Player::getMaxHp() +{ + return m_baseStats.max_hp; +} + +uint32_t Core::Entity::Player::getMaxMp() +{ + return m_baseStats.max_mp; +} + +uint16_t Core::Entity::Player::getZoneId() const +{ + return m_zoneId; +} + +uint8_t Core::Entity::Player::getMode() const +{ + return m_mode; +} + +void Core::Entity::Player::setMode( uint8_t mode ) +{ + m_mode = mode; +} + +uint8_t Core::Entity::Player::getStartTown() const +{ + return m_startTown; +} + +Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus() +{ + uint64_t newMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::NewAdventurer ); + uint64_t afkMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Afk ); + uint64_t busyMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Busy ); + uint64_t dcMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Disconnected ); + uint64_t meldMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::LfMeld ); + uint64_t ptMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::LfParty ); + + OnlineStatus status = OnlineStatus::Online; + + //if( hasStateFlag( Common::PlayerStateFlag::NewAdventurer ) ) + if( m_onlineStatus & newMask ) + status = OnlineStatus::NewAdventurer; + + if( m_onlineStatus & afkMask ) + status = OnlineStatus::Afk; + + if( m_onlineStatus & busyMask ) + status = OnlineStatus::Busy; + + if( m_onlineStatus & dcMask ) + status = OnlineStatus::Disconnected; + + if( m_onlineStatus & meldMask ) + status = OnlineStatus::LfMeld; + + if( m_onlineStatus & ptMask ) + status = OnlineStatus::LfParty; + + if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) || hasStateFlag( PlayerStateFlag::WatchingCutscene1 ) ) + status = OnlineStatus::Cutscene; + + // TODO: add all the logic for returning the proper online status, there probably is a better way for this alltogether + return status; +} + +void Core::Entity::Player::setOnlineStatusMask( uint64_t status ) +{ + m_onlineStatus = status; +} + +uint64_t Core::Entity::Player::getOnlineStatusMask() const +{ + return m_onlineStatus; +} + +void Core::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut ) +{ + GamePacketNew< FFXIVIpcPrepareZoning > preparePacket( getId() ); + preparePacket.data().targetZone = targetZone; + preparePacket.data().fadeOut = fadeOut == true ? 1 : 0; + queuePacket( preparePacket ); +} + +void Core::Entity::Player::calculateStats() +{ + uint8_t tribe = getLookAt( Common::CharaLook::Tribe ); + uint8_t level = getLevel(); + uint8_t job = getClass(); + + auto classInfoIt = g_exdData.m_classJobInfoMap.find( job ); + auto tribeInfoIt = g_exdData.m_tribeInfoMap.find( tribe ); + auto paramGrowthInfoIt = g_exdData.m_paramGrowthInfoMap.find( level ); + + if( tribeInfoIt == g_exdData.m_tribeInfoMap.end() || + classInfoIt == g_exdData.m_classJobInfoMap.end() || + paramGrowthInfoIt == g_exdData.m_paramGrowthInfoMap.end() ) + return; + + auto tribeInfo = tribeInfoIt->second; + auto classInfo = classInfoIt->second; + auto paramGrowthInfo = paramGrowthInfoIt->second; + + // TODO: put formula somewhere else... + float base = 0.0f; + + if( level < 51 ) + base = 0.053f * ( level * level ) + ( 1.022f * level ) - 0.907f + 20; + else + base = 1.627f * level + 120.773f; + + m_baseStats.str = base * ( static_cast< float >( classInfo.mod_str ) / 100 ) + tribeInfo.mod_str; + m_baseStats.dex = base * ( static_cast< float >( classInfo.mod_dex ) / 100 ) + tribeInfo.mod_dex; + m_baseStats.vit = base * ( static_cast< float >( classInfo.mod_vit ) / 100 ) + tribeInfo.mod_vit; + m_baseStats.inte = base * ( static_cast< float >( classInfo.mod_int ) / 100 ) + tribeInfo.mod_int; + m_baseStats.mnd = base * ( static_cast< float >( classInfo.mod_mnd ) / 100 ) + tribeInfo.mod_mnd; + m_baseStats.pie = base * ( static_cast< float >( classInfo.mod_pie ) / 100 ) + tribeInfo.mod_pie; + + m_baseStats.skillSpeed = paramGrowthInfo.base_secondary; + m_baseStats.spellSpeed = paramGrowthInfo.base_secondary; + m_baseStats.accuracy = paramGrowthInfo.base_secondary; + m_baseStats.critHitRate = paramGrowthInfo.base_secondary; + m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary; + m_baseStats.healingPotMagic = paramGrowthInfo.base_secondary; + + m_baseStats.max_mp = floor( + floor( + ( ( m_baseStats.pie - base ) * ( static_cast< float >( paramGrowthInfo.piety_scalar ) / 100 ) ) + paramGrowthInfo.mp_const ) * ( static_cast< float >( classInfo.mod_mpcpgp ) / 100 ) + ); + + m_baseStats.max_hp = floor( + floor( + ( ( m_baseStats.vit - base ) * ( ( static_cast< float >( paramGrowthInfo.piety_scalar ) ) / 100 ) ) + paramGrowthInfo.hp_mod ) * ( static_cast< float >( classInfo.mod_hp * 0.9f ) / 100 ) * 15 + ); + + if( m_mp > m_baseStats.max_mp ) + m_mp = m_baseStats.max_mp; + + if( m_hp > m_baseStats.max_hp ) + m_hp = m_baseStats.max_hp; + + + m_baseStats.determination = base; + +} + + +void Core::Entity::Player::setAutoattack(bool mode) +{ + m_bAutoattack = mode; + m_lastAttack = Util::getTimeMs(); +} + +bool Core::Entity::Player::isAutoattackOn() const +{ + return m_bAutoattack; +} + +void Core::Entity::Player::sendStats() +{ + GamePacketNew< FFXIVIpcPlayerStats > statPacket( getId() ); + statPacket.data().strength = m_baseStats.str; + statPacket.data().dexterity = m_baseStats.dex; + statPacket.data().vitality = m_baseStats.vit; + statPacket.data().intelligence = m_baseStats.inte; + statPacket.data().mind = m_baseStats.mnd; + statPacket.data().piety = m_baseStats.pie; + statPacket.data().determination = m_baseStats.determination; + statPacket.data().hp = m_baseStats.max_hp; + statPacket.data().mp = m_baseStats.max_mp; + statPacket.data().accuracy = m_baseStats.accuracy; + statPacket.data().attack = m_baseStats.attack; + statPacket.data().attackMagicPotency = m_baseStats.attackPotMagic; + statPacket.data().healingMagicPotency = m_baseStats.healingPotMagic; + statPacket.data().skillSpeed = m_baseStats.skillSpeed; + statPacket.data().spellSpeed = m_baseStats.spellSpeed; + statPacket.data().spellSpeed1 = m_baseStats.spellSpeed; + statPacket.data().spellSpeedMod = 100; + + statPacket.data().criticalHitRate = m_baseStats.spellSpeed; + statPacket.data().defense = m_baseStats.spellSpeed; + statPacket.data().magicDefense = m_baseStats.spellSpeed; + statPacket.data().attack = m_baseStats.spellSpeed; + + queuePacket( statPacket ); +} + +void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type ) +{ + auto data = g_exdData.getAetheryteInfo( aetheryteId ); + + if( data != nullptr ) + { + + setStateFlag( PlayerStateFlag::BetweenAreas ); + sendStateFlags(); + + auto z_pos = g_zoneMgr.getZonePosition( data->levelId ); + + Common::FFXIVARR_POSITION3 pos; + pos.x = 0; + pos.y = 0; + pos.z = 0; + float rot = 0; + + if( z_pos != nullptr ) + { + pos = z_pos->getTargetPosition(); + rot = z_pos->getTargetRotation(); + } + + + sendDebug( "Teleport: " + data->placename + " " + data->placename_aethernet + + "(" + std::to_string( data->levelId ) + ")" ); + + // TODO: this should be simplified and a type created in server_common/common.h. + if( type == 1 ) // teleport + { + prepareZoning( data->target_zone, true ); + sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x04 ) ); + setZoningType( Common::ZoneingType::Teleport ); + } + else if( type == 2 ) // aethernet + { + prepareZoning( data->target_zone, true ); + sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x04 ) ); + setZoningType( Common::ZoneingType::Teleport ); + } + else if( type == 3 ) // return + { + prepareZoning( data->target_zone, true ); + sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x03 ) ); + setZoningType( Common::ZoneingType::Return ); + } + + m_queuedZoneing = boost::make_shared( data->target_zone, pos, Util::getTimeMs(), rot ); + + } +} + +void Core::Entity::Player::forceZoneing( uint32_t zoneId ) +{ + m_queuedZoneing = boost::make_shared( zoneId, getPos(), Util::getTimeMs(), 0 ); + //performZoning( zoneId, Common::ZoneingType::None, getPos() ); +} + +void Core::Entity::Player::setZone( uint32_t zoneId ) +{ + auto pPlayer = getAsPlayer(); + + auto pZone = g_zoneMgr.getZone( zoneId ); + + + if( !pZone /*|| ( ( pZone == m_pCurrentZone ) && m_lastPing )*/ ) + { + g_log.error( "Zone " + std::to_string( zoneId ) + " not found on this server." ); + return; + } + + m_zoneId = zoneId; + + // mark character as zoning in progress + setLoadingComplete( false ); + + if( m_lastPing != 0 ) + m_pCurrentZone->removeActor( shared_from_this() ); + + m_pCurrentZone = pZone; + m_pCurrentZone->pushActor( shared_from_this() ); + + // mark the player for a position update in DB + setSyncFlag( PlayerSyncFlags::Position ); + + GamePacketNew< FFXIVIpcInit > initPacket( getId() ); + initPacket.data().charId = getId(); + queuePacket( initPacket ); + + sendInventory(); + + // set flags, will be reset automatically by zoning ( only on client side though ) + pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas ); + pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas1 ); + pPlayer->sendStateFlags(); + + pPlayer->sendStats(); + + // only initialize the UI if the player in fact just logged in. + if( isLogin() ) + { + Server::InitUIPacket initUIPacket( pPlayer ); + queuePacket( initUIPacket ); + + GamePacketNew< FFXIVIpcPlayerClassInfo > classInfoPacket( getId() ); + classInfoPacket.data().classId = getClass(); + classInfoPacket.data().unknown = 1; + classInfoPacket.data().level = getLevel(); + classInfoPacket.data().level1 = getLevel(); + queuePacket( classInfoPacket ); + + GamePacketNew< FFXIVGCAffiliation > gcAffPacket( getId() ); + gcAffPacket.data().gcId = m_gc; + gcAffPacket.data().gcRank[0] = m_gcRank[0]; + gcAffPacket.data().gcRank[1] = m_gcRank[1]; + gcAffPacket.data().gcRank[2] = m_gcRank[2]; + queuePacket( gcAffPacket ); + } + + GamePacketNew< FFXIVIpcInitZone > initZonePacket( getId() ); + initZonePacket.data().zoneId = getCurrentZone()->getLayoutId(); + initZonePacket.data().weatherId = static_cast< uint8_t >( getCurrentZone()->getCurrentWeather() ); + initZonePacket.data().bitmask = 0x2A; + initZonePacket.data().pos.x = getPos().x; + initZonePacket.data().pos.y = getPos().y; + initZonePacket.data().pos.z = getPos().z; + queuePacket( initZonePacket ); + + if( isLogin() ) + { + GamePacketNew< FFXIVARR_IPC_UNK322 > unk322( getId() ); + queuePacket( unk322 ); + + GamePacketNew< FFXIVARR_IPC_UNK320 > unk320( getId() ); + queuePacket( unk320 ); + } + + if( getLastPing() == 0 ) + sendQuestInfo(); + + m_bMarkedForZoning = false; +} + +uint32_t Core::Entity::Player::getPlayTime() const +{ + return m_playTime; +} + +uint8_t Core::Entity::Player::getRace() const +{ + return getLookAt( CharaLook::Race ); +} + +uint8_t Core::Entity::Player::getGender() const +{ + return getLookAt( CharaLook::Gender ); +} + +void Core::Entity::Player::initSpawnIdQueue() +{ + while( !m_freeSpawnIdQueue.empty() ) + { + m_freeSpawnIdQueue.pop(); + } + + for( int i = 1; i < MAX_DISPLAYED_ACTORS; i++ ) + { + m_freeSpawnIdQueue.push( i ); + } +} + +uint8_t Core::Entity::Player::getSpawnIdForActorId( uint32_t actorId ) +{ + if( m_freeSpawnIdQueue.empty() ) + return 0; + + uint8_t spawnId = m_freeSpawnIdQueue.front(); + m_freeSpawnIdQueue.pop(); + m_playerIdToSpawnIdMap[actorId] = spawnId; + return spawnId; +} + +void Core::Entity::Player::assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ) +{ + m_playerIdToSpawnIdMap[actorId] = spawnId; +} + +void Core::Entity::Player::registerAetheryte( uint8_t aetheryteId ) +{ + + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( aetheryteId, value, index ); + + m_aetheryte[index] |= value; + + setSyncFlag( Aetherytes ); + + queuePacket( ActorControlPacket143( getId(), LearnTeleport, aetheryteId, 1 ) ); + +} + +bool Core::Entity::Player::isAetheryteRegistered( uint8_t aetheryteId ) const +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( aetheryteId, value, index ); + + return m_aetheryte[index] & value; +} + +uint8_t * Core::Entity::Player::getDiscoveryBitmask() +{ + return m_discovery; +} + +void Core::Entity::Player::discover( int16_t map_id, int16_t sub_id ) +{ + // map.exd field 12 -> index in one of the two discovery sections, if field 15 is false, need to use 2nd section + // section 1 starts at 4 - 2 bytes each + + // section to starts at 320 - 4 bytes long + + int32_t offset = 4; + + auto info = g_exdData.m_zoneInfoMap[getCurrentZone()->getId()]; + if( info.is_two_byte ) + offset = 4 + 2 * info.discovery_index; + else + offset = 324 + 4 * info.discovery_index; + + int32_t index = offset + sub_id / 8; + uint8_t bitIndex = sub_id % 8; + + uint8_t value = 1 << bitIndex; + + m_discovery[index] |= value; + + setSyncFlag( PlayerSyncFlags::Discovery ); + + uint16_t level = getLevel(); + + uint32_t exp = ( g_exdData.m_paramGrowthInfoMap[level].needed_exp * 5 / 100 ); + + gainExp( exp ); + + + +} + +bool Core::Entity::Player::isNewAdventurer() const +{ + return m_bNewAdventurer; +} + +void Core::Entity::Player::setNewAdventurer( bool state ) +{ + if( !state ) + { + unsetStateFlag( PlayerStateFlag::NewAdventurer ); + } + else + { + setStateFlag( PlayerStateFlag::NewAdventurer ); + } + sendStateFlags(); + m_bNewAdventurer = state; + setSyncFlag( PlayerSyncFlags::NewAdventurer ); +} + +void Core::Entity::Player::resetDiscovery() +{ + memset( m_discovery, 0, sizeof( m_discovery ) ); + setSyncFlag( PlayerSyncFlags::Discovery ); +} + +void Core::Entity::Player::changePosition( float x, float y, float z, float o ) +{ + Common::FFXIVARR_POSITION3 pos; + pos.x = x; + pos.y = y; + pos.z = z; + m_queuedZoneing = boost::make_shared( getZoneId(), pos, Util::getTimeMs(), o ); +} + +void Core::Entity::Player::learnAction( uint8_t actionId ) +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( actionId, value, index ); + + m_unlocks[index] |= value; + + setSyncFlag( Unlocks ); + queuePacket( ActorControlPacket143( getId(), ToggleActionUnlock, actionId, 1 ) ); +} + +bool Core::Entity::Player::isActionLearned( uint8_t actionId ) const +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( actionId, value, index ); + + return m_unlocks[index] & value; +} + +void Core::Entity::Player::gainExp( uint32_t amount ) +{ + uint32_t currentExp = getExp(); + + uint16_t level = getLevel(); + + uint32_t neededExpToLevel = g_exdData.m_paramGrowthInfoMap[level].needed_exp; + + uint32_t neededExpToLevelplus1 = g_exdData.m_paramGrowthInfoMap[level + 1].needed_exp; + + queuePacket( ActorControlPacket143( getId(), GainExpMsg, static_cast< uint8_t >( getClass() ), amount ) ); + + if( level >= 60 ) // temporary fix for leveling over levelcap + { + queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), amount ) ); + return; + } + + if( ( currentExp + amount ) >= neededExpToLevel ) + { + // levelup + amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelplus1 ? neededExpToLevelplus1 - 1 : ( currentExp + amount - neededExpToLevel ); + setExp( amount ); + gainLevel(); + queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), amount ) ); + + } + else + { + queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), currentExp + amount ) ); + setExp( currentExp + amount ); + } + + sendStatusUpdate(); + setSyncFlag( PlayerSyncFlags::ExpLevel ); +} + +void Core::Entity::Player::gainLevel() +{ + setLevel( getLevel() + 1 ); + + calculateStats(); + sendStats(); + sendStatusUpdate(); + + m_hp = getMaxHp(); + m_mp = getMaxMp(); + + GamePacketNew< FFXIVIpcStatusEffectList > effectListPacket( getId() ); + effectListPacket.data().classId = getClass(); + effectListPacket.data().classId1 = getClass(); + effectListPacket.data().level = getLevel(); + effectListPacket.data().current_hp = getMaxHp(); + effectListPacket.data().current_mp = getMaxMp(); + effectListPacket.data().currentTp = 1000; + effectListPacket.data().max_hp = getMaxHp(); + effectListPacket.data().max_mp = getMaxMp(); + sendToInRangeSet( effectListPacket, true ); + + sendToInRangeSet( ActorControlPacket142( getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ), + getLevel(), getLevel() - 1 ), true ); + + + GamePacketNew< FFXIVIpcUpdateClassInfo > classInfoPacket( getId() ); + classInfoPacket.data().classId = getClass(); + classInfoPacket.data().classId1 = getClass(); + classInfoPacket.data().level = getLevel(); + classInfoPacket.data().nextLevelIndex = getLevel(); + classInfoPacket.data().currentExp = getExp(); + queuePacket( classInfoPacket ); + +} + +void Core::Entity::Player::unlock() +{ + queuePacket( PlayerStateFlagsPacket( getAsPlayer(), PlayerStateFlagList{} ) ); +} + +void Core::Entity::Player::sendStatusUpdate( bool toSelf ) +{ + // CGamePacket* pPE = new CGamePacket(0x140, 0x0128, getId(), getId()); + + //pPE->setInt8At(0x20, static_cast(getClass())); + + // pPE->setInt8At(0x21, getLevel()); + // pPE->setInt8At(0x22, getLevel()); + + // // current exp + // pPE->setInt32At(0x28, getExp()); + + // // rested exp + // //pPE->setInt32At(0x2C, m_hp); + + // pPE->setInt32At(0x24, m_hp); + // pPE->setInt32At(0x28, getMaxHp()); + // pPE->setInt16At(0x2C, m_mp); + // pPE->setInt16At(0x2E, getMaxMp()); + // pPE->setInt16At(0x30, m_tp); + + // sendToInRangeSet(pPE, toSelf); + + sendToInRangeSet( UpdateHpMpTpPacket( shared_from_this() ), true ); + +} + +uint8_t Core::Entity::Player::getLevel() const +{ + uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx; + return static_cast< uint8_t >( m_classArray[classJobIndex] ); +} + +uint32_t Core::Entity::Player::getExp() const +{ + uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx; + return m_expArray[classJobIndex]; +} + +void Core::Entity::Player::setExp( uint32_t amount ) +{ + uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx; + m_expArray[classJobIndex] = amount; +} + +bool Core::Entity::Player::isInCombat() const +{ + return m_bInCombat; +} + +void Core::Entity::Player::setInCombat( bool mode ) +{ + //m_lastAttack = GetTickCount(); + m_bInCombat = mode; +} + +void Core::Entity::Player::setClassJob( Core::Common::ClassJob classJob ) +{ + m_class = classJob; + uint8_t level = getLevel(); + + if( getHp() > getMaxHp() ) + m_hp = getMaxHp(); + + if( getMp() > getMaxMp() ) + m_mp = getMaxMp(); + + m_tp = 0; + + GamePacketNew< FFXIVIpcPlayerClassInfo > classInfoPacket( getId() ); + classInfoPacket.data().classId = getClass(); + classInfoPacket.data().level = getLevel(); + queuePacket( classInfoPacket ); + + sendToInRangeSet( ActorControlPacket142( getId(), ClassJobChange, 0x04 ), true ); + + setSyncFlag( Status ); + sendStatusUpdate( true ); +} + +void Core::Entity::Player::setLevel( uint8_t level ) +{ + uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx; + m_classArray[classJobIndex] = level; + + setSyncFlag( PlayerSyncFlags::ExpLevel ); +} + +uint8_t Core::Entity::Player::getUserLevel() const +{ + return m_userLevel; +} + +bool Core::Entity::Player::isFirstLogin() const +{ + return m_bFirstLogin; +} + +void Core::Entity::Player::setFirstLogin( bool mode ) +{ + m_bFirstLogin = mode; + setSyncFlag( PlayerSyncFlags::FirstLogin ); +} + +void Core::Entity::Player::eventActionStart( uint32_t eventId, + uint32_t action, + ActionCallback finishCallback, + ActionCallback interruptCallback, + uint64_t additional ) +{ + Action::ActionPtr pEventAction( new Action::EventAction( shared_from_this(), eventId, action, + finishCallback, interruptCallback, additional ) ); + + setCurrentAction( pEventAction ); + auto pEvent = getEvent( eventId ); + + if( !pEvent && getEventCount() ) + { + // We're trying to play a nested event, need to start it first. + eventStart( getId(), eventId, Event::Event::Nest, 0, 0 ); + pEvent = getEvent( eventId ); + } + else if( !pEvent ) + { + g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" ); + return; + } + + if( pEvent ) + pEvent->setPlayedScene( true ); + pEventAction->onStart(); +} + + +void Core::Entity::Player::eventItemActionStart( uint32_t eventId, + uint32_t action, + ActionCallback finishCallback, + ActionCallback interruptCallback, + uint64_t additional ) +{ + Action::ActionPtr pEventItemAction( new Action::EventItemAction( shared_from_this(), eventId, action, + finishCallback, interruptCallback, additional ) ); + + setCurrentAction( pEventItemAction ); + + pEventItemAction->onStart(); +} + +void Core::Entity::Player::sendModel() +{ + ModelEquipPacket modelEquip( getAsPlayer() ); + sendToInRangeSet( modelEquip, true ); +} + +uint32_t Core::Entity::Player::getModelForSlot( Inventory::EquipSlot slot ) +{ + return m_modelEquip[slot]; +} + +uint64_t Core::Entity::Player::getModelMainWeapon() const +{ + return m_modelMainWeapon; +} + +uint64_t Core::Entity::Player::getModelSubWeapon() const +{ + return m_modelSubWeapon; +} + +uint64_t Core::Entity::Player::getModelSystemWeapon() const +{ + return m_modelSystemWeapon; +} + +int8_t Core::Entity::Player::getAetheryteMaskAt( uint8_t index ) const +{ + if( index > 11 ) + return 0; + return m_aetheryte[index]; +} + +uint8_t Core::Entity::Player::getBirthDay() const +{ + return m_birthDay; +} + +uint8_t Core::Entity::Player::getBirthMonth() const +{ + return m_birthMonth; +} + +uint8_t Core::Entity::Player::getGuardianDeity() const +{ + return m_guardianDeity; +} + +uint8_t Core::Entity::Player::getLookAt( uint8_t index ) const +{ + return m_customize[index]; +} + +void Core::Entity::Player::setLookAt( uint8_t index, uint8_t value ) +{ + m_customize[index] = value; + setSyncFlag( PlayerSyncFlags::Look ); +} + +// spawn this player for pTarget +void Core::Entity::Player::spawn( Core::Entity::PlayerPtr pTarget ) +{ + g_log.debug( "[" + std::to_string( pTarget->getId() ) + "] Spawning " + + getName() + " for " + + pTarget->getName() ); + + PlayerSpawnPacket spawnActor( getAsPlayer(), pTarget ); + pTarget->queuePacket( spawnActor ); +} + +// despawn +void Core::Entity::Player::despawn( Core::Entity::ActorPtr pTarget ) +{ + auto pPlayer = pTarget->getAsPlayer(); + + pPlayer->freePlayerSpawnId( getId() ); + + pPlayer->queuePacket( ActorControlPacket143( getId(), DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) ); +} + +void Core::Entity::Player::setLastPing( uint32_t ping ) +{ + m_lastPing = ping; +} + +uint32_t Core::Entity::Player::getLastPing() const +{ + return m_lastPing; +} + + +void Core::Entity::Player::setVoiceId( uint8_t voiceId ) +{ + m_voice = voiceId; +} + +void Core::Entity::Player::setGc( uint8_t gc ) +{ + m_gc = gc; + + GamePacketNew< FFXIVGCAffiliation > gcAffPacket( getId() ); + gcAffPacket.data().gcId = m_gc; + gcAffPacket.data().gcRank[0] = m_gcRank[0]; + gcAffPacket.data().gcRank[1] = m_gcRank[1]; + gcAffPacket.data().gcRank[2] = m_gcRank[2]; + queuePacket( gcAffPacket ); + + setSyncFlag( PlayerSyncFlags::GC ); +} + +void Core::Entity::Player::setGcRankAt( uint8_t index, uint8_t rank ) +{ + m_gcRank[index] = rank; + + GamePacketNew< FFXIVGCAffiliation > gcAffPacket( getId() ); + gcAffPacket.data().gcId = m_gc; + gcAffPacket.data().gcRank[0] = m_gcRank[0]; + gcAffPacket.data().gcRank[1] = m_gcRank[1]; + gcAffPacket.data().gcRank[2] = m_gcRank[2]; + queuePacket( gcAffPacket ); + + setSyncFlag( PlayerSyncFlags::GC ); +} + +const uint8_t * Core::Entity::Player::getStateFlags() const +{ + return m_stateFlags; +} + +bool Core::Entity::Player::hasStateFlag( Core::Common::PlayerStateFlag flag ) const +{ + int iFlag = static_cast< uint32_t >( flag ); + + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( iFlag, value, index ); + + return m_stateFlags[index] & value; +} + +void Core::Entity::Player::setStateFlag( Core::Common::PlayerStateFlag flag ) +{ + int iFlag = static_cast< uint32_t >( flag ); + + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( iFlag, value, index ); + + m_stateFlags[index] |= value; + +} + +void Core::Entity::Player::sendStateFlags() +{ + queuePacket( PlayerStateFlagsPacket( getAsPlayer() ) ); +} + +void Core::Entity::Player::unsetStateFlag( Core::Common::PlayerStateFlag flag ) +{ + if( !hasStateFlag( flag ) ) + return; + + int iFlag = static_cast< uint32_t >( flag ); + + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( iFlag, value, index ); + + m_stateFlags[index] ^= value; + +} + +void Core::Entity::Player::update( int64_t currTime ) +{ + + // a zoning is pending, lets do it + if( m_queuedZoneing && ( currTime - m_queuedZoneing->m_queueTime ) > 800 ) + { + Common::FFXIVARR_POSITION3 targetPos = m_queuedZoneing->m_targetPosition; + if( getCurrentZone()->getId() != m_queuedZoneing->m_targetZone ) + { + performZoning( m_queuedZoneing->m_targetZone, targetPos, m_queuedZoneing->m_targetRotation); + } + else + { + GamePacketNew< FFXIVIpcActorSetPos > setActorPosPacket( getId() ); + setActorPosPacket.data().r16 = Math::Util::floatToUInt16Rot( m_queuedZoneing->m_targetRotation ); + setActorPosPacket.data().waitForLoad = 0x04; + setActorPosPacket.data().x = targetPos.x; + setActorPosPacket.data().y = targetPos.y; + setActorPosPacket.data().z = targetPos.z; + sendToInRangeSet( setActorPosPacket, true ); + setPosition( targetPos ); + } + m_queuedZoneing.reset(); + return; + } + + if( m_hp <= 0 && m_status != ActorStatus::Dead ) + { + die(); + setSyncFlag( PlayerSyncFlags::Status ); + } + + if( !isAlive() ) + return; + + m_pStatusEffectContainer->update(); + + m_lastUpdate = currTime; + + // @TODO needs to happen in a if check. Don't want autoattacking while an action is being performed. + if( !checkAction() ) + { + if( m_targetId ) + { + auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand ); + + for( auto actor : m_inRangeActors ) + { + if( isAutoattackOn() && + actor->getId() == m_targetId && + actor->isAlive() && + mainWeap ) + { + // default autoattack range + // TODO make this dependant on bnpc size + uint32_t range = 7; + + // default autoattack range for ranged classes + if( getClass() == JOB_MACHINIST || + getClass() == JOB_BARD || + getClass() == CLASS_ARCHER ) + range = 25; + + + if( Math::Util::distance(getPos().x, getPos().y, getPos().z, + actor->getPos().x, actor->getPos().y, actor->getPos().z) <= range ) + { + + if( ( currTime - m_lastAttack ) > mainWeap->getDelay() ) + { + m_lastAttack = currTime; + autoAttack( actor ); + } + + } + } + } + } + } + + if( ( currTime - m_lastTickTime ) > 3000 ) + { + // add 3 seconds to total play time + m_playTime += 3; + setSyncFlag( PlayerSyncFlags::PlayTime ); + + m_lastTickTime = currTime; + onTick(); + } + + createUpdateSql(); + +} + +void Core::Entity::Player::onMobKill( uint16_t nameId ) +{ + g_scriptMgr.onMobKill( getAsPlayer(), nameId ); +} + +void Core::Entity::Player::freePlayerSpawnId( uint32_t actorId ) +{ + uint8_t spawnId = m_playerIdToSpawnIdMap[actorId]; + m_playerIdToSpawnIdMap.erase( actorId ); + m_freeSpawnIdQueue.push( spawnId ); + + GamePacketNew< FFXIVIpcActorFreeSpawn > freeActorSpawnPacket( getId() ); + freeActorSpawnPacket.data().actorId = actorId; + freeActorSpawnPacket.data().spawnId = spawnId; + queuePacket( freeActorSpawnPacket ); + +} + +void Core::Entity::Player::setSyncFlag( uint32_t updateFlag ) +{ + m_updateFlags |= updateFlag; +} + +uint8_t * Core::Entity::Player::getAetheryteArray() +{ + return m_aetheryte; +} + +/*! set homepoint */ +void Core::Entity::Player::setHomepoint( uint8_t aetheryteId ) +{ + m_homePoint = aetheryteId; + + queuePacket( ActorControlPacket143( getId(), SetHomepoint, aetheryteId ) ); + + setSyncFlag( HomePoint ); +} + +/*! get homepoint */ +uint8_t Core::Entity::Player::getHomepoint() const +{ + return m_homePoint; +} + +uint16_t * Core::Entity::Player::getClassArray() +{ + return m_classArray; +} + +const uint16_t * Core::Entity::Player::getClassArray() const +{ + return m_classArray; +} + +const uint8_t * Core::Entity::Player::getLookArray() const +{ + return m_customize; +} + +const uint32_t * Core::Entity::Player::getModelArray() const +{ + return m_modelEquip; +} + +uint32_t * Core::Entity::Player::getExpArray() +{ + return m_expArray; +} + +const uint32_t * Core::Entity::Player::getExpArray() const +{ + return m_expArray; +} + +uint8_t * Core::Entity::Player::getHowToArray() +{ + return m_howTo; +} + +const uint8_t * Core::Entity::Player::getHowToArray() const +{ + return m_howTo; +} + +const uint8_t * Core::Entity::Player::getUnlockBitmask() const +{ + return m_unlocks; +} + +uint64_t Core::Entity::Player::getContentId() const +{ + return m_contentId; +} + +uint8_t Core::Entity::Player::getVoiceId() const +{ + return m_voice; +} + +uint8_t Core::Entity::Player::getGc() const +{ + return m_gc; +} + +const uint8_t * Core::Entity::Player::getGcRankArray() const +{ + return m_gcRank; +} + +void Core::Entity::Player::queuePacket( Core::Network::Packets::GamePacketPtr pPacket ) +{ + auto pSession = g_serverZone.getSession( m_id ); + + if( pSession ) + { + auto pZoneCon = pSession->getZoneConnection(); + + if( pZoneCon ) + pZoneCon->queueOutPacket( pPacket ); + } +} + +bool Core::Entity::Player::isLoadingComplete() const +{ + return m_bLoadingComplete; +} + +void Core::Entity::Player::setLoadingComplete( bool bComplete ) +{ + m_bLoadingComplete = bComplete; +} + +void Core::Entity::Player::performZoning(uint16_t zoneId, const Common::FFXIVARR_POSITION3 &pos, float rotation) +{ + m_pos = pos; + m_zoneId = zoneId; + m_bMarkedForZoning = true; + setRotation( rotation ); + + // mark the player for a position update in DB + setSyncFlag( PlayerSyncFlags::Position ); + setZone( zoneId ); +} + +bool Core::Entity::Player::isMarkedForZoning() const +{ + return m_bMarkedForZoning; +} + +ZoneingType Core::Entity::Player::getZoningType() const +{ + return m_zoningType; +} + +void Core::Entity::Player::setZoningType( Common::ZoneingType zoneingType ) +{ + m_zoningType = zoneingType; +} + +void Core::Entity::Player::setSearchInfo( uint8_t selectRegion, uint8_t selectClass, const char* searchMessage ) +{ + m_searchSelectRegion = selectRegion; + m_searchSelectClass = selectClass; + memset( &m_searchMessage[0], 0, sizeof( searchMessage ) ); + strcpy( &m_searchMessage[0], searchMessage ); + + setSyncFlag( PlayerSyncFlags::SearchInfo ); +} + +const char* Core::Entity::Player::getSearchMessage() const +{ + return &m_searchMessage[0]; +} + +uint8_t Core::Entity::Player::getSearchSelectRegion() const +{ + return m_searchSelectRegion; +} + +uint8_t Core::Entity::Player::getSearchSelectClass() const +{ + return m_searchSelectClass; +} + +void Core::Entity::Player::sendNotice( const std::string& message ) //Purple Text +{ + queuePacket( ServerNoticePacket( getId(), message ) ); +} + +void Core::Entity::Player::sendUrgent( const std::string& message ) //Red Text +{ + queuePacket( ChatPacket( getAsPlayer(), ChatType::ServerUrgent, message ) ); +} + +void Core::Entity::Player::sendDebug( const std::string& message ) //Grey Text +{ + queuePacket( ChatPacket( getAsPlayer(), ChatType::ServerDebug, message ) ); +} + +void Core::Entity::Player::updateHowtosSeen( uint32_t howToId ) +{ + uint8_t index = howToId / 8; + uint8_t bitIndex = howToId % 8; + + uint8_t value = 1 << bitIndex; + + m_howTo[index] |= value; + + setSyncFlag( PlayerSyncFlags::HowTo ); +} + + +void Core::Entity::Player::onMobAggro( Core::Entity::BattleNpcPtr pBNpc ) +{ + hateListAdd( pBNpc ); + + queuePacket( ActorControlPacket142( getId(), ToggleAggro, 1 ) ); +} + +void Core::Entity::Player::onMobDeaggro( Core::Entity::BattleNpcPtr pBNpc ) +{ + hateListRemove( pBNpc ); + + if( m_actorIdTohateSlotMap.empty() ) + queuePacket( ActorControlPacket142( getId(), ToggleAggro ) ); +} + +void Core::Entity::Player::hateListAdd( Core::Entity::BattleNpcPtr pBNpc ) + +{ + if( !m_freeHateSlotQueue.empty() ) + { + uint8_t hateId = m_freeHateSlotQueue.front(); + m_freeHateSlotQueue.pop(); + m_actorIdTohateSlotMap[pBNpc->getId()] = hateId; + sendHateList(); + } +} + +void Core::Entity::Player::hateListRemove( Core::Entity::BattleNpcPtr pBNpc ) +{ + + auto it = m_actorIdTohateSlotMap.begin(); + for( ; it != m_actorIdTohateSlotMap.end(); ++it ) + { + if( it->first == pBNpc->getId() ) + { + uint8_t hateSlot = it->second; + m_freeHateSlotQueue.push( hateSlot ); + m_actorIdTohateSlotMap.erase( it ); + sendHateList(); + + return; + } + } +} + +bool Core::Entity::Player::hateListHasMob( Core::Entity::BattleNpcPtr pBNpc ) +{ + + auto it = m_actorIdTohateSlotMap.begin(); + for( ; it != m_actorIdTohateSlotMap.end(); ++it ) + { + if( it->first == pBNpc->getId() ) + return true; + } + return false; +} + +void Core::Entity::Player::initHateSlotQueue() +{ + m_freeHateSlotQueue = std::queue< uint8_t >(); + for( int i = 1; i < 26; i++ ) + m_freeHateSlotQueue.push( i ); +} + +void Core::Entity::Player::sendHateList() +{ + GamePacketNew< FFXIVIpcHateList > hateListPacket( getId() ); + hateListPacket.data().numEntries = m_actorIdTohateSlotMap.size(); + auto it = m_actorIdTohateSlotMap.begin(); + for( int i = 0; it != m_actorIdTohateSlotMap.end(); ++it, i++ ) + { + hateListPacket.data().entry[i].actorId = it->first; + hateListPacket.data().entry[i].hatePercent = 100; + } + queuePacket( hateListPacket ); +} + +bool Core::Entity::Player::isLogin() const +{ + return m_bIsLogin; +} + +void Core::Entity::Player::setIsLogin( bool bIsLogin ) +{ + m_bIsLogin = bIsLogin; +} + +void Core::Entity::Player::autoAttack( ActorPtr pTarget ) +{ + + pTarget->onActionHostile( shared_from_this() ); + //uint64_t tick = Util::getTimeMs(); + + //srand(static_cast< uint32_t >(tick)); + uint32_t damage = 10 + rand() % 12; + uint32_t variation = 0 + rand() % 3; + + if( getClass() == 5 || getClass() == 23 || getClass() == 31 ) + { + GamePacketNew< FFXIVIpcEffect > effectPacket(getId()); + effectPacket.data().targetId = pTarget->getId(); + effectPacket.data().actionAnimationId = 8; + // effectPacket.data().unknown_2 = variation; + effectPacket.data().numEffects = 1; + effectPacket.data().unknown_61 = 1; + effectPacket.data().unknown_62 = 1; + effectPacket.data().actionTextId = 8; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); + effectPacket.data().effectTargetId = pTarget->getId(); + effectPacket.data().effectTarget = pTarget->getId(); + effectPacket.data().effects[0].param1 = damage; + effectPacket.data().effects[0].unknown_1 = 3; + effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].unknown_3 = 7; + + sendToInRangeSet(effectPacket, true); + } + else + { + + GamePacketNew< FFXIVIpcEffect > effectPacket(getId()); + effectPacket.data().targetId = pTarget->getId(); + effectPacket.data().actionAnimationId = 7; + // effectPacket.data().unknown_2 = variation; + effectPacket.data().numEffects = 1; + effectPacket.data().unknown_61 = 1; + effectPacket.data().unknown_62 = 1; + effectPacket.data().actionTextId = 7; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); + effectPacket.data().effectTarget = pTarget->getId(); + effectPacket.data().effects[0].param1 = damage; + effectPacket.data().effects[0].unknown_1 = 3; + effectPacket.data().effects[0].unknown_2 = 2; + effectPacket.data().effects[0].unknown_3 = 71; + + sendToInRangeSet(effectPacket, true); + } + + pTarget->takeDamage(damage); + +} diff --git a/src/servers/Server_Zone/Player.h b/src/servers/Server_Zone/Player.h new file mode 100644 index 00000000..4f2ae69d --- /dev/null +++ b/src/servers/Server_Zone/Player.h @@ -0,0 +1,591 @@ +#ifndef _PLAYER_H +#define _PLAYER_H + +#include "Forwards.h" + +#include + +#include "Actor.h" +#include "Inventory.h" +#include +#include + +namespace Core { + +namespace Entity { + +struct QueuedZoning +{ + uint16_t m_targetZone; + Common::FFXIVARR_POSITION3 m_targetPosition; + float m_targetRotation; + uint64_t m_queueTime; + + QueuedZoning( uint16_t targetZone, const Common::FFXIVARR_POSITION3& targetPosition, uint64_t queuedTime, float targetRotation ) + : m_targetZone( targetZone ) + , m_targetPosition( targetPosition ) + , m_queueTime( queuedTime ) + , m_targetRotation( targetRotation ) {} +}; + +/** Class representing the Player +* Inheriting from Actor +* +*/ +class Player : public Actor +{ +public: + /*! Contructor */ + Player(); + + /*! Destructor */ + ~Player(); + + void autoAttack( ActorPtr pTarget ) override; + + // EventHandlers + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! start an event action */ + void eventActionStart( uint32_t eventId, uint32_t action, ActionCallback finishCallback, ActionCallback interruptCallback, uint64_t additional ); + /*! start an event item action */ + void eventItemActionStart( uint32_t eventId, uint32_t action, ActionCallback finishCallback, ActionCallback interruptCallback, uint64_t additional ); + /*! start/register a normal event */ + void eventStart( uint64_t actorId, uint32_t eventId, uint8_t eventParam, uint8_t eventParam1, uint32_t eventParam2 ); + /*! play a subevent */ + void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t eventParam2, uint32_t eventParam3 ); + /*! play a subevent */ + void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, + uint32_t eventParam2, uint32_t eventParam3, Scripting::EventReturnCallback eventReturnCallback ); + /*! play a subevent */ + void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, + uint32_t eventParam2, uint32_t eventParam3, uint32_t eventParam4, Scripting::EventReturnCallback eventReturnCallback ); + /*! play a subevent */ + void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags, Scripting::EventReturnCallback eventReturnCallback ); + /*! play a subevent */ + void eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags ); + /*! finish / unregister an event */ + void eventFinish( uint32_t eventId, uint32_t freePlayer ); + /*! add an event to the event array */ + void addEvent( Event::EventPtr pEvent ); + /*! retrieve an event from the event array */ + Event::EventPtr getEvent( uint32_t eventId ); + /*! get number of active events */ + size_t getEventCount(); + /*! remove an event from the event array */ + void removeEvent( uint32_t eventId ); + /*! return the eventlist */ + std::map< uint32_t, Event::EventPtr >& eventList(); + + void checkEvent( uint32_t eventId ); + + + + // Events + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! Event to be called when zoning process gets triggered */ + void onZoneStart(); + /*! Event to be called when zoning process is finished */ + void onZoneDone(); + /*! Event to be called on login */ + void onLogin(); + /*! Event to be called on update tick */ + void onTick() override; + /*! Event to be called upon player death */ + void onDeath() override; + /*! Event called on every session iteration */ + void update( int64_t currTime ) override; + /*! Event to be called upon Bnpc kill */ + void onMobKill( uint16_t nameId ); + + + // Quest + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! load data for currently active quests */ + bool loadActiveQuests(); + /*! update quest ( register it as active quest if new ) */ + void updateQuest( uint16_t questId, uint16_t sequence ); + /*! return true if quest is currently active */ + bool hasQuest( uint16_t questId ); + /*! return the current quest sequence */ + uint8_t getQuestSeq( uint16_t questId ); + /*! send the quest tracker packet */ + void sendQuestTracker(); + /*! set quest tracker flag for a specified slot */ + void setQuestTracker( uint16_t index, int16_t flag ); + /*! return the index of a given quest in the players quest list */ + int16_t getQuestIndex( uint16_t questId ); + /*! finish a given quest */ + void finishQuest( uint16_t questId ); + /*! finish a given quest */ + void unfinishQuest( uint16_t questId ); + /*! remove a given quest */ + void removeQuest( uint16_t questId ); + /*! add a quest to the completed quests mask */ + void updateQuestsCompleted( uint32_t questId ); + /*! remove a quest from the completed quest mask */ + void removeQuestsCompleted( uint32_t questId ); + + bool giveQuestRewards( uint32_t questId, uint32_t optionalChoice ); + + boost::shared_ptr< Common::QuestActive > getQuestActive( uint16_t index ); + + uint8_t getQuestUI8A( uint16_t questId ); + uint8_t getQuestUI8B( uint16_t questId ); + uint8_t getQuestUI8C( uint16_t questId ); + uint8_t getQuestUI8D( uint16_t questId ); + uint8_t getQuestUI8E( uint16_t questId ); + uint8_t getQuestUI8F( uint16_t questId ); + uint8_t getQuestUI8AH( uint16_t questId ); + uint8_t getQuestUI8BH( uint16_t questId ); + uint8_t getQuestUI8CH( uint16_t questId ); + uint8_t getQuestUI8DH( uint16_t questId ); + uint8_t getQuestUI8EH( uint16_t questId ); + uint8_t getQuestUI8FH( uint16_t questId ); + uint8_t getQuestUI8AL( uint16_t questId ); + uint8_t getQuestUI8BL( uint16_t questId ); + uint8_t getQuestUI8CL( uint16_t questId ); + uint8_t getQuestUI8DL( uint16_t questId ); + uint8_t getQuestUI8EL( uint16_t questId ); + uint8_t getQuestUI8FL( uint16_t questId ); + uint16_t getQuestUI16A( uint16_t questId ); + uint16_t getQuestUI16B( uint16_t questId ); + uint16_t getQuestUI16C( uint16_t questId ); + uint32_t getQuestUI32A( uint16_t questId ); + + uint8_t getQuestBitFlag8( uint16_t questId ); + uint8_t getQuestBitFlag16( uint16_t questId ); + uint8_t getQuestBitFlag24( uint16_t questId ); + uint8_t getQuestBitFlag32( uint16_t questId ); + uint8_t getQuestBitFlag40( uint16_t questId ); + uint8_t getQuestBitFlag48( uint16_t questId ); + + + void setQuestUI8A( uint16_t questId, uint8_t val ); + void setQuestUI8B( uint16_t questId, uint8_t val ); + void setQuestUI8C( uint16_t questId, uint8_t val ); + void setQuestUI8D( uint16_t questId, uint8_t val ); + void setQuestUI8E( uint16_t questId, uint8_t val ); + void setQuestUI8F( uint16_t questId, uint8_t val ); + void setQuestUI8AH( uint16_t questId, uint8_t val ); + void setQuestUI8BH( uint16_t questId, uint8_t val ); + void setQuestUI8CH( uint16_t questId, uint8_t val ); + void setQuestUI8DH( uint16_t questId, uint8_t val ); + void setQuestUI8EH( uint16_t questId, uint8_t val ); + void setQuestUI8FH( uint16_t questId, uint8_t val ); + void setQuestUI8AL( uint16_t questId, uint8_t val ); + void setQuestUI8BL( uint16_t questId, uint8_t val ); + void setQuestUI8CL( uint16_t questId, uint8_t val ); + void setQuestUI8DL( uint16_t questId, uint8_t val ); + void setQuestUI8EL( uint16_t questId, uint8_t val ); + void setQuestUI8FL( uint16_t questId, uint8_t val ); + void setQuestUI16A( uint16_t questId, uint16_t val ); + void setQuestUI16B( uint16_t questId, uint16_t val ); + void setQuestUI16C( uint16_t questId, uint16_t val ); + void setQuestUI32A( uint16_t questId, uint32_t val ); + + void setQuestBitFlag8( uint16_t questId, uint8_t val ); + void setQuestBitFlag16( uint16_t questId, uint8_t val ); + void setQuestBitFlag24( uint16_t questId, uint8_t val ); + void setQuestBitFlag32( uint16_t questId, uint8_t val ); + void setQuestBitFlag40( uint16_t questId, uint8_t val ); + void setQuestBitFlag48( uint16_t questId, uint8_t val ); + + // Inventory / Item / Currency + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! add an item to the first free slot in one of the 4 main containers */ + bool tryAddItem( uint16_t catalogId, uint16_t quantity ); + /*! add an item to a given container */ + bool addItem( uint16_t containerId, uint16_t catalogId, uint16_t quantity ); + /*! equip an item to a specified slot */ + void equipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem, bool sendModel ); + /*! remove an item from an equipment slot */ + void unequipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem ); + /*! equip a weapon, possibly forcing a job change */ + void equipWeapon( ItemPtr pItem ); + /*! get a const pointer to the inventory object */ + InventoryPtr getInvetory() const; + /*! get the current main hand model */ + uint64_t getModelMainWeapon() const; + /*! get the current off hand model */ + uint64_t getModelSubWeapon() const; + /*! get the current system hand model */ + uint64_t getModelSystemWeapon() const; + /*! return a const pointer to the model array */ + const uint32_t * getModelArray() const; + /*! return the equipment model in a specified equipment slot */ + uint32_t getModelForSlot( Inventory::EquipSlot slot ); + /*! return the current amount of currency of type */ + uint32_t getCurrency( uint8_t type ) const; + /*! add amount to the currency of type */ + void addCurrency( uint8_t type, uint32_t amount ); + /*! remove amount from the currency of type */ + void removeCurrency( uint8_t type, uint32_t amount ); + /*! return the current amount of crystals of type */ + uint32_t getCrystal( uint8_t type ) const; + /*! add amount to the crystals of type */ + void addCrystal( uint8_t type, uint32_t amount ); + /*! remove amount from the crystals of type */ + void removeCrystal( uint8_t type, uint32_t amount ); + + // Class / Job / Exp + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! returns the level of the currently active class / job */ + uint8_t getLevel() const override; + /*! returns the exp of the currently active class / job */ + uint32_t getExp() const; + /*! sets the exp of the currently active class / job */ + void setExp( uint32_t amount ); + /*! adds exp to the currently active class / job */ + void gainExp( uint32_t amount ); + /*! gain a level on the currently active class / job */ + void gainLevel(); + /*! set level on the currently active class / job to given level */ + void setLevel( uint8_t level ); + /*! change class or job to given class / job */ + void setClassJob( Core::Common::ClassJob classJob ); + /*! returns a pointer to the class array */ + uint16_t * getClassArray(); + /*! returns a const pointer to the class array */ + const uint16_t * getClassArray() const; + /*! returns a pointer to the exp array */ + uint32_t * getExpArray(); + /*! returns a const pointer to the exp array */ + const uint32_t * getExpArray() const; + + // Base Look / Stats / Params + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! return the birth day */ + uint8_t getBirthDay() const; + /*! return the birth month */ + uint8_t getBirthMonth() const; + /*! return the guardian diety Id */ + uint8_t getGuardianDeity() const; + /*! get look at specified index */ + uint8_t getLookAt( uint8_t index ) const; + /*! return the race */ + uint8_t getRace() const; + /*! return gender 0 male, 1 female */ + uint8_t getGender() const; + /*! return the id of the home town */ + uint8_t getStartTown() const; + /*! return the voice id */ + uint8_t getVoiceId() const; + /*! return the grand company */ + uint8_t getGc() const; + /*! return the grand company rank */ + const uint8_t * getGcRankArray() const; + /*! set look at index */ + void setLookAt( uint8_t index, uint8_t value ); + /*! set the voice Id */ + void setVoiceId( uint8_t voiceId ); + /*! set the grand company */ + void setGc( uint8_t gc ); + /*! set the grand company rank */ + void setGcRankAt( uint8_t index, uint8_t rank ); + /*! return a const pointer to the look array */ + const uint8_t * getLookArray() const; + /*! return true if the player is loggin in for the first time */ + bool isFirstLogin() const; + /*! set first log in state */ + void setFirstLogin( bool mode ); + /*! returns true if the player is currently in combat */ + bool isInCombat() const; + /*! sets players combat state */ + void setInCombat( bool mode ); + /*! return current online status depending on current state / activity */ + Common::OnlineStatus getOnlineStatus(); + /*! sets the players zone, initiating a zoning process */ + void setZone( uint32_t zoneId ); + + void forceZoneing( uint32_t zoneId ); + /*! change position, sends update too */ + void changePosition( float x, float y, float z, float o ); + /*! return the contentId */ + uint64_t getContentId() const; + /*! return max hp */ + uint32_t getMaxHp(); + /*! return max mp */ + uint32_t getMaxMp(); + /*! return a players total play time */ + uint32_t getPlayTime() const; + /*! return true if the player has "new adventurere" status */ + bool isNewAdventurer() const; + /*! change the players "new adventurere" status */ + void setNewAdventurer( bool state ); + /*! sets the list of current online status */ + void setOnlineStatusMask( uint64_t status ); + /*! returns the current online status */ + uint64_t getOnlineStatusMask() const; + /*! perform a teleport of a specified type ( teleport,return,aethernet ) */ + void teleport( uint16_t aetheryteId, uint8_t type = 1 ); + /*! prepares zoning / fades out the screen */ + void prepareZoning( uint16_t targetZone, bool fadeOut ); + + void calculateStats() override; + void sendStats(); + + + // Aetheryte / Action / Attribute bitmasks + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! register aetheryte aetheryteId and send update */ + void registerAetheryte( uint8_t aetheryteId ); + /*! check if aetheryte is already registered */ + bool isAetheryteRegistered( uint8_t aetheryteId ) const; + /*! return a const pointer to the aetheryte unlock bitmask array */ + int8_t getAetheryteMaskAt( uint8_t index ) const; + /*! return a pointer to the aetheryte unlock bitmask array */ + uint8_t * getAetheryteArray(); + /*! set homepoint */ + void setHomepoint( uint8_t aetheryteId ); + /*! get homepoint */ + uint8_t getHomepoint() const; + /*! discover subarea subid fo map map_id, also send udpate packet */ + void discover( int16_t map_id, int16_t sub_id ); + /*! return a pointer to the discovery bitmask array */ + uint8_t * getDiscoveryBitmask(); + /*! helper/debug function to reset all discovered areas */ + void resetDiscovery(); + /*! get a pointer to the howto bitmask array */ + uint8_t * getHowToArray(); + /*! get a const pointer to the howto bitmask array */ + const uint8_t * getHowToArray() const; + /*! update bitmask for how-to's seen */ + void updateHowtosSeen( uint32_t howToId ); + /*! learn an action / update the unlock bitmask. */ + void learnAction( uint8_t actionId ); + /*! check if an action is already unlocked in the bitmask. */ + bool isActionLearned( uint8_t actionId ) const; + /*! return a const pointer to the unlock bitmask array */ + const uint8_t * getUnlockBitmask() const; + + + // Spawn handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! initialize the spawnId queue */ + void initSpawnIdQueue(); + /*! get the spawn id mapped to a specific actorId */ + uint8_t getSpawnIdForActorId( uint32_t actorId ); + /*! assigns the given spawnId to the actor */ + void assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ); + /*! frees the spawnId assigned to the given actor */ + void freePlayerSpawnId( uint32_t actorId ); + /*! send spawn packets to pTarget */ + void spawn( PlayerPtr pTarget ) override; + /*! send despawn packets to pTarget */ + void despawn( ActorPtr pTarget ) override; + + // Player State Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /* return a const pointer to the state flag array */ + const uint8_t* getStateFlags() const; + /* set a specified state flag */ + void setStateFlag( Common::PlayerStateFlag flag ); + /* check if a specified flag is set */ + bool hasStateFlag( Common::PlayerStateFlag flag ) const; + /* reset a specified flag */ + void unsetStateFlag( Common::PlayerStateFlag flag ); + /* helper function, send an empty state flag update */ + void unlock(); + + // Player Session Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! return the userlevel */ + uint8_t getUserLevel() const; + /*! set timestamp for last received ping */ + void setLastPing( uint32_t ping ); + /*! get timestamp of last received ping */ + uint32_t getLastPing() const; + + // Player Database Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! generate the update sql based on update flags */ + void createUpdateSql(); + /*! set an update flag */ + void setSyncFlag( uint32_t updateFlag ); + /*! load player from db, by id */ + bool load( uint32_t charId, SessionPtr pSession ); + /*! load active class data */ + bool loadClassData(); + /*! load search info */ + bool loadSearchInfo(); + + // Player Network Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + /*! send current models ( equipment ) */ + void sendModel(); + /*! send active state flags */ + void sendStateFlags(); + /*! send status update */ + void sendStatusUpdate( bool toSelf = true ) override; + /*! send the entire inventory sequence */ + void sendInventory() const; + /*! send active quest list */ + void sendQuestInfo(); + /*! send a quest specific message */ + void sendQuestMessage( uint32_t questId, int8_t msgId, uint8_t type, uint32_t var1, uint32_t var2 ); + /*! queue a packet for the player */ + void queuePacket( Network::Packets::GamePacketPtr pPacket ); + /*! returns true if loading is complete ( 0x69 has been received ) */ + bool isLoadingComplete() const; + /*! set the loading complete bool */ + void setLoadingComplete( bool bComplete ); + /*! mark this player for zoning, notify worldserver */ + void performZoning(uint16_t zoneId, const Common::FFXIVARR_POSITION3& pos, float rotation); + /*! return true if the player is marked for zoning */ + bool isMarkedForZoning() const; + + Common::ZoneingType getZoningType() const; + void setZoningType( Common::ZoneingType zoneingType ); + + void setSearchInfo( uint8_t selectRegion, uint8_t selectClass, const char* searchMessage ); + const char* getSearchMessage() const; + uint8_t getSearchSelectRegion() const; + uint8_t getSearchSelectClass() const; + + void sendNotice( const std::string& message ); + void sendUrgent( const std::string& message ); + void sendDebug( const std::string& message ); + + void onMobAggro( BattleNpcPtr pBNpc ); + void onMobDeaggro( BattleNpcPtr pBNpc ); + + void initHateSlotQueue(); + void hateListAdd( BattleNpcPtr pBNpc ); + void hateListRemove( BattleNpcPtr pBNpc ); + + bool hateListHasMob( BattleNpcPtr pBNpc ); + + void sendHateList(); + + bool isLogin() const; + void setIsLogin( bool bIsLogin ); + + uint16_t getZoneId() const; + + uint8_t getMode() const; + void setMode( uint8_t mode ); + + void setAutoattack( bool mode ); + bool isAutoattackOn() const; + +private: + uint32_t m_lastWrite; + uint32_t m_lastPing; + + bool m_bIsLogin; + + // ==== CHARABASE + uint64_t m_contentId; // This id will be the name of the folder for character settings in "My Games" + + uint8_t m_mode; + +private: + uint8_t m_mount; + uint8_t m_ignore; + uint8_t m_invincibleGM; + uint8_t m_voice; + + uint64_t m_modelMainWeapon; + uint64_t m_modelSubWeapon; + uint64_t m_modelSystemWeapon; + + uint32_t m_modelEquip[10]; + + uint8_t m_emoteModeType; + + // timestamp of first login + uint32_t m_firstLogin; + + // id of initial language + uint32_t m_language; + + bool m_bNewGame; + + uint32_t m_primaryTerritoryType; + uint32_t m_primaryTerritoryId; + uint32_t m_primaryLayoutId; + uint32_t m_primaryExclusiveId; + uint32_t m_primaryMoveType; + uint32_t m_primaryContentId; + + uint32_t m_secondaryTerritoryType; + uint32_t m_secondaryTerritoryId; + Common::FFXIVARR_POSITION3 m_secondaryPos; + float m_secondaryRot; + uint32_t m_secondaryLayoutId; + // !! END CHARABASE + + // ==== CHARADETAIL + uint8_t m_guardianDeity; + uint8_t m_birthDay; + uint8_t m_birthMonth; + + struct RetainerInfo + { + uint32_t retainerId; + char retainerName[32]; + uint32_t createUnixTime; + bool isActive; + bool isRename; + uint8_t status; + } m_retainerInfo[8]; + + uint8_t m_titleList[32]; + uint8_t m_achievement[16]; + uint8_t m_howTo[33]; + uint8_t m_homePoint; + uint8_t m_startTown; + uint8_t m_favoritePoint[3]; + uint16_t m_townWarpFstFlags; + uint8_t m_questCompleteFlags[200]; + uint8_t m_chocoboTaxiStandFlags[8]; + uint8_t m_discovery[420]; + uint32_t m_playTime; + + uint16_t m_classArray[25]; + uint32_t m_expArray[25]; + uint8_t m_aetheryte[16]; + uint8_t m_unlocks[64]; + + InventoryPtr m_pInventory; + std::map< uint32_t, Event::EventPtr > m_eventMap; + std::map< uint32_t, uint8_t > m_playerIdToSpawnIdMap; // maps player to spawn id + std::queue< uint8_t > m_freeSpawnIdQueue; // queue with spawn ids free to be assigned + std::queue< uint8_t > m_freeHateSlotQueue; // queue with "hate slots" free to be assigned + std::map< uint32_t, uint8_t > m_actorIdTohateSlotMap; + std::map< uint32_t, uint16_t > m_questIdToQuestIdx; // quest mapping, quest id to quest container index + std::map< uint16_t, uint32_t > m_questIdxToQuestId; // quest mapping, quest container index to questId + std::queue< uint8_t > m_freeQuestIdxQueue; // queue with quest indices free to be assigned + boost::shared_ptr< Common::QuestActive > m_activeQuests[30]; + int16_t m_questTracking[5]; + uint8_t m_stateFlags[7]; + uint8_t m_userLevel; + uint16_t zoneId; + bool m_bFirstLogin; + bool m_bInCombat; + bool m_bLoadingComplete; + bool m_bAutoattack; + + Common::ZoneingType m_zoningType; + + bool m_bMarkedForZoning; + uint32_t m_updateFlags; + bool m_bNewAdventurer; + uint64_t m_onlineStatus; + boost::shared_ptr< QueuedZoning > m_queuedZoneing; + + // search info + char m_searchMessage[193]; // searchmessage to show in profile + uint8_t m_searchSelectRegion; // regions selected to show up in profile + uint8_t m_searchSelectClass; // class selected to show up in profile + + // gc info + uint8_t m_gc; + uint8_t m_gcRank[3]; + +}; + +} +} + +#endif diff --git a/src/servers/Server_Zone/PlayerEvent.cpp b/src/servers/Server_Zone/PlayerEvent.cpp new file mode 100644 index 00000000..958cbc7e --- /dev/null +++ b/src/servers/Server_Zone/PlayerEvent.cpp @@ -0,0 +1,312 @@ +#include +#include +#include +#include + +#include "Player.h" + +#include "Zone.h" + +#include "Forwards.h" + +#include "GameConnection.h" +#include "ActorControlPacket142.h" +#include "InitUIPacket.h" +#include "ServerNoticePacket.h" +#include "EventStartPacket.h" +#include "EventPlayPacket.h" +#include "EventFinishPacket.h" + +#include "Event.h" + +extern Core::Logger g_log; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +void Core::Entity::Player::addEvent( Event::EventPtr pEvent ) +{ + m_eventMap[pEvent->getId()] = pEvent; +} + +std::map< uint32_t, Core::Event::EventPtr >& Core::Entity::Player::eventList() +{ + return m_eventMap; +} + +Core::Event::EventPtr Core::Entity::Player::getEvent( uint32_t eventId ) +{ + auto it = m_eventMap.find( eventId ); + if( it != m_eventMap.end() ) + return it->second; + + return Event::EventPtr( nullptr ); +} + +size_t Core::Entity::Player::getEventCount() +{ + return m_eventMap.size(); +} + +void Core::Entity::Player::removeEvent( uint32_t eventId ) +{ + auto it = m_eventMap.find( eventId ); + if( it != m_eventMap.end() ) + { + auto tmpEvent = it->second; + m_eventMap.erase( it ); + } +} + +void Core::Entity::Player::checkEvent( uint32_t eventId ) +{ + auto pEvent = getEvent( eventId ); + if( pEvent ) + if( !pEvent->hasPlayedScene() ) + eventFinish( eventId, 1 ); +} + + +void Core::Entity::Player::eventStart( uint64_t actorId, uint32_t eventId, + uint8_t eventType, uint8_t eventParam1, + uint32_t eventParam2 ) +{ + Event::EventPtr newEvent( new Event::Event( actorId, eventId, eventType, eventParam1, eventParam2 ) ); + addEvent( newEvent ); + + setStateFlag( PlayerStateFlag::Occupied2 ); + sendStateFlags(); + + EventStartPacket eventStart( getId(), actorId, eventId, eventType, eventParam1, eventParam2 ); + + queuePacket( eventStart ); + +} + +void Core::Entity::Player::eventPlay( uint32_t eventId, uint32_t scene, + uint32_t flags, uint32_t eventParam2, + uint32_t eventParam3 ) +{ + eventPlay( eventId, scene, flags, eventParam2, eventParam3, nullptr ); +} + +void Core::Entity::Player::eventPlay( uint32_t eventId, uint32_t scene, + uint32_t flags, Scripting::EventReturnCallback eventCallback ) +{ + eventPlay( eventId, scene, flags, 0, 0, eventCallback ); +} + +void Core::Entity::Player::eventPlay( uint32_t eventId, uint32_t scene, uint32_t flags ) +{ + eventPlay( eventId, scene, flags, 0, 0, nullptr ); +} + +void Core::Entity::Player::eventPlay( uint32_t eventId, uint32_t scene, + uint32_t flags, uint32_t eventParam2, + uint32_t eventParam3, Scripting::EventReturnCallback eventCallback ) +{ + if( flags & 0x02 ) + { + setStateFlag( PlayerStateFlag::WatchingCutscene ); + sendToInRangeSet( ActorControlPacket142( getId(), SetStatusIcon, + static_cast< uint8_t >( getOnlineStatus() ) ), true ); + } + + auto pEvent = getEvent( eventId ); + if( !pEvent && getEventCount() ) + { + // We're trying to play a nested event, need to start it first. + eventStart( getId(), eventId, Event::Event::Nest, 0, 0 ); + pEvent = getEvent( eventId ); + } + else if( !pEvent ) + { + g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" ); + return; + } + + pEvent->setPlayedScene( true ); + pEvent->setEventReturnCallback( eventCallback ); + EventPlayPacket eventPlay( getId(), pEvent->getActorId(), pEvent->getId(), + scene, flags, eventParam2, eventParam3 ); + + queuePacket( eventPlay ); +} + +void Core::Entity::Player::eventPlay( uint32_t eventId, uint32_t scene, + uint32_t flags, uint32_t eventParam2, + uint32_t eventParam3, uint32_t eventParam4, Scripting::EventReturnCallback eventCallback ) +{ + if( flags & 0x02 ) + { + setStateFlag( PlayerStateFlag::WatchingCutscene ); + sendToInRangeSet( ActorControlPacket142( getId(), SetStatusIcon, + static_cast< uint8_t >( getOnlineStatus() ) ), true ); + } + + auto pEvent = getEvent( eventId ); + if( !pEvent && getEventCount() ) + { + // We're trying to play a nested event, need to start it first. + eventStart( getId(), eventId, Event::Event::Nest, 0, 0 ); + pEvent = getEvent( eventId ); + } + else if( !pEvent ) + { + g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" ); + return; + } + + pEvent->setPlayedScene( true ); + pEvent->setEventReturnCallback( eventCallback ); + EventPlayPacket eventPlay( getId(), pEvent->getActorId(), pEvent->getId(), + scene, flags, eventParam2, eventParam3, eventParam4 ); + + queuePacket( eventPlay ); +} + +void Core::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlayer ) +{ + auto pEvent = getEvent( eventId ); + + if( !pEvent ) + { + g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" ); + return; + } + + if( getEventCount() > 1 && pEvent->getEventType() != Event::Event::Nest ) + { + // this is the parent of a nested event, we can't finish it until the parent finishes + return; + } + + switch( pEvent->getEventType() ) + { + case Event::Event::Nest: + { + queuePacket( EventFinishPacket( getId(), pEvent->getId(), pEvent->getEventType(), pEvent->getEventParam3() ) ); + removeEvent( pEvent->getId() ); + + auto events = eventList(); + + for( auto it : events ) + { + + if( it.second->hasPlayedScene() == false ) + { + // TODO: not happy with this, this is also prone to break wit more than one remaining event in there + queuePacket( EventFinishPacket( getId(), it.second->getId(), it.second->getEventType(), it.second->getEventParam3() ) ); + removeEvent( it.second->getId() ); + } + } + + break; + } + default: + { + queuePacket( EventFinishPacket( getId(), pEvent->getId(), pEvent->getEventType(), pEvent->getEventParam3() ) ); + break; + } + } + + if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) ) + { + unsetStateFlag( PlayerStateFlag::WatchingCutscene ); + sendToInRangeSet( ActorControlPacket142( getId(), SetStatusIcon, + static_cast< uint8_t >( getOnlineStatus() ) ), true ); + } + + removeEvent( pEvent->getId() ); + + if( freePlayer == 1 ) + { + unsetStateFlag( PlayerStateFlag::Occupied2 ); + sendStateFlags(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Core::Entity::Player::onLogin() +{ + + // TODO: Replace this with MoTD from config + sendNotice( "<<>>\nCompiled: " __DATE__ " " __TIME__ "\n" + "Mind you, character data is going to be wiped regulary!" ); +} + +void Core::Entity::Player::onZoneStart() +{ + +} + +void Core::Entity::Player::onZoneDone() +{ + +} + +void Core::Entity::Player::onDeath() +{ + +} + + +// TODO: slightly ugly here and way too static. Needs too be done properly +void Core::Entity::Player::onTick() +{ + + bool sendUpdate = false; + + if( !isAlive() || !isLoadingComplete() ) + return; + + int32_t addHp = getMaxHp() * 0.1f + 1; + int32_t addMp = 8 * 0.1f + 1; + + if( !m_actorIdTohateSlotMap.empty() ) + { + addHp = getMaxHp() * 0.01f + 1; + addMp = getMaxHp() * 0.01f + 1; + } + + if( m_hp < getMaxHp() ) + { + + if( m_hp + addHp < getMaxHp() ) + m_hp += addHp; + else + m_hp = getMaxHp(); + + sendUpdate = true; + } + + if( m_mp < getMaxMp() ) + { + + if( m_mp + addMp < getMaxMp() ) + m_mp += addMp; + else + m_mp = getMaxMp(); + + sendUpdate = true; + } + + if( m_tp < 1000 ) + { + int32_t addTp = 100; + if( m_tp + addTp < 1000 ) + m_tp += addTp; + else + m_tp = 1000; + + sendUpdate = true; + } + + if( sendUpdate ) + { + sendStatusUpdate(); + setSyncFlag( PlayerSyncFlags::Status ); + } +} diff --git a/src/servers/Server_Zone/PlayerInventory.cpp b/src/servers/Server_Zone/PlayerInventory.cpp new file mode 100644 index 00000000..05802442 --- /dev/null +++ b/src/servers/Server_Zone/PlayerInventory.cpp @@ -0,0 +1,217 @@ +#include + +#include "Player.h" + +#include "ZoneMgr.h" +#include "Zone.h" + +#include + +#include "ActorControlPacket143.h" + +#include "Inventory.h" +#include "Item.h" + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::InventoryPtr Core::Entity::Player::getInvetory() const +{ + return m_pInventory; +} + +// TODO: This has to be redone and simplified +void Core::Entity::Player::equipWeapon( Core::ItemPtr pItem ) +{ + ClassJob currentClass = static_cast< ClassJob >( getClass() ); + + switch( pItem->getCategory() ) + { + case ItemCategory::PugWep: + if( currentClass != ClassJob::CLASS_PUGILIST && + currentClass != ClassJob::JOB_MONK ) + setClassJob( ClassJob::CLASS_PUGILIST ); + break; + case ItemCategory::GlaWep: + if( currentClass != ClassJob::CLASS_GLADIATOR && + currentClass != ClassJob::JOB_KNIGHT ) + setClassJob( ClassJob::CLASS_GLADIATOR ); + break; + case ItemCategory::MrdWep: + if( currentClass != ClassJob::CLASS_MARAUDER && + currentClass != ClassJob::JOB_WARRIOR ) + setClassJob( ClassJob::CLASS_MARAUDER ); + break; + case ItemCategory::ArcWep: + if( currentClass != ClassJob::CLASS_ARCHER && + currentClass != ClassJob::JOB_BARD ) + setClassJob( ClassJob::CLASS_ARCHER ); + break; + case ItemCategory::LncWep: + if( currentClass != ClassJob::CLASS_LANCER && + currentClass != ClassJob::JOB_DRAGON ) + setClassJob( ClassJob::CLASS_LANCER ); + break; + case ItemCategory::ThmWep: + case ItemCategory::Thm2Wep: + if( currentClass != ClassJob::CLASS_THAUMATURGE && + currentClass != ClassJob::JOB_BLACK ) + setClassJob( ClassJob::CLASS_THAUMATURGE ); + break; + case ItemCategory::CnjWep: + case ItemCategory::Cnj2Wep: + if( currentClass != ClassJob::CLASS_CONJURER && + currentClass != ClassJob::JOB_WHITE ) + setClassJob( ClassJob::CLASS_CONJURER ); + break; + case ItemCategory::ArnWep: + if( currentClass != ClassJob::CLASS_ARCANIST && + currentClass != ClassJob::JOB_SUMMONER && + currentClass != ClassJob::JOB_SCHOLAR ) + setClassJob( ClassJob::CLASS_ARCANIST ); + break; + default: + break; + } +} + +// equip an item +void Core::Entity::Player::equipItem( Inventory::EquipSlot equipSlotId, Core::ItemPtr pItem, bool sendModel ) +{ + + // Console->outDebOnly("Equipping into slot %i", equipSlotID); + + uint64_t model = pItem->getModelId1(); + uint64_t model2 = pItem->getModelId2(); + + switch( equipSlotId ) + { + case Inventory::EquipSlot::MainHand: + m_modelMainWeapon = model; + m_modelSubWeapon = model2; + // TODO: add job change upon changing weapon if needed + // equipWeapon( pItem ); + break; + + case Inventory::EquipSlot::OffHand: + m_modelSubWeapon = model; + break; + + case Inventory::EquipSlot::SoulCrystal: + // TODO: add Job change on equipping crystal + // change job + break; + + default: // any other slot + m_modelEquip[static_cast< uint8_t >( equipSlotId )] = static_cast< uint32_t >( model ); + break; + + } + + if( sendModel ) + this->sendModel(); +} + +void Core::Entity::Player::unequipItem( Inventory::EquipSlot equipSlotId, ItemPtr pItem ) +{ + m_modelEquip[static_cast< uint8_t >( equipSlotId )] = 0; + sendModel(); +} + +uint32_t Core::Entity::Player::getCurrency( uint8_t type ) const +{ + return m_pInventory->getCurrency( static_cast< Inventory::CurrencyType >( type ) ); +} + +// TODO: these next functions are so similar that they could likely be simplified +void Core::Entity::Player::addCurrency( uint8_t type, uint32_t amount ) +{ + if( !m_pInventory->addCurrency( static_cast< Inventory::CurrencyType >( type ), amount ) ) + return; + + GamePacketNew< FFXIVIpcUpdateInventorySlot > invUpPacket( getId() ); + invUpPacket.data().containerId = Inventory::InventoryType::Currency; + invUpPacket.data().catalogId = 1; + invUpPacket.data().quantity = m_pInventory->getCurrency( static_cast< Inventory::CurrencyType >( type ) ); + invUpPacket.data().slot = static_cast< uint8_t >( type ) - 1; + + queuePacket( invUpPacket ); +} + +void Core::Entity::Player::removeCurrency( uint8_t type, uint32_t amount ) +{ + if( !m_pInventory->removeCurrency( static_cast< Inventory::CurrencyType >( type ), amount ) ) + return; + + GamePacketNew< FFXIVIpcUpdateInventorySlot > invUpPacket( getId() ); + invUpPacket.data().containerId = Inventory::InventoryType::Currency; + invUpPacket.data().catalogId = 1; + invUpPacket.data().quantity = m_pInventory->getCurrency( static_cast< Inventory::CurrencyType >( type ) ); + invUpPacket.data().slot = static_cast< uint8_t >( type ) - 1; + + queuePacket( invUpPacket ); +} + + +uint32_t Core::Entity::Player::getCrystal( uint8_t type ) const +{ + return m_pInventory->getCrystal( static_cast< Inventory::CrystalType >( type ) ); +} + +void Core::Entity::Player::addCrystal( uint8_t type, uint32_t amount ) +{ + if( !m_pInventory->addCrystal( static_cast< Inventory::CrystalType >( type ), amount ) ) + return; + + GamePacketNew< FFXIVIpcUpdateInventorySlot > invUpPacket( getId() ); + invUpPacket.data().containerId = Inventory::InventoryType::Crystal; + invUpPacket.data().catalogId = static_cast< uint8_t >( type ) + 1; + invUpPacket.data().quantity = m_pInventory->getCrystal( static_cast< Inventory::CrystalType >( type ) ); + invUpPacket.data().slot = static_cast< uint8_t >( type ) - 1; + + queuePacket( invUpPacket ); + + queuePacket( ActorControlPacket143( getId(), ItemObtainIcon, static_cast< uint8_t >( type ) + 1, amount ) ); +} + +void Core::Entity::Player::removeCrystal( uint8_t type, uint32_t amount ) +{ + if( !m_pInventory->removeCrystal( static_cast< Inventory::CrystalType >( type ), amount ) ) + return; + + GamePacketNew< FFXIVIpcUpdateInventorySlot > invUpPacket( getId() ); + invUpPacket.data().containerId = Inventory::InventoryType::Crystal; + invUpPacket.data().catalogId = static_cast< uint8_t >( type ) + 1; + invUpPacket.data().quantity = m_pInventory->getCrystal( static_cast< Inventory::CrystalType >( type ) ); + invUpPacket.data().slot = static_cast< uint8_t >( type ) - 1; + + queuePacket( invUpPacket ); +} + +bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint16_t quantity ) +{ + + for( uint8_t i = 0; i < 4; i++ ) + { + if( m_pInventory->addItem( i, -1, catalogId, quantity ) != -1 ) + { + return true; + } + } + return false; +} + +bool Core::Entity::Player::addItem( uint16_t containerId, uint16_t catalogId, uint16_t quantity ) +{ + if( m_pInventory->addItem( containerId, -1, catalogId, quantity ) != -1 ) + return true; + + return false; +} + +void Core::Entity::Player::sendInventory() const +{ + m_pInventory->send(); +} + diff --git a/src/servers/Server_Zone/PlayerQuest.cpp b/src/servers/Server_Zone/PlayerQuest.cpp new file mode 100644 index 00000000..89e1959d --- /dev/null +++ b/src/servers/Server_Zone/PlayerQuest.cpp @@ -0,0 +1,1185 @@ +#include +#include +#include +#include +#include +#include + +#include "GameConnection.h" + +#include "QuestMessagePacket.h" + +#include "Session.h" +#include "Player.h" +#include "Inventory.h" + + + +extern Core::Db::Database g_database; +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +bool Core::Entity::Player::loadActiveQuests() +{ + + auto pQR = g_database.query( "SELECT * FROM charaquest WHERE CharacterId = " + std::to_string( m_id ) + ";" ); + + if( !pQR ) + return false; + + Db::Field* field = pQR->fetch(); + + for( uint8_t i = 0; i < 30; i++ ) + { + + uint16_t index = i * 10; + + //g_log.debug( " QUEST_ID: " + std::to_string( field[index].getInt16() ) + " INDEX: " + std::to_string( index ) ); + + if( field[index].getInt16() != 0 ) + { + + boost::shared_ptr pActiveQuest( new QuestActive() ); + pActiveQuest->c.questId = field[index].getInt16(); + pActiveQuest->c.sequence = field[index + 1].getUInt8(); + pActiveQuest->c.flags = field[index + 2].getUInt8(); + pActiveQuest->c.UI8A = field[index + 3].getUInt8(); + pActiveQuest->c.UI8B = field[index + 4].getUInt8(); + pActiveQuest->c.UI8C = field[index + 5].getUInt8(); + pActiveQuest->c.UI8D = field[index + 6].getUInt8(); + pActiveQuest->c.UI8E = field[index + 7].getUInt8(); + pActiveQuest->c.UI8F = field[index + 8].getUInt8(); + pActiveQuest->c.padding1 = field[index + 9].getUInt8(); + m_activeQuests[i] = pActiveQuest; + + m_questIdToQuestIdx[pActiveQuest->c.questId] = i; + m_questIdxToQuestId[i] = pActiveQuest->c.questId; + + } + else + { + m_activeQuests[i] = nullptr; + m_freeQuestIdxQueue.push( i ); + } + + } + + return true; + +} + +void Core::Entity::Player::finishQuest( uint16_t questId ) +{ + + int8_t idx = getQuestIndex( questId ); + + if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) ) + { + GamePacketNew< FFXIVIpcQuestUpdate > questUpdatePacket( getId() ); + questUpdatePacket.data().slot = idx; + questUpdatePacket.data().questInfo.c.questId = 0; + questUpdatePacket.data().questInfo.c.sequence = 0xFF; + queuePacket( questUpdatePacket ); + + GamePacketNew< FFXIVIpcQuestFinish > questFinishPacket( getId() ); + questFinishPacket.data().questId = questId; + questFinishPacket.data().flag1 = 1; + questFinishPacket.data().flag2 = 1; + queuePacket( questFinishPacket ); + + updateQuestsCompleted( questId ); + setSyncFlag( PlayerSyncFlags::Quests ); + setSyncFlag( PlayerSyncFlags::QuestTracker ); + + for( int ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] == idx ) + m_questTracking[ii] = -1; + } + + boost::shared_ptr pQuest = m_activeQuests[idx]; + m_activeQuests[idx].reset(); + + m_freeQuestIdxQueue.push( idx ); + m_questIdToQuestIdx.erase( questId ); + m_questIdxToQuestId.erase( idx ); + } + + + sendQuestTracker(); + +} + +void Core::Entity::Player::unfinishQuest( uint16_t questId ) +{ + removeQuestsCompleted( questId ); + setSyncFlag( PlayerSyncFlags::Quests ); + sendQuestInfo(); +} + +void Core::Entity::Player::removeQuest( uint16_t questId ) +{ + + int8_t idx = getQuestIndex( questId ); + + if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) ) + { + + GamePacketNew< FFXIVIpcQuestUpdate > questUpdatePacket( getId() ); + questUpdatePacket.data().slot = idx; + questUpdatePacket.data().questInfo.c.questId = 0; + questUpdatePacket.data().questInfo.c.sequence = 0xFF; + queuePacket( questUpdatePacket ); + + GamePacketNew< FFXIVIpcQuestFinish > questFinishPacket( getId() ); + questFinishPacket.data().questId = questId; + questFinishPacket.data().flag1 = 1; + questFinishPacket.data().flag2 = 1; + queuePacket( questFinishPacket ); + + setSyncFlag( PlayerSyncFlags::Quests ); + setSyncFlag( PlayerSyncFlags::QuestTracker ); + + for( int ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] == idx ) + m_questTracking[ii] = -1; + } + + boost::shared_ptr pQuest = m_activeQuests[idx]; + m_activeQuests[idx].reset(); + + m_freeQuestIdxQueue.push( idx ); + m_questIdToQuestIdx.erase( questId ); + m_questIdxToQuestId.erase( idx ); + } + + sendQuestTracker(); + +} + +bool Core::Entity::Player::hasQuest( uint16_t questId ) +{ + return ( getQuestIndex( questId ) > -1 ); +} + +int16_t Core::Entity::Player::getQuestIndex( uint16_t questId ) +{ + auto it = m_questIdToQuestIdx.find( questId ); + if( it != m_questIdToQuestIdx.end() ) + return it->second; + + return -1; +} + +uint8_t Core::Entity::Player::getQuestBitFlag8( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag8; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestBitFlag16( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag16; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestBitFlag24( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag24; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestBitFlag32( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag32; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestBitFlag40( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag40; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestBitFlag48( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->a.BitFlag48; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8A( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8A; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8B( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8B; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8C( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8C; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8D( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8D; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8E( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8E; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8F( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->c.UI8F; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8AH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8AH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8BH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8BH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8CH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8CH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8DH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8DH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8EH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8EH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8FH( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8FH; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8AL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8AL; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8BL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8BL; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8CL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8CL; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8DL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8DL; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8EL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8EL; + } + + return value; +} + +uint8_t Core::Entity::Player::getQuestUI8FL( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint8_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + value = pNewQuest->b.UI8FL; + } + + return value; +} + +uint16_t Core::Entity::Player::getQuestUI16A( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint16_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + // value = pNewQuest->d.UI16A; + } + + return value; +} + +uint16_t Core::Entity::Player::getQuestUI16B( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint16_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + // value = pNewQuest->d.UI16B; + } + + return value; +} + +uint16_t Core::Entity::Player::getQuestUI16C( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint16_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + // value = pNewQuest->d.UI16C; + } + + return value; +} + +uint32_t Core::Entity::Player::getQuestUI32A( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + uint32_t value = 0; + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + // value = pNewQuest->e.UI32A; + } + + return value; +} + +void Core::Entity::Player::setQuestUI8A( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8A = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8B( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8B = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8C( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8C = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8D( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8D = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8E( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8E = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8F( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->c.UI8F = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8AH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8AH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8BH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8BH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8CH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8CH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8DH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8DH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8EH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8EH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8FH( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8FH = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8AL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8AL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8BL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8BL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8CL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8CL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8DL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8DL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8EL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8EL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI8FL( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->b.UI8FL = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI16A( uint16_t questId, uint16_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + // pNewQuest->d.UI16A = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI16B( uint16_t questId, uint16_t val ) + +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + // pNewQuest->d.UI16B = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI16C( uint16_t questId, uint16_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + +// pNewQuest->d.UI16C = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestUI32A( uint16_t questId, uint32_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + // pNewQuest->e.UI32A = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestBitFlag8( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag8 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestBitFlag16( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag16 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestBitFlag24( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag24 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} +void Core::Entity::Player::setQuestBitFlag32( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag32 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestBitFlag40( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag40 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + +void Core::Entity::Player::setQuestBitFlag48( uint16_t questId, uint8_t val ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + + pNewQuest->a.BitFlag48 = val; + + updateQuest( questId, pNewQuest->c.sequence ); + setSyncFlag( PlayerSyncFlags::Quests ); + } +} + + + +uint8_t Core::Entity::Player::getQuestSeq( uint16_t questId ) +{ + int16_t idx = getQuestIndex( questId ); + + if( idx != -1 ) + { + boost::shared_ptr< QuestActive > pNewQuest = m_activeQuests[idx]; + return pNewQuest->c.sequence; + } + return 0; +} + +void Core::Entity::Player::updateQuest( uint16_t questId, uint16_t sequence ) +{ + if( hasQuest( questId ) ) + { + GamePacketNew< FFXIVIpcQuestUpdate > pe_qa( getId() ); + int16_t index = getQuestIndex( questId ); + auto pNewQuest = m_activeQuests[index]; + pe_qa.data().slot = index; + pNewQuest->c.sequence = sequence; + pe_qa.data().questInfo = *pNewQuest; + queuePacket( pe_qa ); + + setSyncFlag( PlayerSyncFlags::Quests ); + + } + else + { + + int8_t idx = m_freeQuestIdxQueue.front(); + m_freeQuestIdxQueue.pop(); + + GamePacketNew< FFXIVIpcQuestUpdate > pe_qa( getId() ); + + boost::shared_ptr< QuestActive > pNewQuest( new QuestActive() ); + pNewQuest->c.questId = questId; + pNewQuest->c.sequence = sequence; + pNewQuest->c.padding = 0; + m_activeQuests[idx] = pNewQuest; + + m_questIdToQuestIdx[questId] = idx; + m_questIdxToQuestId[idx] = questId; + + pe_qa.data().slot = idx; + pNewQuest->c.sequence = sequence; + pe_qa.data().questInfo = *pNewQuest; + + + queuePacket( pe_qa ); + + setSyncFlag( PlayerSyncFlags::Quests ); + setSyncFlag( PlayerSyncFlags::QuestTracker ); + + for( int ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] == -1 ) + { + m_questTracking[ii] = idx; + break; + } + } + + sendQuestTracker(); + + } +} + +void Core::Entity::Player::sendQuestTracker() +{ + GamePacketNew< FFXIVIpcQuestTracker > trackerPacket( getId() ); + + for( int ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] >= 0 ) + { + trackerPacket.data().entry[ii].active = 1; + trackerPacket.data().entry[ii].questIndex = m_questTracking[ii]; + } + } + queuePacket( trackerPacket ); +} + +void Core::Entity::Player::setQuestTracker( uint16_t index, int16_t flag ) +{ + if( flag == 0 ) + { + //remove + for( uint8_t ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] == index ) + { + m_questTracking[ii] = -1; + break; + } + } + } + else + { + //add + for( uint8_t ii = 0; ii < 5; ii++ ) + { + if( m_questTracking[ii] == -1 ) + { + m_questTracking[ii] = index; + break; + } + } + } + + setSyncFlag( PlayerSyncFlags::QuestTracker ); + +} + + +void Core::Entity::Player::sendQuestInfo() +{ + GamePacketNew< FFXIVIpcQuestActiveList > pe_qa( getId() ); + + for( int i = 0; i < 30; i++ ) + { + uint8_t offset = i * 12; + if( m_activeQuests[i] != nullptr ) + { + + auto& quest = pe_qa.data().activeQuests[i]; + quest = *m_activeQuests[i]; + + } + } + + queuePacket( pe_qa ); + + GamePacketNew< FFXIVIpcQuestCompleteList > pe_qc( getId() ); + memcpy( pe_qc.data().questCompleteMask, m_questCompleteFlags, 200 ); + queuePacket( pe_qc ); + + sendQuestTracker(); +} + +void Core::Entity::Player::sendQuestMessage( uint32_t questId, int8_t msgId, uint8_t type, uint32_t var1, uint32_t var2 ) +{ + queuePacket( QuestMessagePacket( getAsPlayer(), questId, msgId, type, var1, var2 ) ); +} + + +void Core::Entity::Player::updateQuestsCompleted( uint32_t questId ) +{ + uint8_t index = questId / 8; + uint8_t bitIndex = ( questId ) % 8; + + uint8_t value = 0x80 >> bitIndex; + + m_questCompleteFlags[index] |= value; + + setSyncFlag( PlayerSyncFlags::Quests ); +} + +void Core::Entity::Player::removeQuestsCompleted( uint32_t questId ) +{ + uint8_t index = questId / 8; + uint8_t bitIndex = ( questId ) % 8; + + uint8_t value = 0x80 >> bitIndex; + + m_questCompleteFlags[index] ^= value; + + setSyncFlag( PlayerSyncFlags::Quests ); +} + +bool Core::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optionalChoice ) +{ + uint32_t playerLevel = getLevel(); + auto questInfo = g_exdData.getQuestInfo( questId ); + + + if( !questInfo ) + return false; + + auto paramGrowth = g_exdData.m_paramGrowthInfoMap[questInfo->quest_level]; + + uint32_t exp = ( questInfo->reward_exp_factor * paramGrowth.quest_exp_mod * ( 45 + 5 * questInfo->quest_level) ) / 100; + exp = exp + ( questInfo->reward_exp_factor / 100 ) * 10000; + + exp = questInfo->reward_exp_factor; + + uint16_t rewardItemCount = questInfo->reward_item.size(); + uint16_t optionalItemCount = questInfo->reward_item_optional.size() > 0 ? 1 : 0; + + uint32_t gilReward = questInfo->reward_gil; + + // TODO: check if there is room in inventory, else return false; + if( exp > 0 ) + gainExp( exp ); + + if( rewardItemCount > 0 ) + { + for( uint32_t i = 0; i < questInfo->reward_item.size(); i++ ) + { + // TODO: add the correct amount of items instead of 1 + addItem( -1, questInfo->reward_item.at( i ), questInfo->reward_item_count.at( i ) ); + } + } + + if( optionalItemCount > 0 ) + { + auto itemId = questInfo->reward_item_optional.at( optionalChoice ); + // TODO: add the correct amount of items instead of 1 + addItem( -1, itemId, questInfo->reward_item_optional_count.at( optionalChoice ) ); + } + + if( gilReward > 0 ) + addCurrency( 1, gilReward ); + + return true; +} + +boost::shared_ptr Core::Entity::Player::getQuestActive( uint16_t index ) +{ + return m_activeQuests[index]; +} + diff --git a/src/servers/Server_Zone/PlayerSpawnPacket.h b/src/servers/Server_Zone/PlayerSpawnPacket.h new file mode 100644 index 00000000..0353502e --- /dev/null +++ b/src/servers/Server_Zone/PlayerSpawnPacket.h @@ -0,0 +1,101 @@ +#ifndef _PLAYERSPAWN_H +#define _PLAYERSPAWN_H + +#include +#include +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { + namespace Network { + namespace Packets { + namespace Server { + + /** + * @brief The packet sent to finish an event. + */ + class PlayerSpawnPacket : + public GamePacketNew + { + public: + PlayerSpawnPacket( Entity::PlayerPtr pPlayer, Entity::PlayerPtr pTarget ) : + GamePacketNew( pPlayer->getId(), pTarget->getId() ) + { + initialize( pPlayer, pTarget ); + }; + + private: + void initialize( Entity::PlayerPtr pPlayer, Entity::PlayerPtr pTarget ) + { + // todo: figure out unkown offsets + // TODO: temporary gm rank + //m_data.gmRank = 0xff; + + + m_data.currentMount = 0; + m_data.classJob = pPlayer->getClass(); + //m_data.status = static_cast< uint8_t >( pPlayer->getStatus() ); + m_data.hPCurr = pPlayer->getHp(); + m_data.mPCurr = pPlayer->getMp(); + m_data.tPCurr = pPlayer->getTp(); + m_data.hPMax = pPlayer->getMaxHp(); + m_data.mPMax = pPlayer->getMaxMp(); + m_data.gmRank = 0xff; + //m_data.tPMax = 3000; + m_data.level = pPlayer->getLevel(); + memcpy( m_data.look, pPlayer->getLookArray(), 26 ); + m_data.mainWeaponModel = pPlayer->getModelMainWeapon(); + m_data.secWeaponModel = pPlayer->getModelSubWeapon(); + m_data.models[0] = pPlayer->getModelForSlot( Inventory::EquipSlot::Head ); + m_data.models[1] = pPlayer->getModelForSlot( Inventory::EquipSlot::Body ); + m_data.models[2] = pPlayer->getModelForSlot( Inventory::EquipSlot::Hands ); + m_data.models[3] = pPlayer->getModelForSlot( Inventory::EquipSlot::Legs ); + m_data.models[4] = pPlayer->getModelForSlot( Inventory::EquipSlot::Feet ); + strcpy( m_data.name, pPlayer->getName().c_str() ); + m_data.pos.x = pPlayer->getPos().x; + m_data.pos.y = pPlayer->getPos().y; + m_data.pos.z = pPlayer->getPos().z; + m_data.voice = pPlayer->getVoiceId(); + + m_data.rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + + m_data.onlineStatus = static_cast< uint8_t >( pPlayer->getOnlineStatus() ); + + //m_data.u23 = 0x04; + //m_data.u24 = 256; + m_data.state = 1; + m_data.type = 1; + if( pTarget == pPlayer ) + { + m_data.spawnIndex = 0x00; + } + else + { + m_data.spawnIndex = pTarget->getSpawnIdForActorId( pPlayer->getId() ); + } + // 0x20 == spawn hidden to be displayed by the spawneffect control + m_data.displayFlags = pPlayer->getStance(); + + if( pPlayer->getZoningType() != Common::ZoneingType::None ) + { + m_data.displayFlags |= 0x20; + } + + m_data.targetId = pPlayer->getTargetId(); + //m_data.type = 1; + //m_data.unknown_33 = 4; + //m_data.unknown_38 = 0x70; + //m_data.unknown_60 = 3; + //m_data.unknown_61 = 7; + + + }; + }; + + } + } + } +} + +#endif /*_PlayerSpawn_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/PlayerSql.cpp b/src/servers/Server_Zone/PlayerSql.cpp new file mode 100644 index 00000000..83ab972b --- /dev/null +++ b/src/servers/Server_Zone/PlayerSql.cpp @@ -0,0 +1,437 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "Player.h" + +#include "ZoneMgr.h" +#include "Zone.h" + +#include "ServerZone.h" + +#include "Forwards.h" + +#include "GameConnection.h" +#include "InitUIPacket.h" +#include "StatusEffectContainer.h" +#include "Inventory.h" + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::ServerZone g_serverZone; +extern Core::ZoneMgr g_zoneMgr; +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +// load player from the db +bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) +{ + // TODO: can't help but think that the whole player loading could be handled better... + const std::string char_id_str = std::to_string( charId ); + + auto pQR = g_database.query( "SELECT c.Name, " + "c.PrimaryTerritoryId, " + "c.Hp, " + "c.Mp, " + "c.Gp, " + "c.Mode, " + "c.Pos_0_0, " + "c.Pos_0_1, " + "c.Pos_0_2, " + "c.Pos_0_3, " + "c.FirstLogin, " + "c.Customize, " + "c.ModelMainWeapon, " + "c.ModelSubWeapon, " + "c.ModelEquip, " + "cd.GuardianDeity, " + "cd.BirthDay, " + "cd.BirthMonth, " + "cd.Status, " + "cd.Class, " + "cd.Homepoint, " + "cd.HowTo, " + "c.ContentId, " + "c.Voice, " + "cd.QuestCompleteFlags, " + "cd.QuestTracking, " + "c.IsNewGame, " + "cd.Aetheryte, " + "cd.unlocks, " + "cd.Discovery, " + "cd.StartTown, " + "cd.TotalPlayTime, " + "c.IsNewAdventurer, " + "cd.GrandCompany, " + "cd.GrandCompanyRank " + "FROM charabase AS c " + " INNER JOIN charadetail AS cd " + " ON c.CharacterId = cd.CharacterId " + "WHERE c.CharacterId = " + char_id_str + ";" ); + + if( !pQR ) + { + g_log.error( "Player id " + char_id_str + " does not exist!" ); + return false; + } + + m_updateFlags = PlayerSyncFlags::None; + m_id = charId; + + Db::Field *field = pQR->fetch(); + + strcpy( m_name, field[0].getString() ); + + ZonePtr pCurrZone = g_zoneMgr.getZone( field[1].getInt32() ); + m_zoneId = field[1].getInt32(); + + // see if a valid zone could be found for the character + if( !pCurrZone ) + { + g_log.error( "[" + char_id_str + "] Zone " + std::to_string( field[1].getInt32() ) + "not found!" ); + g_log.error( "[" + char_id_str + "] Setting default zone instead" ); + + // default to new gridania + // TODO: should probably just abort and mark character as corrupt + pCurrZone = g_zoneMgr.getZone( 132 ); + + m_pos.x = 0.0f; + m_pos.y = 0.0f; + m_pos.z = 0.0f; + setRotation( 0.0f ); + } + + m_hp = field[2].getUInt32(); + + m_mp = field[3].getUInt32(); + m_tp = 0; + + m_pos.x = field[6].getFloat(); + m_pos.y = field[7].getFloat(); + m_pos.z = field[8].getFloat(); + setRotation( field[9].getFloat() ); + + m_bFirstLogin = field[10].getBool(); + + field[11].getBinary( reinterpret_cast< char* >( m_customize ), 26 ); + + field[14].getBinary( reinterpret_cast< char* >( m_modelEquip ), 40 ); + + m_guardianDeity = field[15].getUInt8(); + m_birthDay = field[16].getUInt8(); + m_birthMonth = field[17].getUInt8(); + m_status = static_cast< ActorStatus >( field[18].getUInt8() ); + m_class = static_cast< ClassJob >( field[19].getUInt8() ); + m_homePoint = field[20].getUInt8(); + + field[21].getBinary( reinterpret_cast< char* >( m_howTo ), sizeof( m_howTo ) ); + + m_contentId = field[22].getUInt64(); + + m_voice = field[23].getUInt32(); + + field[24].getBinary( reinterpret_cast< char* >( m_questCompleteFlags ), 200 ); + + field[25].getBinary( reinterpret_cast< char* >( m_questTracking ), 10 ); + + m_bNewGame = field[26].getBool(); + + field[27].getBinary( reinterpret_cast< char* >( m_aetheryte ), sizeof( m_aetheryte ) ); + + field[28].getBinary( reinterpret_cast< char* >( m_unlocks ), 64 ); + + field[29].getBinary( reinterpret_cast< char* >( m_discovery ), sizeof( m_discovery ) ); + + m_startTown = field[30].getInt8(); + m_playTime = field[31].getUInt32(); + + m_bNewAdventurer = field[32].getBool(); + + m_gc = field[33].getUInt8(); + field[34].getBinary( reinterpret_cast< char* >( m_gcRank ), 3 ); + + m_pCell = nullptr; + + if( !loadActiveQuests() || !loadClassData() || !loadSearchInfo() ) + g_log.error( "Player id " + char_id_str + " data corrupt!" ); + + m_userLevel = 1; + + m_maxHp = getMaxHp(); + m_maxMp = getMaxMp(); + + m_modelSubWeapon = 0; + m_lastTickTime = 0; + + auto pPlayer = getAsPlayer(); + m_pInventory = InventoryPtr( new Inventory( pPlayer ) ); + + pPlayer->calculateStats(); + + // first login, run the script event + if( m_bNewGame ) + { + //g_scriptMgr.onPlayerFirstEnterWorld( pPlayer ); + m_bNewGame = false; + m_hp = getMaxHp(); + m_mp = getMaxMp(); + setSyncFlag( PlayerSyncFlags::NewGame ); + } + + if( m_hp > getMaxHp() ) + m_hp = getMaxHp(); + + if( m_mp > getMaxMp() ) + m_mp = getMaxMp(); + + if( m_hp == 0 ) + m_status = ActorStatus::Dead; + + if( m_bNewAdventurer ) + setStateFlag( PlayerStateFlag::NewAdventurer ); + + setStateFlag( PlayerStateFlag::BetweenAreas ); + + m_pInventory->load(); + + initHateSlotQueue(); + + initSpawnIdQueue(); + + m_pStatusEffectContainer = StatusEffect::StatusEffectContainerPtr( new StatusEffect::StatusEffectContainer( shared_from_this() ) ); + + if( !m_playerIdToSpawnIdMap.empty() ) + m_playerIdToSpawnIdMap.clear(); + + return true; +} + + +bool Core::Entity::Player::loadClassData() +{ + auto pQR = g_database.query( "SELECT * FROM characlass WHERE CharacterId = " + std::to_string( m_id ) + ";" ); + + if( !pQR ) + return false; + + Db::Field* field = pQR->fetch(); + + for( uint8_t i = 0; i < 25; i++ ) + { + uint8_t index = i * 2; + m_classArray[i] = field[index].getUInt8(); + m_expArray[i] = field[index + 1].getUInt32(); + } + + return true; +} + +bool Core::Entity::Player::loadSearchInfo() +{ + auto pQR = g_database.query( "SELECT * FROM charainfosearch WHERE CharacterId = " + std::to_string( m_id ) + ";" ); + + if( !pQR ) + return false; + + Db::Field* field = pQR->fetch(); + + m_searchSelectClass = field[1].getUInt8(); + m_searchSelectRegion = field[2].getUInt8(); + sprintf( m_searchMessage, field[3].getString() ); + + return true; +} + + +void Core::Entity::Player::createUpdateSql() +{ + + // if nothing to update, don't bother. + if( m_updateFlags == PlayerSyncFlags::None ) + return; + + std::set< std::string > charaBaseSet; + std::set< std::string > charaDetailSet; + std::set< std::string > charaClassSet; + std::set< std::string > charaQuestSet; + std::set< std::string > charaInfoSearchSet; + + std::string dbName = g_serverZone.getConfig()->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" ); + std::string updateCharaBase = "UPDATE " + dbName + ".charabase SET "; + std::string updateCharaDetail = "UPDATE " + dbName + ".charadetail SET "; + std::string updateCharaClass = "UPDATE " + dbName + ".characlass SET "; + std::string updateCharaQuest = "UPDATE " + dbName + ".charaquest SET "; + std::string updateCharaInfoSearch = "UPDATE " + dbName + ".charainfosearch SET "; + + std::string condition = " UPDATE_DATE = NOW() WHERE CharacterId = " + std::to_string( m_id ) + ";"; + + if( m_updateFlags & PlayerSyncFlags::Position ) + { + charaBaseSet.insert( " Pos_0_0 = " + std::to_string( m_pos.x ) ); + charaBaseSet.insert( " Pos_0_1 = " + std::to_string( m_pos.y ) ); + charaBaseSet.insert( " Pos_0_2 = " + std::to_string( m_pos.z ) ); + charaBaseSet.insert( " Pos_0_3 = " + std::to_string( getRotation() ) ); + charaBaseSet.insert( " PrimaryTerritoryId = " + std::to_string( m_zoneId ) ); + } + + if( m_updateFlags & PlayerSyncFlags::HomePoint ) + charaDetailSet.insert( " Homepoint = " + std::to_string( m_homePoint ) ); + + if( m_updateFlags & PlayerSyncFlags::Discovery ) + charaDetailSet.insert( " Discovery = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_discovery ), sizeof( m_discovery ) ) ) + "')" ); + + if( m_updateFlags & PlayerSyncFlags::PlayTime ) + charaDetailSet.insert( " TotalPlayTime = " + std::to_string( m_playTime ) ); + + if( m_updateFlags & PlayerSyncFlags::Unlocks ) + charaDetailSet.insert( " unlocks = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_unlocks ), sizeof( m_unlocks ) ) ) + "')" ); + + if( m_updateFlags & PlayerSyncFlags::QuestTracker ) + charaDetailSet.insert( " QuestTracking = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_questTracking ), sizeof( m_questTracking ) ) ) + "')" ); + + if( m_updateFlags & PlayerSyncFlags::HowTo ) + charaDetailSet.insert( " HowTo = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_howTo ), sizeof( m_howTo ) ) ) + "')" ); + + if( m_updateFlags & PlayerSyncFlags::Aetherytes ) + charaDetailSet.insert( " Aetheryte = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_aetheryte ), sizeof( m_aetheryte ) ) ) + "')" ); + + + if( m_updateFlags & PlayerSyncFlags::NewGame ) + charaBaseSet.insert( " IsNewGame = " + std::to_string( static_cast< uint32_t >( m_bNewGame ) ) ); + + if( m_updateFlags & PlayerSyncFlags::FirstLogin ) + charaBaseSet.insert( " FirstLogin = 0 " ); + + if( m_updateFlags & PlayerSyncFlags::NewAdventurer ) + charaBaseSet.insert( " IsNewAdventurer = " + std::to_string( static_cast< uint32_t >( m_bNewAdventurer ) ) ); + + if( m_updateFlags & PlayerSyncFlags::GC ) + { + charaDetailSet.insert( " GrandCompany = " + std::to_string( m_gc ) ); + charaDetailSet.insert( " GrandCompanyRank = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_gcRank ), sizeof( m_gcRank ) ) ) + "')" ); + } + + + + if( m_updateFlags & PlayerSyncFlags::ExpLevel ) + { + uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx; + charaClassSet.insert( " Lv_" + std::to_string( classJobIndex ) + " = " + std::to_string( static_cast< uint32_t >( getLevel() ) ) ); + charaClassSet.insert( " Exp_" + std::to_string( classJobIndex ) + " = " + std::to_string( getExp() ) ); + } + + if( m_updateFlags & PlayerSyncFlags::Status ) + { + charaBaseSet.insert( " Hp = " + std::to_string( getHp() ) ); + charaBaseSet.insert( " Mp = " + std::to_string( getMp() ) ); + charaBaseSet.insert( " Mode = " + std::to_string( static_cast< uint32_t >( getStance() ) ) ); + charaBaseSet.insert( " ModelEquip = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_modelEquip ), 40 ) ) + "')" ); + charaDetailSet.insert( " Class = " + std::to_string( static_cast< uint32_t >( getClass() ) ) ); + charaDetailSet.insert( " Status = " + std::to_string( static_cast< uint8_t >( getStatus() ) ) ); + } + + if( m_updateFlags & PlayerSyncFlags::Quests ) + { + charaDetailSet.insert( " QuestCompleteFlags = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_questCompleteFlags ), 200 ) ) + "')" ); + + for( int i = 0; i < 30; i++ ) + { + if( m_activeQuests[i] != nullptr ) + { + charaQuestSet.insert( " QuestId_" + std::to_string( i ) + " = " + std::to_string( m_activeQuests[i]->c.questId ) ); + charaQuestSet.insert( " Sequence_" + std::to_string( i ) + " = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.sequence ) ) ); + charaQuestSet.insert( " Flags_" + std::to_string( i ) + " = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.flags ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_0 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8A ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_1 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8B ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_2 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8C ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_3 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8D ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_4 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8E ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_5 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.UI8F ) ) ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_6 = " + std::to_string( static_cast< uint32_t >( m_activeQuests[i]->c.padding1 ) ) ); + } + else + { + charaQuestSet.insert( " QuestId_" + std::to_string( i ) + " = 0" ); + charaQuestSet.insert( " Sequence_" + std::to_string( i ) + " = 0" ); + charaQuestSet.insert( " Flags_" + std::to_string( i ) + " = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_0 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_1 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_2 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_3 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_4 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_5 = 0" ); + charaQuestSet.insert( " Variables_" + std::to_string( i ) + "_6 = 0" ); + } + } + } + + if( m_updateFlags & PlayerSyncFlags::SearchInfo ) + { + charaInfoSearchSet.insert( " SelectClassId = " + std::to_string( m_searchSelectClass ) ); + charaInfoSearchSet.insert( " SelectRegion = " + std::to_string( m_searchSelectRegion ) ); + charaInfoSearchSet.insert( " SearchComment = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_searchMessage ), sizeof( m_searchMessage ) ) + "')" ) ); + } + + if( !charaInfoSearchSet.empty() ) + { + for( auto entry : charaInfoSearchSet ) + updateCharaInfoSearch += entry + ", "; + + updateCharaInfoSearch += condition; + g_database.execute( updateCharaInfoSearch.c_str() ); + } + + if( !charaBaseSet.empty() ) + { + for( auto entry : charaBaseSet ) + updateCharaBase += entry + ", "; + + updateCharaBase += condition; + g_database.execute( updateCharaBase.c_str() ); + } + + if( !charaDetailSet.empty() ) + { + for( auto entry : charaDetailSet ) + updateCharaDetail += entry + ", "; + + updateCharaDetail += condition; + g_database.execute( updateCharaDetail.c_str() ); + } + + if( !charaClassSet.empty() ) + { + for( auto entry : charaClassSet ) + updateCharaClass += entry + ", "; + + updateCharaClass += condition; + g_database.execute( updateCharaClass.c_str() ); + } + + if( !charaQuestSet.empty() ) + { + for( auto entry : charaQuestSet ) + updateCharaQuest += entry + ", "; + + updateCharaQuest += condition; + g_database.execute( updateCharaQuest.c_str() ); + } + + m_updateFlags = PlayerSyncFlags::None; + +} + diff --git a/src/servers/Server_Zone/PlayerStateFlagsPacket.h b/src/servers/Server_Zone/PlayerStateFlagsPacket.h new file mode 100644 index 00000000..7b7f801d --- /dev/null +++ b/src/servers/Server_Zone/PlayerStateFlagsPacket.h @@ -0,0 +1,58 @@ +#ifndef _PLAYERSTATE_H +#define _PLAYERSTATE_H + +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class PlayerStateFlagsPacket : + public GamePacketNew< FFXIVIpcPlayerStateFlags > +{ +public: + PlayerStateFlagsPacket( Entity::PlayerPtr pActor ) : + GamePacketNew< FFXIVIpcPlayerStateFlags >( pActor->getId(), pActor->getId() ) + { + initialize( pActor->getStateFlags() ); + } + + PlayerStateFlagsPacket( Entity::PlayerPtr pActor, std::vector< Common::PlayerStateFlag > flags ) : + GamePacketNew< FFXIVIpcPlayerStateFlags >( pActor->getId(), pActor->getId() ) + { + uint8_t newFlags[7]; + memset( newFlags, 0, 7 ); + + for( auto& flag : flags ) + { + int iFlag = static_cast< uint32_t >( flag ); + uint8_t index = iFlag / 8; + uint8_t bitIndex = iFlag % 8; + + uint8_t value = 1 << bitIndex; + + newFlags[index] |= value; + } + + initialize( newFlags ); + } + +private: + void initialize( const uint8_t* flags ) + { + memcpy( m_data.flags, flags, 7 ); + }; +}; + +} +} +} +} + +#endif /*_PLAYERSTATE_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/QuestMessagePacket.h b/src/servers/Server_Zone/QuestMessagePacket.h new file mode 100644 index 00000000..6e5d6b5c --- /dev/null +++ b/src/servers/Server_Zone/QuestMessagePacket.h @@ -0,0 +1,42 @@ +#ifndef _QUESTMESSAGE_H +#define _QUESTMESSAGE_H + +#include +#include "Player.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class QuestMessagePacket : + public GamePacketNew< FFXIVIpcQuestMessage > +{ +public: + QuestMessagePacket( Entity::ActorPtr pActor, uint32_t questId, int8_t msgId, uint8_t type = 0, uint32_t var1 = 0, uint32_t var2 = 0 ) : + GamePacketNew< FFXIVIpcQuestMessage >( pActor->getId(), pActor->getId() ) + { + initialize( questId, msgId, type, var1, var2 ); + }; + +private: + void initialize( uint32_t questId, int8_t msgId, uint8_t type, uint32_t var1, uint32_t var2 ) + { + m_data.questId = questId; + m_data.msgId = msgId; + m_data.type = type; + m_data.var1 = var1; + m_data.var2 = var2; + }; +}; + +} +} +} +} + +#endif /* _QUESTMESSAGE_H */ \ No newline at end of file diff --git a/src/servers/Server_Zone/ScriptManager.cpp b/src/servers/Server_Zone/ScriptManager.cpp new file mode 100644 index 00000000..4a05c549 --- /dev/null +++ b/src/servers/Server_Zone/ScriptManager.cpp @@ -0,0 +1,396 @@ +#include +#include +#include + +#include +#include "ScriptManager.h" + +#include "Zone.h" +#include "Player.h" +#include "BattleNpc.h" +#include "ServerZone.h" +#include "Event.h" +#include "EventHelper.h" +#include "ScriptManager.h" + +#include "ServerNoticePacket.h" + +#include +#include +#include +#include +#include + +extern Core::Logger g_log; +extern Core::Data::ExdData g_exdData; +extern Core::ServerZone g_serverZone; +extern Core::Scripting::ScriptManager g_scriptManager; + +Core::Scripting::ScriptManager::ScriptManager() +{ + m_pChaiHandler = create_chaiscript(); +} + +Core::Scripting::ScriptManager::~ScriptManager() +{ + +} + +void Core::Scripting::ScriptManager::loadDir( std::string dirname, std::set& chaiFiles ) +{ + + boost::filesystem::path targetDir( dirname ); + + boost::filesystem::recursive_directory_iterator iter( targetDir ), eod; + + BOOST_FOREACH( boost::filesystem::path const& i, make_pair( iter, eod ) ) + { + + if( is_regular_file( i ) && boost::filesystem::extension( i.string() ) == ".chai" || + boost::filesystem::extension( i.string() ) == ".inc" ) + { + chaiFiles.insert( i.string() ); + } + } + +} + +void Core::Scripting::ScriptManager::onPlayerFirstEnterWorld( Core::Entity::PlayerPtr pPlayer ) +{ + try + { + std::string test = m_onFirstEnterWorld( *pPlayer ); + } + catch( const std::exception &e ) + { + std::string what = e.what(); + g_log.Log( LoggingSeverity::error, what ); + } +} + +bool Core::Scripting::ScriptManager::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ) +{ + return g_serverZone.registerBnpcTemplate( templateName, bnpcBaseId, bnpcNameId, modelId, aiName ); +} + +void Core::Scripting::ScriptManager::reload() +{ + auto handler = create_chaiscript(); + m_pChaiHandler.swap( handler ); + init(); +} + +const boost::shared_ptr& Core::Scripting::ScriptManager::getHandler() const +{ + return m_pChaiHandler; +} + + +bool Core::Scripting::ScriptManager::onTalk( Core::Entity::PlayerPtr pPlayer, uint64_t actorId, uint32_t eventId ) +{ + std::string eventName = "onTalk"; + std::string objName = Event::getEventName( eventId ); + + pPlayer->sendDebug("Actor: " + + std::to_string( actorId ) + + " \neventId: " + + std::to_string( eventId ) + + " (0x" + boost::str( boost::format( "%|08X|" ) + % static_cast< uint64_t >( eventId & 0xFFFFFFF ) ) + ")" ); + + uint16_t eventType = eventId >> 16; + + try + { + // Get object from engine + auto obj = m_pChaiHandler->eval( objName ); + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( actorId, eventId, Event::Event::Talk, 0, 0 ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint64_t ) > >( eventName ); + fn( obj, eventId, *pPlayer, actorId ); + + pPlayer->checkEvent( eventId ); + } + catch( std::exception& e ) + { + + if( eventType == Common::EventType::Quest ) + { + auto questInfo = g_exdData.getQuestInfo( eventId ); + if( questInfo ) + { + pPlayer->sendDebug( "Quest not implemented: " + questInfo->name + "\n" + e.what() ); + return false; + } + } + + pPlayer->sendDebug( e.what() ); + return false; + } + return true; +} + +bool Core::Scripting::ScriptManager::onEnterTerritory( Core::Entity::PlayerPtr pPlayer, uint32_t eventId, + uint16_t param1, uint16_t param2 ) +{ + std::string eventName = "onEnterTerritory"; + std::string objName = Event::getEventName( eventId ); + + try + { + // Get object from engine + auto obj = m_pChaiHandler->eval( objName ); + + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( pPlayer->getId(), eventId, Event::Event::EnterTerritory, 0, pPlayer->getZoneId() ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint16_t, uint16_t ) > >( eventName ); + fn( obj, eventId, *pPlayer, param1, param2 ); + + pPlayer->checkEvent( eventId ); + } + catch( std::exception& e ) + { + pPlayer->sendDebug( e.what() ); + return false; + } + return true; +} + +bool Core::Scripting::ScriptManager::onWithinRange( Entity::PlayerPtr pPlayer, uint32_t eventId, uint32_t param1, float x, float y, float z ) +{ + std::string eventName = "onWithinRange"; + std::string objName = Event::getEventName( eventId ); + + try + { + // Get object from engine + auto obj = m_pChaiHandler->eval( Event::getEventName( eventId ) ); + + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( pPlayer->getId(), eventId, Event::Event::WithinRange, 1, param1 ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint32_t, float, float, float ) > >( eventName ); + fn( obj, eventId, *pPlayer, param1, x, y, z ); + + pPlayer->checkEvent( eventId ); + } + catch( std::exception& e ) + { + pPlayer->sendDebug( e.what() ); + return false; + } + return true; +} + +bool Core::Scripting::ScriptManager::onOutsideRange( Entity::PlayerPtr pPlayer, uint32_t eventId, uint32_t param1, float x, float y, float z ) +{ + std::string eventName = "onOutsideRange"; + std::string objName = Event::getEventName( eventId ); + + try + { + // Get object from engine + auto obj = m_pChaiHandler->eval( Event::getEventName( eventId ) ); + + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( pPlayer->getId(), eventId, Event::Event::OutsideRange, 1, param1 ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint32_t, float, float, float ) > >( eventName ); + fn( obj, eventId, *pPlayer, param1, x, y, z ); + + pPlayer->checkEvent( eventId ); + } + catch( std::exception& e ) + { + pPlayer->sendDebug( e.what() ); + return false; + } + return true; +} + +bool Core::Scripting::ScriptManager::onEmote( Core::Entity::PlayerPtr pPlayer, uint64_t actorId, + uint32_t eventId, uint8_t emoteId ) +{ + std::string eventName = "onEmote"; + std::string objName = Event::getEventName( eventId ); + + try + { + auto obj = m_pChaiHandler->eval( Event::getEventName( eventId ) ); + + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( actorId, eventId, Event::Event::Emote, 0, emoteId ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint64_t, uint8_t ) > >( eventName ); + fn( obj, eventId, *pPlayer, actorId, emoteId ); + + pPlayer->checkEvent( eventId ); + } + catch( std::exception& e ) + { + uint16_t eventType = eventId >> 16; + + if( eventType == Common::EventType::Quest ) + { + auto questInfo = g_exdData.getQuestInfo( eventId ); + if( questInfo ) + { + pPlayer->sendDebug( "Quest not implemented: " + questInfo->name + "\n" + e.what() ); + return false; + } + } + return false; + } + return true; +} + +bool Core::Scripting::ScriptManager::onEventHandlerReturn( Core::Entity::PlayerPtr pPlayer, uint32_t eventId, uint16_t subEvent, + uint16_t param1, uint16_t param2, uint16_t param3 ) +{ + + pPlayer->sendDebug("eventId: " + + std::to_string( eventId ) + + " ( 0x" + boost::str( boost::format( "%|08X|" ) % ( uint64_t ) ( eventId & 0xFFFFFFF ) ) + " ) " + + " scene: " + std::to_string( subEvent ) + + " p1: " + std::to_string( param1 ) + + " p2: " + std::to_string( param2 ) + + " p3: " + std::to_string( param3 ) ); + + try + { + auto pEvent = pPlayer->getEvent( eventId ); + if( pEvent ) + { + pEvent->setPlayedScene( false ); + // try to retrieve a stored callback + auto eventCallback = pEvent->getEventReturnCallback(); + // if there is one, proceed to call it + if( eventCallback ) + { + eventCallback( *pPlayer, eventId, param1, param2, param3 ); + if( !pEvent->hasPlayedScene() ) + pPlayer->eventFinish( eventId, 1 ); + else + pEvent->setPlayedScene( false ); + } + // else, finish the event. + else + pPlayer->eventFinish( eventId, 1 ); + } + } + catch( std::exception& e ) + { + pPlayer->sendNotice( e.what() ); + return false; + } + + return true; +} + +bool Core::Scripting::ScriptManager::onEventHandlerTradeReturn( Core::Entity::PlayerPtr pPlayer, uint32_t eventId, + uint16_t subEvent, uint16_t param, uint32_t catalogId ) +{ + std::string eventName = Event::getEventName( eventId ) + "_TRADE"; + + try + { + auto fn = m_pChaiHandler->eval< std::function< void( Entity::Player&, uint32_t, uint16_t, uint16_t, uint32_t ) > >( eventName ); + fn( *pPlayer, eventId, subEvent, param, catalogId ); + } + catch( ... ) + { + return false; + } + + return true; +} + +bool Core::Scripting::ScriptManager::onEventItem( Entity::PlayerPtr pPlayer, uint32_t eventItemId, + uint32_t eventId, uint32_t castTime, uint64_t targetId ) +{ + std::string eventName = "onEventItem"; + std::string objName = Event::getEventName( eventId ); + + try + { + auto obj = m_pChaiHandler->eval( Event::getEventName( eventId ) ); + + pPlayer->sendDebug( "Calling: " + objName + "." + eventName ); + + pPlayer->eventStart( targetId, eventId, Event::Event::Item, 0, 0 ); + + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, uint32_t, Entity::Player&, uint32_t, uint32_t, uint64_t ) > >( eventName ); + fn( obj, eventId, *pPlayer, eventItemId, castTime, targetId ); + } + catch( std::exception& e ) + { + pPlayer->sendNotice( e.what() ); + return false; + } + + return true; + +} + +bool Core::Scripting::ScriptManager::onMobKill( Entity::PlayerPtr pPlayer, uint16_t nameId ) +{ + std::string eventName = "onBnpcKill_" + std::to_string( nameId ); + + + // loop through all active quests and try to call available onMobKill callbacks + for( size_t i = 0; i < 30; i++ ) + { + auto activeQuests = pPlayer->getQuestActive( i ); + if( !activeQuests ) + continue; + + uint16_t questId = activeQuests->c.questId; + if( questId != 0 ) + { + auto obj = m_pChaiHandler->eval( Event::getEventName( 0x00010000 | questId ) ); + std::string objName = Event::getEventName( 0x00010000 | questId ); + + pPlayer->sendDebug("Calling: " + objName + "." + eventName); + + try + { + auto fn = m_pChaiHandler->eval< std::function< void( chaiscript::Boxed_Value &, Entity::Player& ) > >(eventName); + fn( obj, *pPlayer ); + } + catch( std::exception& e ) + { + g_log.info( e.what() ); + } + + } + } + + return true; +} + +bool Core::Scripting::ScriptManager::onZoneInit( ZonePtr pZone ) +{ + std::string eventName = "onZoneInit_" + pZone->getInternalName(); + + try + { + auto fn = m_pChaiHandler->eval< std::function< void( Zone& ) > >( eventName ); + fn( *pZone ); + } + catch( std::exception& e ) + { + g_log.info( e.what() ); + return false; + } + + return true; + +} + diff --git a/src/servers/Server_Zone/ScriptManager.h b/src/servers/Server_Zone/ScriptManager.h new file mode 100644 index 00000000..83673fc2 --- /dev/null +++ b/src/servers/Server_Zone/ScriptManager.h @@ -0,0 +1,65 @@ +#ifndef _SCRIPTMANAGER_H_ +#define _SCRIPTMANAGER_H_ + +#include +#include +#include + +#include +#include "Forwards.h" + + +namespace chaiscript +{ + class ChaiScript; +} + +namespace Core +{ + namespace Scripting + { + + class ScriptManager + { + private: + + boost::shared_ptr< chaiscript::ChaiScript > m_pChaiHandler; + + std::function< std::string( Entity::Player& ) > m_onFirstEnterWorld; + // auto fn = m_pChaiHandler->eval< std::function >( "onFirstEnterWorld" ); + + public: + ScriptManager(); + ~ScriptManager(); + + int init(); + void reload(); + + const boost::shared_ptr< chaiscript::ChaiScript >& getHandler() const; + + void onPlayerFirstEnterWorld( Entity::PlayerPtr pPlayer ); + + static bool registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); + + bool onTalk( Entity::PlayerPtr pPlayer, uint64_t actorId, uint32_t eventId ); + bool onEnterTerritory( Entity::PlayerPtr pPlayer, uint32_t eventId, uint16_t param1, uint16_t param2 ); + bool onWithinRange( Entity::PlayerPtr pPlayer, uint32_t eventId, uint32_t param1, float x, float y, float z ); + bool onOutsideRange( Entity::PlayerPtr pPlayer, uint32_t eventId, uint32_t param1, float x, float y, float z ); + bool onEmote( Entity::PlayerPtr pPlayer, uint64_t actorId, uint32_t eventId, uint8_t emoteId ); + bool onEventItem( Entity::PlayerPtr pPlayer, uint32_t eventItemId, uint32_t eventId, uint32_t castTime, uint64_t targetId ); + + bool onMobKill( Entity::PlayerPtr pPlayer, uint16_t nameId ); + + bool onZoneInit( ZonePtr pZone ); + + bool onEventHandlerReturn( Entity::PlayerPtr pPlayer, uint32_t eventId, uint16_t subEvent, uint16_t param1, uint16_t param2, uint16_t param3 ); + bool onEventHandlerTradeReturn( Entity::PlayerPtr pPlayer, uint32_t eventId, uint16_t subEvent, uint16_t param, uint32_t catalogId ); + + + void loadDir( std::string dirname, std::set& chaiFiles ); + + + }; + } +} +#endif diff --git a/src/servers/Server_Zone/ScriptManagerInit.cpp b/src/servers/Server_Zone/ScriptManagerInit.cpp new file mode 100644 index 00000000..b3b0fcaa --- /dev/null +++ b/src/servers/Server_Zone/ScriptManagerInit.cpp @@ -0,0 +1,199 @@ +#include +#include + +#include +#include "ScriptManager.h" + +#include "Zone.h" +#include "Player.h" +#include "BattleNpc.h" +#include "Event.h" +#include "EventHelper.h" + +#include "ServerNoticePacket.h" + +#include +#include +#include +#include +#include + + +extern Core::Logger g_log; + +int Core::Scripting::ScriptManager::init() +{ + // ACTOR / PLAYER BINDINGS + ///////////////////////////////////////////////////////////////////////////////////////////// + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getName ), "getName" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getId ), "getId" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getHp ), "getHp" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getMp ), "getMp" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getTp ), "getTp" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getLevel ), "getLevel" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Actor::getTargetId ), "getTargetId" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::forceZoneing ), "setZone" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getClassAsInt ), "getClass" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getRace ), "getRace" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getGender ), "getGender" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::isFirstLogin ), "isFirstLogin" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setFirstLogin ), "setFirstLogin" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::gainExp ), "gainExp" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::unlock ), "unlock" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::registerAetheryte ), "aetheryteRegister" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::learnAction ), "learnAction" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getHomepoint ), "getHomepoint" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setHomepoint ), "setHomepoint" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::teleport ), "teleport" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::prepareZoning ), "prepareZoning" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getCurrency ), "getCurrency" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::addCurrency ), "addCurrency" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::removeCurrency ), "removeCurrency" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getCrystal ), "getCrystals" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::addCrystal ), "addCrystals" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::removeCrystal ), "removeCrystals" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::updateQuest ), "questUpdate" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::finishQuest ), "questFinish" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::sendQuestMessage ), "questMessage" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestSeq ), "questGetSeq" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::hasQuest ), "hasQuest" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getZoneId ), "getZoneId" ); + + + m_pChaiHandler->add( chaiscript::fun( &Core::Event::mapEventActorToRealActor ), "mapActor" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8A ), "getQuestUI8A" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8B ), "getQuestUI8B" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8C ), "getQuestUI8C" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8D ), "getQuestUI8D" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8E ), "getQuestUI8E" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8F ), "getQuestUI8F" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8AH ), "getQuestUI8AH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8BH ), "getQuestUI8BH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8CH ), "getQuestUI8CH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8DH ), "getQuestUI8DH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8EH ), "getQuestUI8EH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8FH ), "getQuestUI8FH" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8AL ), "getQuestUI8AL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8BL ), "getQuestUI8BL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8CL ), "getQuestUI8CL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8DL ), "getQuestUI8DL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8EL ), "getQuestUI8EL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI8FL ), "getQuestUI8FL" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI16A ), "getQuestUI16A" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI16B ), "getQuestUI16B" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI16C ), "getQuestUI16C" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestUI32A ), "getQuestUI32A" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag8 ), "getQuestBitFlag8" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag16 ), "getQuestBitFlag16" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag24 ), "getQuestBitFlag24" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag32 ), "getQuestBitFlag32" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag40 ), "getQuestBitFlag40" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::getQuestBitFlag48 ), "getQuestBitFlag48" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8A ), "setQuestUI8A" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8B ), "setQuestUI8B" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8C ), "setQuestUI8C" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8D ), "setQuestUI8D" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8E ), "setQuestUI8E" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8F ), "setQuestUI8F" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8AH ), "setQuestUI8AH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8BH ), "setQuestUI8BH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8CH ), "setQuestUI8CH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8DH ), "setQuestUI8DH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8EH ), "setQuestUI8EH" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8FH ), "setQuestUI8FH" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8AL ), "setQuestUI8AL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8BL ), "setQuestUI8BL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8CL ), "setQuestUI8CL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8DL ), "setQuestUI8DL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8EL ), "setQuestUI8EL" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI8FL ), "setQuestUI8FL" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI16A ), "setQuestUI16A" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI16B ), "setQuestUI16B" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI16C ), "setQuestUI16C" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestUI32A ), "setQuestUI32A" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag8 ), "setQuestBitFlag8" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag16 ), "setQuestBitFlag16" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag24 ), "setQuestBitFlag24" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag32 ), "setQuestBitFlag32" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag40 ), "setQuestBitFlag40" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::setQuestBitFlag48 ), "setQuestBitFlag48" ); + + m_pChaiHandler->add(chaiscript::fun(&Entity::Player::giveQuestRewards), "giveQuestRewards"); + + m_pChaiHandler->add( chaiscript::fun< void, Entity::Player, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t >( &Entity::Player::eventPlay ), "eventPlay" ); + m_pChaiHandler->add( chaiscript::fun< void, Entity::Player, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, EventReturnCallback >( &Entity::Player::eventPlay ), "eventPlay" ); + m_pChaiHandler->add( chaiscript::fun< void, Entity::Player, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, EventReturnCallback >( &Entity::Player::eventPlay ), "eventPlay" ); + m_pChaiHandler->add( chaiscript::fun< void, Entity::Player, uint32_t, uint32_t, uint32_t, EventReturnCallback >( &Entity::Player::eventPlay ), "eventPlay" ); + m_pChaiHandler->add( chaiscript::fun< void, Entity::Player, uint32_t, uint32_t, uint32_t >( &Entity::Player::eventPlay ), "eventPlay" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::eventActionStart ), "eventActionStart" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::eventItemActionStart ), "eventItemActionStart" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::changePosition ), "changePos" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::tryAddItem ), "tryAddItem" ); + + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::isAetheryteRegistered ), "isAetheryteRegistered" ); + m_pChaiHandler->add( chaiscript::fun( &Entity::Player::isActionLearned ), "isActionLearned" ); + + m_pChaiHandler->add( chaiscript::base_class< Entity::Actor, Entity::Player >() ); + m_pChaiHandler->add( chaiscript::base_class< Entity::Actor, Entity::BattleNpc >() ); + m_pChaiHandler->add( chaiscript::user_type< Entity::Actor >(), "Actor" ); + m_pChaiHandler->add( chaiscript::user_type< Entity::Player >(), "Player" ); + m_pChaiHandler->add( chaiscript::user_type< Entity::BattleNpc >(), "BattleNpc" ); + + m_pChaiHandler->add( chaiscript::user_type< Zone >(), "Zone" ); + m_pChaiHandler->add( chaiscript::fun( &Zone::getName ), "getName" ); + + m_pChaiHandler->add( chaiscript::fun( ®isterBnpcTemplate ), "registerBnpcTemplate" ); + + // EVENT BINDINGS + ///////////////////////////////////////////////////////////////////////////////////////////// + + std::set< std::string > chaiFiles; + + g_log.info( "ScriptEngine: Loading Scripts..." ); + loadDir( "scripts/chai", chaiFiles ); + + uint16_t scriptCount = 0; + uint16_t errorCount = 0; + for( auto itr = chaiFiles.begin(); itr != chaiFiles.end(); ++itr ) + { + auto& fileName = *itr; + + try + { + m_pChaiHandler->eval_file( fileName ); + scriptCount++; + } + catch( std::exception& e ) + { + g_log.Log( LoggingSeverity::error, e.what() ); + errorCount++; + } + + } + + g_log.info( "\tloaded " + std::to_string( scriptCount ) + + " scripts, " + std::to_string( errorCount ) + " errors." ); + + std::function f = + m_pChaiHandler->eval< std::function >( "onFirstEnterWorld" ); + + m_onFirstEnterWorld = f; + + + return true; +} + diff --git a/src/servers/Server_Zone/ServerNoticePacket.h b/src/servers/Server_Zone/ServerNoticePacket.h new file mode 100644 index 00000000..6db52097 --- /dev/null +++ b/src/servers/Server_Zone/ServerNoticePacket.h @@ -0,0 +1,38 @@ +#ifndef _SERVERNOTICEPACKET_H +#define _SERVERNOTICEPACKET_H + +#include +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class ServerNoticePacket : + public GamePacketNew +{ +public: + ServerNoticePacket( uint32_t playerId, const std::string& message ) : + GamePacketNew( playerId, playerId ) + { + initialize( message ); + }; + +private: + void initialize( const std::string& message ) + { + strcpy( m_data.message, message.c_str() ); + }; +}; + +} +} +} +} + +#endif /*_SERVERNOTICEPACKET_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/ServerZone.cpp b/src/servers/Server_Zone/ServerZone.cpp new file mode 100644 index 00000000..45312f88 --- /dev/null +++ b/src/servers/Server_Zone/ServerZone.cpp @@ -0,0 +1,367 @@ +#include +#include +#include + +#include "ServerZone.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "GameConnection.h" +#include "Session.h" + +#include "ZoneMgr.h" + +#include "GameCommandHandler.h" + +#include "ScriptManager.h" + +#include "Forwards.h" +#include +#include + + +Core::Logger g_log; +Core::Db::Database g_database; +Core::GameCommandHandler g_gameCommandMgr; +Core::Scripting::ScriptManager g_scriptMgr; +Core::Data::ExdData g_exdData; +Core::ZoneMgr g_zoneMgr; + + +Core::ServerZone::ServerZone( const std::string& configPath, uint16_t serverId ) + : m_serverId( serverId ) + , m_configPath( configPath ) +{ + m_pConfig = XMLConfigPtr( new XMLConfig ); +} + +Core::ServerZone::~ServerZone() +{ +} + +Core::XMLConfigPtr Core::ServerZone::getConfig() const +{ + return m_pConfig; +} + +bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, + uint32_t bnpcNameId, uint32_t modelId, std::string aiName ) +{ + + auto it = m_bnpcTemplates.find( templateName ); + + if( it != m_bnpcTemplates.end() ) + { + g_log.error( templateName + " already registered, skipping..." ); + return false; + } + + Entity::BattleNpcTemplatePtr pNpcTemplate( new Entity::BattleNpcTemplate( templateName, bnpcBaseId, bnpcNameId, modelId, aiName ) ); + m_bnpcTemplates[templateName] = pNpcTemplate; + + return true; +} + +Core::Entity::BattleNpcTemplatePtr Core::ServerZone::getBnpcTemplate( std::string templateName ) +{ + auto it = m_bnpcTemplates.find(templateName); + + if (it != m_bnpcTemplates.end()) + return nullptr; + + return it->second; +} + + +void Core::ServerZone::setServerId( uint16_t serverId ) +{ + m_serverId = serverId; +} + +bool Core::ServerZone::loadSettings( int argc, char* argv[] ) +{ + g_log.info( "Loading config " + m_configPath ); + + if( !m_pConfig->loadConfig( m_configPath ) ) + { + g_log.fatal( "Error loading config " + m_configPath ); + return false; + } + + std::vector args( argv + 1, argv + argc ); + for( auto i = 0; i + 1 < args.size(); i += 2 ) + { + std::string arg( "" ); + std::string val( "" ); + + try + { + arg = std::string( args[i] ); + val = std::string( args[i + 1] ); + + // trim '-' from start of arg + arg = arg.erase( 0, arg.find_first_not_of( '-' ) ); + + if( arg == "ip" ) + { + // todo: ip addr in config + m_pConfig->setValue< std::string >( "Settings.General.ListenIP", val ); + } + else if( arg == "p" || arg == "port" ) + { + m_pConfig->setValue< std::string >( "Settings.General.ListenPort", val ); + } + else if( arg == "exdPath" || arg == "dataPath" ) + { + m_pConfig->setValue< std::string >( "Settings.General.DataPath", val ); + } + else if( arg == "h" || arg == "dbhost" ) + { + m_pConfig->setValue< std::string >( "Settings.General.Mysql.Host", val ); + } + else if( arg == "dbport" ) + { + m_pConfig->setValue< std::string >( "Settings.General.Mysql.Port", val ); + } + else if( arg == "u" || arg == "user" || arg == "dbuser" ) + { + m_pConfig->setValue< std::string >( "Settings.General.Mysql.Username", val ); + } + else if( arg == "pass" || arg == "dbpass" ) + { + m_pConfig->setValue< std::string >( "Settings.General.Mysql.Pass", val ); + } + else if( arg == "d" || arg == "db" || arg == "database" ) + { + m_pConfig->setValue< std::string >( "Settings.General.Mysql.Database", val ); + } + } + catch( ... ) + { + g_log.error( "Error parsing argument: " + arg + " " + "value: " + val + "\n" ); + g_log.error( "Usage: \n" ); + } + } + + g_log.info( "Setting up EXD data" ); + if( !g_exdData.init( m_pConfig->getValue< std::string >( "Settings.General.DataPath", "" ) ) ) + { + g_log.fatal( "Error setting up EXD data " ); + return false; + } + + Db::DatabaseParams params; + params.bufferSize = 16384; + params.connectionCount = 3; + params.databaseName = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" ); + params.hostname = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Host", "127.0.0.1" ); + params.password = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Pass", "" ); + params.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 ); + params.username = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" ); + + if( !g_database.initialize( params ) ) + { + std::this_thread::sleep_for( std::chrono::milliseconds( 5000 ) ); + return false; + } + + m_serverId = m_serverId ? m_serverId : m_pConfig->getValue< uint16_t >( "Settings.General.ServerId" ); + m_port = m_pConfig->getValue< uint16_t >( "Settings.General.ListenPort" ); + m_ip = m_pConfig->getValue< std::string >( "Settings.General.ListenIp" );; + + g_log.info( "Server ID: " + std::to_string( m_serverId ) ); + + return true; +} + +void Core::ServerZone::run( int argc, char* argv[] ) +{ + // TODO: add more error checks for the entire initialisation + g_log.setLogPath( "SapphireZone_" + std::to_string( m_serverId ) + "_" ); + g_log.init(); + + g_log.info( "===========================================================" ); + g_log.info( "Sapphire Server Project " ); + g_log.info( "Version: x.y.z" ); + g_log.info( "Compiled: " __DATE__ " " __TIME__ ); + g_log.info( "===========================================================" ); + + if( !loadSettings( argc, argv ) ) + { + g_log.fatal( "Unable to load settings!" ); + return; + } + + g_exdData.loadZoneInfo(); + g_exdData.loadClassJobInfo(); + g_exdData.loadParamGrowInfo(); + g_exdData.loadEventActionInfo(); + g_exdData.loadActionInfo(); + g_exdData.loadStatusEffectInfo(); + g_exdData.loadAetheryteInfo(); + g_exdData.loadTribeInfo(); + + Network::HivePtr hive( new Network::Hive() ); + Network::addServerToHive< Network::GameConnection >( m_ip, m_port, hive ); + + g_scriptMgr.init(); + + g_log.info( "ZoneHandler: Setting up zones" ); + g_zoneMgr.createZones(); + + std::vector< std::thread > thread_list; + thread_list.push_back( std::thread( std::bind( &Network::Hive::Run, hive.get() ) ) ); + + g_log.info( "Server listening on port: " + std::to_string( m_port ) ); + g_log.info( "Ready for connections..." ); + + while( true ) + { + std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) ); + + g_zoneMgr.updateZones(); + std::lock_guard lock( m_sessionMutex ); + for( auto sessionIt : m_sessionMap ) + { + auto session = sessionIt.second; + if( session && session->getPlayer() ) + { + // if the player is in a zone, let the zone handler take care of his updates + // else do it here. + if( !session->getPlayer()->getCurrentZone() ) + session->update(); + } + } + + uint32_t currTime = static_cast< uint32_t >( time( nullptr ) ); + auto it = m_sessionMap.begin(); + for( ; it != m_sessionMap.end(); ) + { + uint32_t diff = currTime - it->second->getLastDataTime(); + + auto pPlayer = it->second->getPlayer(); + + if( diff > 20 ) + { + g_log.info( "[" + std::to_string( it->second->getId() ) + "] Session time out" ); + it->second->close(); + // if( it->second.unique() ) + { + it = m_sessionMap.erase( it ); + } + } + else + { + ++it; + } + + } + + } + + // currently never reached, need a "stopServer" variable to break out of the above while loop + /*for( auto& thread_entry : thread_list ) + { + thread_entry.join(); + }*/ + +} + +bool Core::ServerZone::createSession( uint32_t sessionId ) +{ + std::lock_guard< std::mutex > lock( m_sessionMutex ); + + const std::string session_id_str = std::to_string( sessionId ); + + auto it = m_sessionMap.find( sessionId ); + + if( it != m_sessionMap.end() ) + { + g_log.error( "[" + session_id_str + "] Error creating session" ); + return false; + } + + g_log.info( "[" + session_id_str + "] Creating new session" ); + + boost::shared_ptr newSession( new Session( sessionId ) ); + m_sessionMap[sessionId] = newSession; + + if( !newSession->loadPlayer() ) + { + g_log.error( "[" + session_id_str + "] Error loading player " + session_id_str ); + return false; + } + + m_playerSessionMap[newSession->getPlayer()->getName()] = newSession; + + return true; + +} + +void Core::ServerZone::removeSession( uint32_t sessionId ) +{ + m_sessionMap.erase( sessionId ); +} + +void Core::ServerZone::updateSession( uint32_t id ) +{ + std::lock_guard< std::mutex > lock( m_sessionMutex ); + auto it = m_sessionMap.find( id ); + + if( it != m_sessionMap.end() ) + it->second->loadPlayer(); +} + +Core::SessionPtr Core::ServerZone::getSession( uint32_t id ) +{ + //std::lock_guard lock( m_sessionMutex ); + + auto it = m_sessionMap.find( id ); + + if( it != m_sessionMap.end() ) + return ( it->second ); + + return nullptr; +} + +Core::SessionPtr Core::ServerZone::getSession( std::string playerName ) +{ + //std::lock_guard lock( m_sessionMutex ); + + auto it = m_playerSessionMap.find( playerName ); + + if (it != m_playerSessionMap.end()) + return (it->second); + + return nullptr; +} + +void Core::ServerZone::removeSession( std::string playerName ) +{ + m_playerSessionMap.erase( playerName ); +} + +void Core::ServerZone::updateSession( std::string playerName ) +{ + std::lock_guard< std::mutex > lock( m_sessionMutex ); + auto it = m_playerSessionMap.find( playerName ); + + if( it != m_playerSessionMap.end() ) + it->second->loadPlayer(); +} + +uint16_t Core::ServerZone::getServerId() const +{ + return m_serverId; +} + + diff --git a/src/servers/Server_Zone/ServerZone.h b/src/servers/Server_Zone/ServerZone.h new file mode 100644 index 00000000..6b615838 --- /dev/null +++ b/src/servers/Server_Zone/ServerZone.h @@ -0,0 +1,69 @@ +#ifndef __GAMESERVER_H +#define __GAMESERVER_H + +#include + +#include +#include +#include + +#include "Forwards.h" +#include "BattleNpcTemplate.h" + +namespace Core { + + class ServerZone + { + public: + ServerZone( const std::string& configPath, uint16_t serverId = 0 ); + ~ServerZone(); + + void run( int argc, char* argv[] ); + + void setServerId( uint16_t serverId ); + uint16_t getServerId() const; + + bool createSession( uint32_t sessionId ); + void removeSession( uint32_t sessionId ); + void removeSession( std::string playerName ); + + bool loadSettings( int argc, char* argv[] ); + + SessionPtr getSession( uint32_t id ); + SessionPtr getSession( std::string playerName ); + void updateSession( uint32_t id ); + void updateSession( std::string playerName ); + + XMLConfigPtr getConfig() const; + + bool registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId, + uint32_t bnpcNameId, uint32_t modelId, std::string aiName ); + + Entity::BattleNpcTemplatePtr getBnpcTemplate( std::string templateName ); + + + private: + + uint16_t m_serverId; + uint16_t m_port; + std::string m_ip; + + std::string m_configPath; + + XMLConfigPtr m_pConfig; + + std::mutex m_sessionMutex; + + std::map< uint32_t, SessionPtr > m_sessionMap; + std::map< std::string, SessionPtr > m_playerSessionMap; + + std::map< uint32_t, uint32_t > m_zones; + + std::map< std::string, Entity::BattleNpcTemplatePtr > m_bnpcTemplates; + + }; + +} + +#endif + diff --git a/src/servers/Server_Zone/Session.cpp b/src/servers/Server_Zone/Session.cpp new file mode 100644 index 00000000..e67b5fc5 --- /dev/null +++ b/src/servers/Server_Zone/Session.cpp @@ -0,0 +1,93 @@ +#include + +#include +#include +#include "GameConnection.h" +#include "Session.h" + +#include "Player.h" + +Core::Session::Session( uint32_t sessionId ) + : m_sessionId( sessionId ) + , m_lastDataTime( static_cast< uint32_t >( time( nullptr ) ) ) +{ + + // boost::posix_time::ptime now = boost::date_time::not_a_date_time; + // now = boost::posix_time::microsec_clock::universal_time(); + +} + +Core::Session::~Session() +{ +} + +void Core::Session::setZoneConnection( Core::Network::GameConnectionPtr pZoneCon ) +{ + pZoneCon->m_conType = Network::ConnectionType::Zone; + m_pZoneConnection = pZoneCon; +} + +Core::Network::GameConnectionPtr Core::Session::getZoneConnection() const +{ + return m_pZoneConnection; +} + +bool Core::Session::loadPlayer() +{ + + m_pPlayer = Entity::PlayerPtr( new Entity::Player() ); + + if( !m_pPlayer->load( m_sessionId, shared_from_this() ) ) + return false; + + return true; + +} + +void Core::Session::close() +{ + if( m_pZoneConnection ) + m_pZoneConnection->Disconnect(); + + // remove the session from the player + if( m_pPlayer ) + // reset the zone, so the zone handler knows to remove the actor + m_pPlayer->setCurrentZone( nullptr ); +} + +uint32_t Core::Session::getId() const +{ + return m_sessionId; +} + +uint32_t Core::Session::getLastDataTime() const +{ + return m_lastDataTime; +} + +void Core::Session::updateLastDataTime() +{ + m_lastDataTime = static_cast< uint32_t >( time( nullptr ) ); +} + +void Core::Session::update() +{ + if( m_pZoneConnection ) + { + m_pZoneConnection->processInQueue(); + + // SESSION LOGIC + m_pPlayer->update( Util::getTimeMs() ); + + m_pPlayer->createUpdateSql(); + + m_pZoneConnection->processOutQueue(); + } + +} + +Core::Entity::PlayerPtr Core::Session::getPlayer() const +{ + return m_pPlayer; +} + diff --git a/src/servers/Server_Zone/Session.h b/src/servers/Server_Zone/Session.h new file mode 100644 index 00000000..f6b9eac8 --- /dev/null +++ b/src/servers/Server_Zone/Session.h @@ -0,0 +1,51 @@ +#ifndef _SESSION_H_ +#define _SESSION_H_ +#include +#include + +#include "Forwards.h" + + + +namespace Core { + + + class Session : public boost::enable_shared_from_this< Session > + { + public: + Session( uint32_t sessionId ); + ~Session(); + + void setZoneConnection( Network::GameConnectionPtr zoneCon ); + + Network::GameConnectionPtr getZoneConnection() const; + + uint32_t getLastDataTime() const; + + void updateLastDataTime(); + + void close(); + + uint32_t getId() const; + + bool loadPlayer(); + + void update(); + + Entity::PlayerPtr getPlayer() const; + + private: + uint32_t m_sessionId; + + Entity::PlayerPtr m_pPlayer; + + uint32_t m_lastDataTime; + + Network::GameConnectionPtr m_pZoneConnection; + + }; + +} + +#endif + diff --git a/src/servers/Server_Zone/StatusEffect.cpp b/src/servers/Server_Zone/StatusEffect.cpp new file mode 100644 index 00000000..33576896 --- /dev/null +++ b/src/servers/Server_Zone/StatusEffect.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "Actor.h" + +#include "StatusEffect.h" + +extern Core::Logger g_log; +extern Core::Data::ExdData g_exdData; + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::ActorPtr sourceActor, Entity::ActorPtr targetActor, + uint32_t duration, uint32_t tickRate ) + : m_id( id ) + , m_sourceActor( sourceActor ) + , m_targetActor( targetActor ) + , m_duration( duration ) + , m_startTime( 0 ) + , m_tickRate( tickRate ) + , m_lastTick( 0 ) +{ + auto& entry = g_exdData.m_statusEffectInfoMap[id]; + m_name = entry.name; + + std::replace( m_name.begin(), m_name.end(), ' ', '_' ); + std::replace( m_name.begin(), m_name.end(), ':', '_' ); + std::replace( m_name.begin(), m_name.end(), '&', '_' ); + std::replace( m_name.begin(), m_name.end(), '+', 'p' ); + boost::erase_all( m_name, "\'" ); + boost::erase_all( m_name, "&" ); + boost::erase_all( m_name, "-" ); + boost::erase_all( m_name, "(" ); + boost::erase_all( m_name, ")" ); +} + + +Core::StatusEffect::StatusEffect::~StatusEffect() +{ +} + + +void Core::StatusEffect::StatusEffect::onTick() +{ + m_lastTick = Util::getTimeMs(); +} + +uint32_t Core::StatusEffect::StatusEffect::getSrcActorId() const +{ + return m_sourceActor->getId(); +} + +uint32_t Core::StatusEffect::StatusEffect::getTargetActorId() const +{ + return m_targetActor->getId(); +} + +void Core::StatusEffect::StatusEffect::applyStatus() +{ + m_startTime = Util::getTimeMs(); + + // this is only right when an action is being used by the player + // else you probably need to use an actorcontrol + + //GamePacketNew< FFXIVIpcEffect > effectPacket( m_sourceActor->getId() ); + //effectPacket.data().targetId = m_sourceActor->getId(); + //effectPacket.data().actionAnimationId = 3; + //effectPacket.data().unknown_3 = 1; + //effectPacket.data().actionTextId = 3; + //effectPacket.data().unknown_5 = 1; + //effectPacket.data().unknown_6 = 321; + //effectPacket.data().rotation = ( uint16_t ) ( 0x8000 * ( ( m_sourceActor->getPos().getR() + 3.1415926 ) ) / 3.1415926 ); + //effectPacket.data().effectTargetId = m_sourceActor->getId(); + //effectPacket.data().effects[4].unknown_1 = 17; + //effectPacket.data().effects[4].bonusPercent = 30; + //effectPacket.data().effects[4].param1 = m_id; + //effectPacket.data().effects[4].unknown_5 = 0x80; + //m_sourceActor->sendToInRangeSet( effectPacket, true ); + + g_log.debug( "StatusEffect applied: " + m_name ); +} + +void Core::StatusEffect::StatusEffect::removeStatus() +{ + g_log.debug( "StatusEffect removed: " + m_name ); +} + +uint32_t Core::StatusEffect::StatusEffect::getId() const +{ + return m_id; +} + +uint32_t Core::StatusEffect::StatusEffect::getDuration() const +{ + return m_duration; +} + +uint32_t Core::StatusEffect::StatusEffect::getTickRate() const +{ + return m_tickRate; +} + +uint64_t Core::StatusEffect::StatusEffect::getLastTickMs() const +{ + return m_lastTick; +} + +uint64_t Core::StatusEffect::StatusEffect::getStartTimeMs() const +{ + return m_startTime; +} + +void Core::StatusEffect::StatusEffect::setLastTick( uint64_t lastTick ) +{ + m_lastTick = lastTick; +} + +const std::string& Core::StatusEffect::StatusEffect::getName() const +{ + return m_name; +} diff --git a/src/servers/Server_Zone/StatusEffect.h b/src/servers/Server_Zone/StatusEffect.h new file mode 100644 index 00000000..7b9d88f7 --- /dev/null +++ b/src/servers/Server_Zone/StatusEffect.h @@ -0,0 +1,51 @@ +#ifndef _STATUSEFFECT_H_ +#define _STATUSEFFECT_H_ + +#include + +#include "Forwards.h" + +namespace Core +{ +namespace StatusEffect +{ + + +class StatusEffect +{ +public: + StatusEffect( uint32_t id, Entity::ActorPtr sourceActor, Entity::ActorPtr targetActor, + uint32_t duration, uint32_t tickRate ); + + ~StatusEffect(); + + void onTick(); + void applyStatus(); + void removeStatus(); + + uint32_t getId() const; + uint32_t getDuration() const; + uint32_t getTickRate() const; + uint32_t getSrcActorId() const; + uint32_t getTargetActorId() const; + uint64_t getLastTickMs() const; + uint64_t getStartTimeMs() const; + void setLastTick( uint64_t lastTick ); + const std::string& getName() const; + +private: + uint32_t m_id; + Entity::ActorPtr m_sourceActor; + Entity::ActorPtr m_targetActor; + uint32_t m_duration; + uint64_t m_startTime; + uint32_t m_tickRate; + uint64_t m_lastTick; + std::string m_name; + +}; + +} +} + +#endif diff --git a/src/servers/Server_Zone/StatusEffectContainer.cpp b/src/servers/Server_Zone/StatusEffectContainer.cpp new file mode 100644 index 00000000..25799233 --- /dev/null +++ b/src/servers/Server_Zone/StatusEffectContainer.cpp @@ -0,0 +1,151 @@ +#include +#include + +#include "Actor.h" +#include "StatusEffect.h" +#include "StatusEffectContainer.h" +#include "ActorControlPacket142.h" +#include "ActorControlPacket143.h" + + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +Core::StatusEffect::StatusEffectContainer::StatusEffectContainer( Entity::ActorPtr pOwner ) + : m_pOwner( pOwner ) +{ + // initialize the free slot queue + for( uint8_t i = 0; i < MAX_EFFECTS; i++ ) + { + m_freeEffectSlotQueue.push( i ); + } +} + +int8_t Core::StatusEffect::StatusEffectContainer::getFreeSlot() +{ + int8_t freeEffectSlot = -1; + + if( m_freeEffectSlotQueue.empty() ) + return freeEffectSlot; + + freeEffectSlot = m_freeEffectSlotQueue.front(); + m_freeEffectSlotQueue.pop(); + + return freeEffectSlot; +} + +void Core::StatusEffect::StatusEffectContainer::freeSlot( uint8_t slotId ) +{ + m_freeEffectSlotQueue.push( slotId ); +} + + +Core::StatusEffect::StatusEffectContainer::~StatusEffectContainer() +{ +} + + +void Core::StatusEffect::StatusEffectContainer::addStatusEffect( StatusEffectPtr pEffect ) +{ + uint8_t nextSlot = getFreeSlot(); + // if there is no slot left, do not add the effect + if( nextSlot == -1 ) + return; + + pEffect->applyStatus(); + m_effectMap[nextSlot] = pEffect; + + GamePacketNew< Server::FFXIVIpcAddStatusEffect > statusEffectAdd( m_pOwner->getId() ); + statusEffectAdd.data().actor_id = m_pOwner->getId(); + statusEffectAdd.data().actor_id1 = m_pOwner->getId(); + statusEffectAdd.data().current_hp = m_pOwner->getHp(); + statusEffectAdd.data().current_mp = m_pOwner->getMp(); + statusEffectAdd.data().current_tp = m_pOwner->getTp(); + statusEffectAdd.data().duration = static_cast< float >( pEffect->getDuration() ) / 1000; + statusEffectAdd.data().effect_id = pEffect->getId(); + statusEffectAdd.data().effect_index = nextSlot; + statusEffectAdd.data().max_hp = m_pOwner->getMaxHp(); + statusEffectAdd.data().max_mp = m_pOwner->getMaxMp(); + statusEffectAdd.data().max_something = 1; + + bool sendToSelf = m_pOwner->isPlayer() ? true : false; + m_pOwner->sendToInRangeSet( statusEffectAdd, sendToSelf ); + +} + +void Core::StatusEffect::StatusEffectContainer::removeStatusEffect( uint8_t effectSlotId ) +{ + auto pEffectIt = m_effectMap.find( effectSlotId ); + if( pEffectIt == m_effectMap.end() ) + return; + + freeSlot( effectSlotId ); + + auto pEffect = pEffectIt->second; + pEffect->removeStatus(); + + bool sendToSelf = m_pOwner->isPlayer() ? true : false; + m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), StatusEffectLose, pEffect->getId() ), sendToSelf ); + + m_effectMap.erase( effectSlotId ); + + sendUpdate(); +} + +void Core::StatusEffect::StatusEffectContainer::sendUpdate() +{ + uint64_t currentTimeMs = Util::getTimeMs(); + + GamePacketNew< Server::FFXIVIpcStatusEffectList > statusEffectList( m_pOwner->getId() ); + + statusEffectList.data().current_hp = m_pOwner->getHp(); + statusEffectList.data().current_mp = m_pOwner->getMp(); + statusEffectList.data().currentTp = m_pOwner->getTp(); + statusEffectList.data().max_hp = m_pOwner->getMaxHp(); + statusEffectList.data().max_mp = m_pOwner->getMaxMp(); + uint8_t slot = 0; + for( auto effectIt : m_effectMap ) + { + float timeLeft = static_cast< float >( effectIt.second->getDuration() - ( currentTimeMs - effectIt.second->getStartTimeMs() ) ) / 1000; + statusEffectList.data().effect[slot].duration = timeLeft; + statusEffectList.data().effect[slot].effect_id = effectIt.second->getId(); + statusEffectList.data().effect[slot].sourceActorId = effectIt.second->getSrcActorId(); + slot++; + } + + bool sendToSelf = m_pOwner->isPlayer() ? true : false; + m_pOwner->sendToInRangeSet( statusEffectList, sendToSelf ); + +} + +void Core::StatusEffect::StatusEffectContainer::update() +{ + uint64_t currentTimeMs = Util::getTimeMs(); + + for( auto effectIt : m_effectMap ) + { + uint8_t effectIndex = effectIt.first; + auto effect = effectIt.second; + + uint64_t lastTick = effect->getLastTickMs(); + uint64_t startTime = effect->getStartTimeMs(); + uint32_t duration = effect->getDuration(); + uint32_t tickRate = effect->getTickRate(); + + if( ( currentTimeMs - startTime ) > duration ) + { + // remove status effect + removeStatusEffect( effectIndex ); + // break because removing invalidates iterators + break; + } + + if( ( currentTimeMs - lastTick ) > tickRate ) + { + effect->setLastTick( currentTimeMs ); + effect->onTick(); + } + + } +} diff --git a/src/servers/Server_Zone/StatusEffectContainer.h b/src/servers/Server_Zone/StatusEffectContainer.h new file mode 100644 index 00000000..5dd20550 --- /dev/null +++ b/src/servers/Server_Zone/StatusEffectContainer.h @@ -0,0 +1,44 @@ +#ifndef _STATUSEFFECTCONTAINER_H_ +#define _STATUSEFFECTCONTAINER_H_ + +#include + +#include +#include + +#include "Forwards.h" + +namespace Core +{ +namespace StatusEffect +{ + +class StatusEffectContainer +{ +public: + StatusEffectContainer( Entity::ActorPtr pOwner ); + ~StatusEffectContainer(); + + void addStatusEffect( StatusEffectPtr pEffect ); + void removeStatusEffect( uint8_t effectSlotId ); + void update(); + + int8_t getFreeSlot(); + void freeSlot( uint8_t slotId ); + + void sendUpdate(); + + +private: + const uint8_t MAX_EFFECTS = 30; + + Entity::ActorPtr m_pOwner; + std::queue< uint8_t > m_freeEffectSlotQueue; + + std::vector< StatusEffectPtr > m_effectList; + std::map< uint8_t, StatusEffectPtr > m_effectMap; +}; + +} +} +#endif diff --git a/src/servers/Server_Zone/UpdateHpMpTpPacket.h b/src/servers/Server_Zone/UpdateHpMpTpPacket.h new file mode 100644 index 00000000..745dbccc --- /dev/null +++ b/src/servers/Server_Zone/UpdateHpMpTpPacket.h @@ -0,0 +1,39 @@ +#ifndef _UPDATEHPMPTP_H +#define _UPDATEHPMPTP_H + +#include +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Ping response packet. +*/ +class UpdateHpMpTpPacket : + public GamePacketNew< FFXIVIpcUpdateHpMpTp > +{ +public: + UpdateHpMpTpPacket( Entity::ActorPtr pActor ) : + GamePacketNew< FFXIVIpcUpdateHpMpTp >( pActor->getId(), pActor->getId() ) + { + initialize( pActor ); + }; + +private: + void initialize( Entity::ActorPtr pActor ) + { + m_data.hp = pActor->getHp(); + m_data.mp = pActor->getMp(); + m_data.tp = pActor->getTp(); + }; +}; + +} +} +} +} + +#endif /*_UPDATEHPMPTP_H*/ \ No newline at end of file diff --git a/src/servers/Server_Zone/Zone.cpp b/src/servers/Server_Zone/Zone.cpp new file mode 100644 index 00000000..0b670779 --- /dev/null +++ b/src/servers/Server_Zone/Zone.cpp @@ -0,0 +1,800 @@ +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Session.h" + +#include "Actor.h" +#include "Player.h" +#include "BattleNpc.h" +#include "Forwards.h" + +#include "GameConnection.h" + +#include "Zone.h" +#include "ZoneMgr.h" +#include "ServerZone.h" +#include "ScriptManager.h" + +#include "CellHandler.h" + +#include + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::ServerZone g_serverZone; +extern Core::Data::ExdData g_exdData; +extern Core::Scripting::ScriptManager g_scriptMgr; + +namespace Core { + +/** +* \brief +*/ +Zone::Zone() + : m_zoneId( 0 ) + , m_layoutId( 0 ) + , m_bPrivate( false ) + , m_type( Common::RegionType::normal ) + , m_currentWeather( static_cast( Common::Weather::Fair ) ) + , m_weatherOverride( 0 ) + , m_lastMobUpdate( 0 ) +{ +} + +Zone::Zone( uint16_t zoneId, uint32_t layoutId, std::string name, std::string interName, bool bPrivate = false ) + : m_type( Common::RegionType::normal ) + , m_currentWeather( static_cast( Common::Weather::Fair ) ) +{ + m_layoutId = layoutId; + + m_zoneId = zoneId; + m_zoneCode = name; + m_zoneName = interName; + m_bPrivate = bPrivate; + m_lastMobUpdate = 0; + + m_currentWeather = getNextWeather(); + m_weatherOverride = 0; +} + +Zone::~Zone() +{ +} + +bool Zone::init() +{ + memset( m_pCellCache, 0, sizeof( CellCache* ) * _sizeX ); + + if( g_scriptMgr.onZoneInit( shared_from_this() ) ) + { + // all good + } + + loadCellCache(); + + return true; +} + +bool Zone::isPrivateZone() const +{ + return m_bPrivate; +} + +void Zone::setWeatherOverride( uint8_t weather ) +{ + m_weatherOverride = weather; +} + +uint8_t Zone::getCurrentWeather() const +{ + return m_currentWeather; +} + +CellCache* Zone::getCellCacheList( uint32_t cellx, uint32_t celly ) +{ + assert( cellx < _sizeX ); + assert( celly < _sizeY ); + if( m_pCellCache[cellx] == nullptr ) + return nullptr; + + return m_pCellCache[cellx][celly]; +} + +CellCache* Zone::getCellCacheAndCreate( uint32_t cellx, uint32_t celly ) +{ + assert( cellx < _sizeX ); + assert( celly < _sizeY ); + if( m_pCellCache[cellx] == nullptr ) + { + m_pCellCache[cellx] = new CellCache*[_sizeY]; + memset(m_pCellCache[cellx], 0, sizeof( CellCache* ) * _sizeY); + } + + if( m_pCellCache[cellx][celly] == nullptr ) + { + //m_pCellCache[cellx][celly] = new CellCache; + } + + return m_pCellCache[cellx][celly]; +} + +void Zone::loadCellCache() +{ + auto pQR = g_database.query( "SELECT Id," + "Zoneid," + "NameId," + "SizeId," + "ClassJob," + "DisplayFlags1," + "DisplayFlags2," + "Level," + "Pos_0_0," + "Pos_0_1," + "Pos_0_2," + "Rotation," + "MobType," + "Behaviour," + "ModelMainWeapon," + "ModelSubWeapon," + "ModelId," + "Look," + "Models," + "type " + "FROM battlenpc WHERE ZoneId = " + std::to_string( getId() ) + ";" ); + + if( !pQR ) + return; + + std::vector< Entity::BattleNpcPtr > cache; + + do + { + Db::Field *field = pQR->fetch(); + uint32_t id = field[0].getUInt32(); + uint32_t targetZoneId = field[1].getUInt32(); + uint32_t nameId = field[2].getUInt32(); + uint32_t sizeId = field[3].getUInt32(); + uint32_t classJob = field[4].getUInt32(); + uint32_t displayFlags1 = field[5].getUInt32(); + uint32_t displayFlags2 = field[6].getUInt32(); + uint32_t level = field[7].getUInt32(); + float posX = field[8].getFloat(); + float posY = field[9].getFloat(); + float posZ = field[10].getFloat(); + uint32_t rotation = field[11].getUInt32(); + uint32_t mobType = field[12].getUInt32(); + uint32_t behaviour = field[13].getUInt32(); + uint64_t modelMainWeapon = field[14].getUInt32(); + uint64_t modelSubWeapon = field[15].getUInt32(); + uint32_t modelId = field[16].getUInt32(); + uint32_t type = field[17].getUInt32(); + + + Common::FFXIVARR_POSITION3 pos; + pos.x = posX; + pos.y = posY; + pos.z = posZ; + Entity::BattleNpcPtr pBNpc( new Entity::BattleNpc( modelId, nameId, + pos, + sizeId, type, level, behaviour, mobType ) ); + pBNpc->setRotation( rotation ); + cache.push_back( pBNpc ); + + //pushActor( pBNpc ); + + //m_zonePositionMap[id] = ZonePositionPtr( new ZonePosition( id, targetZoneId, Position( posX, posY, posZ, posO ), radius ) ); + + } while( pQR->nextRow() ); + + + + for( auto entry : cache ) + { + // get cell position + uint32_t cellX = CellHandler::getPosX( entry->getPos().x ); + uint32_t cellY = CellHandler::getPosY( entry->getPos().z ); + + // find the right cell, create it if not existing yet + if( m_pCellCache[cellX] == nullptr ) + { + m_pCellCache[cellX] = new CellCache*[_sizeY]; + memset( m_pCellCache[cellX], 0, sizeof( CellCache* ) * _sizeY ); + } + + if( !m_pCellCache[cellX][cellY] ) + m_pCellCache[cellX][cellY] = new CellCache; + + // add the populace cache object to the cells list + m_pCellCache[cellX][cellY]->battleNpcCache.push_back( entry ); + } + +} + +uint8_t Zone::getNextWeather() +{ + auto zoneInfo = g_exdData.m_zoneInfoMap[ getLayoutId() ]; + + uint32_t unix = static_cast< uint32_t >( time( nullptr ) ); + // Get Eorzea hour for weather start + uint32_t bell = unix / 175; + // Do the magic 'cause for calculations 16:00 is 0, 00:00 is 8 and 08:00 is 16 + int32_t increment = ( ( bell + 8 - ( bell % 8 ) ) ) % 24; + + // Take Eorzea days since unix epoch + uint32_t totalDays = ( unix / 4200 ); + + uint32_t calcBase = ( totalDays * 0x64 ) + increment; + + uint32_t step1 = ( calcBase << 0xB ) ^ calcBase; + uint32_t step2 = ( step1 >> 8 ) ^ step1; + + uint8_t rate = static_cast< uint8_t >(step2 % 0x64); + + for( auto entry : zoneInfo.weather_rate_map ) + { + uint8_t sRate = entry.first; + int32_t weatherId = entry.second; + + if( rate <= sRate ) + return weatherId; + } + + return 1; +} + +void Zone::pushActor( Entity::ActorPtr pActor ) +{ + float mx = pActor->getPos().x; + float my = pActor->getPos().z; + uint32_t cx = getPosX( mx ); + uint32_t cy = getPosY( my ); + + Cell* pCell = getCell( cx, cy ); + if( !pCell ) + { + pCell = create( cx, cy ); + pCell->init( cx, cy, shared_from_this() ); + } + + pCell->addActor( pActor ); + + pActor->setCell( pCell ); + + uint32_t cellX = getPosX( pActor->getPos().x ); + uint32_t cellY = getPosY( pActor->getPos().z ); + + uint32_t endX = cellX <= _sizeX ? cellX + 1 : ( _sizeX - 1 ); + uint32_t endY = cellY <= _sizeY ? cellY + 1 : ( _sizeY - 1 ); + uint32_t startX = cellX > 0 ? cellX - 1 : 0; + uint32_t startY = cellY > 0 ? cellY - 1 : 0; + uint32_t posX, posY; + + for( posX = startX; posX <= endX; ++posX ) + { + for( posY = startY; posY <= endY; ++posY ) + { + pCell = getCell( posX, posY ); + if( pCell ) + updateInRangeSet( pActor, pCell ); + } + } + + if( pActor->isPlayer() ) + { + g_log.debug( "[Zone:" + m_zoneCode + "] Adding player [" + std::to_string( pActor->getId() ) + "]" ); + auto pPlayer = pActor->getAsPlayer(); + + // fire the onEnter Lua event + //LuaManager->onRegionEnter(this, pPlayer); + + auto pSession = g_serverZone.getSession( pPlayer->getId() ); + if( pSession ) + m_sessionSet.insert( pSession ); + m_playerMap[pPlayer->getId()] = pPlayer; + updateCellActivity( cx, cy, 2 ); + + } + else if( pActor->isMob() ) + { + + Entity::BattleNpcPtr pBNpc = pActor->getAsBattleNpc(); + m_BattleNpcMap[pBNpc->getId()] = pBNpc; + pBNpc->setPosition( pBNpc->getPos() ); + + } + +} + +void Zone::removeActor( Entity::ActorPtr pActor ) +{ + + + if( pActor->m_pCell ) + { + pActor->m_pCell->removeActor( pActor ); + pActor->m_pCell = nullptr; + } + + if( pActor->isPlayer() ) + { + + g_log.debug( "[Zone:" + m_zoneCode + "] Removing player [" + std::to_string( pActor->getId() ) + "]" ); + // If it's a player and he's inside boundaries - update his nearby cells + if( pActor->getPos().x <= _maxX && pActor->getPos().x >= _minX && + pActor->getPos().z <= _maxY && pActor->getPos().z >= _minY ) + { + uint32_t x = getPosX( pActor->getPos().x ); + uint32_t y = getPosY( pActor->getPos().z ); + updateCellActivity( x, y, 3 ); + } + m_playerMap.erase( pActor->getId() ); + + } + else if( pActor->isMob() ) + m_BattleNpcMap.erase( pActor->getId() ); + + // remove from lists of other actors + if( pActor->hasInRangeActor() ) + { + Entity::ActorPtr pCurAct; + + for( auto iter = pActor->m_inRangeActors.begin(); iter != pActor->m_inRangeActors.end();) + { + pCurAct = *iter; + auto iter2 = iter++; + pCurAct->removeInRangeActor( pActor ); + } + } + pActor->clearInRangeSet(); + +} + +void Zone::queueOutPacketForRange( Entity::PlayerPtr pSourcePlayer, uint32_t range, Network::Packets::GamePacketPtr pPacketEntry ) +{ + for( auto it = m_playerMap.begin(); it != m_playerMap.end(); ++it ) + { + float distance = Math::Util::distance( pSourcePlayer->getPos().x, + pSourcePlayer->getPos().y, + pSourcePlayer->getPos().z, + ( *it ).second->getPos().x, + ( *it ).second->getPos().y, + ( *it ).second->getPos().z ); + + if( ( distance < range ) && pSourcePlayer->getId() != ( *it ).second->getId() ) + { + auto pSession = g_serverZone.getSession( ( *it ).second->getId() ); + pPacketEntry->setValAt( 0x08, ( *it ).second->getId() ); + if( pSession ) + pSession->getZoneConnection()->queueOutPacket( pPacketEntry ); + } + } +} + +uint32_t Zone::getId() +{ + return m_zoneId; +} + +Common::RegionType Zone::getType() const +{ + return m_type; +} + +uint16_t Zone::getLayoutId() const +{ + return m_layoutId; +} + +bool Zone::isInstance() const +{ + return m_type == Common::RegionType::instance; +} + +const std::string& Zone::getName() const +{ + return m_zoneName; +} + +const std::string& Zone::getInternalName() const +{ + return m_zoneCode; +} + +std::size_t Zone::getPopCount() const +{ + return m_playerMap.size(); +} + +bool Zone::checkWeather() +{ + if ( m_weatherOverride != 0 ) + { + if ( m_weatherOverride != m_currentWeather ) + { + m_currentWeather = m_weatherOverride; + g_log.debug( "[Zone:" + m_zoneCode + "] overriding weather to : " + std::to_string( m_weatherOverride ) ); + return true; + } + } + else + { + auto nextWeather = getNextWeather(); + if ( nextWeather != m_currentWeather ) + { + m_currentWeather = nextWeather; + g_log.debug( "[Zone:" + m_zoneCode + "] changing weather to : " + std::to_string( nextWeather ) ); + return true; + } + } + return false; +} + +void Zone::updateBnpcs( int64_t tickCount ) +{ + if( ( tickCount - m_lastMobUpdate ) > 250 ) + { + + m_lastMobUpdate = tickCount; + uint32_t currTime = static_cast< uint32_t >( time( nullptr ) ); + + for( auto it3 = m_BattleNpcDeadMap.begin(); it3 != m_BattleNpcDeadMap.end(); ++it3 ) + { + + Entity::BattleNpcPtr pBNpc = *it3; + + if( ( currTime - pBNpc->getTimeOfDeath() ) > 60 ) + { + + pBNpc->resetHp(); + pBNpc->resetMp(); + pBNpc->resetPos(); + pushActor( pBNpc ); + + m_BattleNpcDeadMap.erase( it3 ); + + break; + } + } + + + for( auto entry : m_BattleNpcMap ) + { + Entity::BattleNpcPtr pBNpc = entry.second; + + if( !pBNpc ) + continue; + + if( !pBNpc->isAlive() && currTime - pBNpc->getTimeOfDeath() > ( 10 ) ) + { + removeActor( pBNpc ); + m_BattleNpcDeadMap.insert( pBNpc ); + break; + } + + pBNpc->update( tickCount ); + + } + } +} + +bool Zone::runZoneLogic() +{ + int64_t tickCount = Util::getTimeMs(); + + bool changedWeather = checkWeather(); + + auto it = m_sessionSet.begin(); + + // update sessions in this zone + for( ; it != m_sessionSet.end(); ) + { + + auto pSession = ( *it ); + + if( !pSession ) + { + it = m_sessionSet.erase( it ); + continue; + } + + // this session is not linked to this area anymore, remove it from zone session list + if( ( !pSession->getPlayer()->getCurrentZone() ) || ( pSession->getPlayer()->getCurrentZone() != shared_from_this() ) ) + { + g_log.debug( "[Zone:" + m_zoneCode + "] removing session " + std::to_string( pSession->getId() ) ); + + if( pSession->getPlayer()->getCell() ) + removeActor( pSession->getPlayer() ); + + it = m_sessionSet.erase( it ); + continue; + } + + if( changedWeather ) + { + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcWeatherChange > weatherChangePacket( pSession->getPlayer()->getId() ); + weatherChangePacket.data().weatherId = m_currentWeather; + weatherChangePacket.data().delay = 5.0f; + pSession->getPlayer()->queuePacket( weatherChangePacket ); + } + + // perform session duties + pSession->update(); + ++it; + } + + updateBnpcs( tickCount ); + + return true; +} + +bool Zone::isCellActive( uint32_t x, uint32_t y ) +{ + uint32_t endX = ( ( x + 1 ) <= _sizeX ) ? x + 1 : ( _sizeX - 1 ); + uint32_t endY = ( ( y + 1 ) <= _sizeY ) ? y + 1 : ( _sizeY - 1 ); + uint32_t startX = x > 0 ? x - 1 : 0; + uint32_t startY = y > 0 ? y - 1 : 0; + uint32_t posX; + uint32_t posY; + + Cell* pCell; + + for( posX = startX; posX <= endX; posX++ ) + { + for( posY = startY; posY <= endY; posY++ ) + { + pCell = getCell( posX, posY ); + + if( pCell && ( pCell->hasPlayers() || pCell->isForcedActive() ) ) + return true; + } + } + + return false; +} + +void Zone::updateCellActivity( uint32_t x, uint32_t y, int radius ) +{ + + uint32_t endX = ( x + radius ) <= _sizeX ? x + radius : ( _sizeX - 1 ); + uint32_t endY = ( y + radius ) <= _sizeY ? y + radius : ( _sizeY - 1 ); + uint32_t startX = x - radius > 0 ? x - radius : 0; + uint32_t startY = y - radius > 0 ? y - radius : 0; + uint32_t posX, posY; + + Cell* pCell; + + for( posX = startX; posX <= endX; posX++ ) + { + for( posY = startY; posY <= endY; posY++ ) + { + pCell = getCell( posX, posY ); + + if( !pCell ) + { + if( isCellActive( posX, posY ) ) + { + pCell = create( posX, posY ); + pCell->init( posX, posY, shared_from_this() ); + + pCell->setActivity( true ); + + assert( !pCell->isLoaded() ); + + CellCache * pCC = getCellCacheAndCreate( posX, posY ); + if( pCC ) + pCell->loadActors( pCC ); + } + } + else + { + //Cell is now active + if( isCellActive( posX, posY ) && !pCell->isActive() ) + { + pCell->setActivity( true ); + + if( !pCell->isLoaded() ) + { + CellCache * pCC = getCellCacheAndCreate( posX, posY ); + if( pCC ) + pCell->loadActors( pCC ); + } + } + else if( !isCellActive( posX, posY ) && pCell->isActive() ) + pCell->setActivity( false ); + } + } + } +} + +void Zone::changeActorPosition( Entity::ActorPtr pActor ) +{ + + if( pActor->getCurrentZone() != shared_from_this() ) + return; + + if( pActor->hasInRangeActor() ) + { + Entity::ActorPtr pCurAct; + + float fRange = 70.0f; + for( auto iter = pActor->m_inRangeActors.begin(); iter != pActor->m_inRangeActors.end();) + { + pCurAct = *iter; + auto iter2 = iter++; + + float distance = Math::Util::distance( pCurAct->getPos().x, + pCurAct->getPos().y, + pCurAct->getPos().z, + pActor->getPos().x, + pActor->getPos().y, + pActor->getPos().z ); + + if( fRange > 0.0f && distance > fRange ) + { + pCurAct->removeInRangeActor( pActor ); + + if( pActor->getCurrentZone() != shared_from_this() ) + return; + + pActor->removeInRangeActor( *iter2 ); + + // @TODO FIXME! + // this break is more or less a hack, iteration will break otherwise after removing + break; + } + } + } + + uint32_t cellX = getPosX( pActor->getPos().x ); + uint32_t cellY = getPosY( pActor->getPos().z ); + + if( cellX >= _sizeX || cellY >= _sizeY ) + { + return; + } + + Cell* pCell = getCell( cellX, cellY ); + Cell* pOldCell = pActor->m_pCell; + if( !pCell ) + { + pCell = create( cellX, cellY ); + pCell->init( cellX, cellY, shared_from_this() ); + } + + // If object moved cell + if( pCell != pOldCell ) + { + + if( pOldCell ) + pOldCell->removeActor( pActor ); + + pCell->addActor( pActor ); + pActor->m_pCell = pCell; + + // if player we need to update cell activity + // radius = 2 is used in order to update both + // old and new cells + if( pActor->isPlayer() ) + { + updateCellActivity( cellX, cellY, 2 ); + if( pOldCell != nullptr ) + { + // only do the second check if theres -/+ 2 difference + if( abs( ( int ) cellX - ( int ) pOldCell->m_posX ) > 2 || + abs( ( int ) cellY - ( int ) pOldCell->m_posY ) > 2 ) + updateCellActivity( pOldCell->m_posX, pOldCell->m_posY, 2 ); + } + } + } + + // update in range actor set + uint32_t endX = cellX <= _sizeX ? cellX + 1 : ( _sizeX - 1 ); + uint32_t endY = cellY <= _sizeY ? cellY + 1 : ( _sizeY - 1 ); + uint32_t startX = cellX > 0 ? cellX - 1 : 0; + uint32_t startY = cellY > 0 ? cellY - 1 : 0; + uint32_t posX, posY; + + for( posX = startX; posX <= endX; ++posX ) + { + for( posY = startY; posY <= endY; ++posY ) + { + pCell = getCell( posX, posY ); + if( pCell ) + updateInRangeSet( pActor, pCell ); + } + } +} + + +void Zone::updateInRangeSet( Entity::ActorPtr pActor, Cell* pCell ) +{ + if( pCell == nullptr ) + return; + + Entity::ActorPtr pCurAct; + + auto iter = pCell->m_actors.begin(); + + float fRange = 70.0f; + int count = 0; + while( iter != pCell->m_actors.end() ) + { + pCurAct = *iter; + ++iter; + + if( !pCurAct ) + continue; + + float distance = Math::Util::distance( pCurAct->getPos().x, + pCurAct->getPos().y, + pCurAct->getPos().z, + pActor->getPos().x, + pActor->getPos().y, + pActor->getPos().z ); + + // Add if we are not ourself and range == 0 or distance is withing range. + if( pCurAct != pActor && ( fRange == 0.0f || distance <= fRange ) ) + { + + if( pActor->isInRangeSet( pCurAct ) ) + // Actor already in range set, skip + continue; + + if( pActor->isPlayer() ) + { + auto pOwnPlayer = pActor->getAsPlayer(); + + if( !pOwnPlayer->isLoadingComplete() ) + continue; + + count++; + if( count > 15 ) + break; + + pActor->addInRangeActor( pCurAct ); + pCurAct->addInRangeActor( pActor ); + // spawn the actor for the player + pCurAct->spawn( pOwnPlayer ); + + if( pCurAct->isPlayer() ) + { + auto pPlayer = pCurAct->getAsPlayer(); + if( !pPlayer->isLoadingComplete() ) + continue; + + pActor->spawn( pPlayer ); + } + + } + else if( pActor->isMob() && pCurAct->isPlayer() && pActor->isAlive() ) + { + auto pPlayer = pCurAct->getAsPlayer(); + if( pPlayer->isLoadingComplete() ) + { + pActor->spawn( pPlayer ); + pCurAct->addInRangeActor( pActor ); + pActor->addInRangeActor( pCurAct ); + } + } + else + { + pActor->addInRangeActor( pCurAct ); + pCurAct->addInRangeActor( pActor ); + } + } + } +} + +} diff --git a/src/servers/Server_Zone/Zone.h b/src/servers/Server_Zone/Zone.h new file mode 100644 index 00000000..a1f6a599 --- /dev/null +++ b/src/servers/Server_Zone/Zone.h @@ -0,0 +1,117 @@ +#ifndef _ZONE_H +#define _ZONE_H + +#include +#include + +#include "Cell.h" +#include "CellHandler.h" + +#include "Forwards.h" + +#include +#include + +#include +#include +namespace Core { + +namespace Entity +{ +class Actor; +class Player; +} + +class Session; + +class ZonePosition; + +typedef std::set< SessionPtr > SessionSet; + +class Zone : public CellHandler< Cell >, public boost::enable_shared_from_this< Zone > +{ +protected: + uint32_t m_zoneId; + uint32_t m_layoutId; + + std::string m_zoneName; + std::string m_zoneCode; + + bool m_bPrivate; + + std::unordered_map m_playerMap; + std::unordered_map m_BattleNpcMap; + + std::set< Entity::BattleNpcPtr > m_BattleNpcDeadMap; + + SessionSet m_sessionSet; + + CellCache** m_pCellCache[_sizeX]; + + Common::RegionType m_type; + + uint8_t m_currentWeather; + uint8_t m_weatherOverride; + + uint64_t m_lastMobUpdate; + +public: + Zone(); + + Zone( uint16_t zoneId, uint32_t layoutId, std::string name, std::string interName, bool bPrivate ); + virtual ~Zone(); + + bool init(); + + bool isPrivateZone() const; + + /*! overrides the zone's weather, set to 0 to unlock */ + void setWeatherOverride( uint8_t weather ); + + uint8_t getCurrentWeather() const; + + CellCache* getCellCacheList( uint32_t cellx, uint32_t celly ); + + CellCache* getCellCacheAndCreate( uint32_t cellx, uint32_t celly ); + + virtual void loadCellCache(); + + uint8_t getNextWeather(); + + void pushActor( Entity::ActorPtr pActor ); + + void removeActor( Entity::ActorPtr pActor ); + + void changeActorPosition( Entity::ActorPtr pActor ); + + bool isCellActive( uint32_t x, uint32_t y ); + + void updateCellActivity( uint32_t x, uint32_t y, int radius ); + + void updateInRangeSet( Entity::ActorPtr pActor, Cell* pCell ); + + void queueOutPacketForRange( Entity::PlayerPtr pSourcePlayer, uint32_t range, Network::Packets::GamePacketPtr pPacketEntry ); + + virtual uint32_t getId(); + + Common::RegionType getType() const; + + uint16_t getLayoutId() const; + + bool isInstance() const; + + const std::string& getName() const; + + const std::string& getInternalName() const; + + std::size_t getPopCount() const; + bool checkWeather(); + void updateBnpcs( int64_t tickCount ); + + bool runZoneLogic(); + +}; + +} + +#endif diff --git a/src/servers/Server_Zone/ZoneMgr.cpp b/src/servers/Server_Zone/ZoneMgr.cpp new file mode 100644 index 00000000..eeb610f5 --- /dev/null +++ b/src/servers/Server_Zone/ZoneMgr.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include "ZoneMgr.h" +#include "Zone.h" + +#include "ZonePosition.h" + + +extern Core::Logger g_log; +extern Core::Db::Database g_database; +extern Core::Data::ExdData g_exdData; + +namespace Core { + + ZoneMgr::ZoneMgr() + { + } + + ZoneMgr::~ZoneMgr() + { + } + + void ZoneMgr::loadZonePositionMap() + { + auto pQR = g_database.query( "SELECT id, target_zone_id, pos_x, pos_y, pos_z, pos_o, radius " \ + "FROM dbzonepositions;" ); + + if( !pQR ) + return; + + do + { + Db::Field *field = pQR->fetch(); + uint32_t id = field[0].getUInt32(); + uint32_t targetZoneId = field[1].getUInt32(); + Common::FFXIVARR_POSITION3 pos; + pos.x = field[2].getFloat(); + pos.y = field[3].getFloat(); + pos.z = field[4].getFloat(); + float posO = field[5].getFloat(); + uint32_t radius = field[6].getUInt32(); + + m_zonePositionMap[id] = ZonePositionPtr( new ZonePosition( id, targetZoneId, pos, radius, posO ) ); + + } while( pQR->nextRow() ); + } + + ZonePositionPtr ZoneMgr::getZonePosition( uint32_t zonePositionId ) + { + auto it = m_zonePositionMap.find( zonePositionId ); + + if( it != m_zonePositionMap.end() ) + return it->second; + + return nullptr; + } + + bool ZoneMgr::createZones() + { + loadZonePositionMap(); + + // find zone info from exd + for( auto zone : g_exdData.m_zoneInfoMap ) + { + uint32_t zoneId = zone.first; + + + auto info = zone.second; + g_log.Log( LoggingSeverity::info, std::to_string( info.id ) + "\t" + info.zone_str ); + + ZonePtr pZone( new Zone( info.id, info.layout_id, info.zone_name, info.zone_str, false ) ); + pZone->init(); + m_zoneMap[info.id] = pZone; + } + + //do + //{ + // Db::Field *field = pQR->fetch(); + // uint16_t id = field[0].getUInt16(); + // std::string inName = field[1].getString(); + // std::string name = field[2].getString(); + // uint32_t layoutId = field[3].getUInt32(); + // bool isPrivate = field[4].getBool(); + + // if(!isPrivate) + // { + // g_log.Log(LoggingSeverity::info, std::to_string(id) + "\t" + inName + " - " + name); + + // ZonePtr pZone( new Zone( id, layoutId, name, inName, isPrivate ) ); + + // m_zoneMap[id] = pZone; + + // // start the region worker + // // ThreadPool->executeTask(pRegion); + // } + // else + // { + // //Console->outTime(" --> %s", inName.c_str()); + // //Console->outTime("\tCached private instance...", name.c_str()); + + // //// write the instance data into the instance cache for later use + // //InstanceCacheEntry * pICE = new InstanceCacheEntry(); + // //pICE->id = id; + // //pICE->inName = inName; + // //pICE->minX = minX; + // //pICE->maxX = maxX; + // //pICE->minY = minY; + // //pICE->maxY = maxY; + // //pICE->name = name; + // //pICE->layoutId = layoutId; + // //pICE->isPrivate = isPrivate; + + // //m_instanceCache[pICE->id] = pICE; + // //m_instanceCacheName[inName] = pICE; + + // //createInstance(pICE); + // } + + //} while(pQR->nextRow()); + + return true; + } + + void ZoneMgr::updateZones() + { + for( auto zone : m_zoneMap ) + { + zone.second->runZoneLogic(); + } + } + + ZonePtr ZoneMgr::getZone( uint32_t zoneId ) + { + ZoneMap::iterator it; + it = m_zoneMap.find( zoneId ); + + if( it != m_zoneMap.end() ) + return it->second; + + return nullptr; + } + +} diff --git a/src/servers/Server_Zone/ZoneMgr.h b/src/servers/Server_Zone/ZoneMgr.h new file mode 100644 index 00000000..6be7e5ce --- /dev/null +++ b/src/servers/Server_Zone/ZoneMgr.h @@ -0,0 +1,39 @@ +#ifndef _ZONEMGR_H +#define _ZONEMGR_H + + +#include +#include +#include "Forwards.h" + +namespace Core { + + typedef std::unordered_map ZoneMap; + + class ZoneMgr + { + public: + ZoneMgr(); + ~ZoneMgr(); + + bool createZones(); + + ZonePtr getZone( uint32_t zoneId ); + + void loadZonePositionMap(); + + ZonePositionPtr getZonePosition( uint32_t zonePositionId ); + + void updateZones(); + + private: + ZoneMap m_zoneMap; + + std::unordered_map m_zonePositionMap; + + }; + +} + +#endif + diff --git a/src/servers/Server_Zone/ZonePosition.cpp b/src/servers/Server_Zone/ZonePosition.cpp new file mode 100644 index 00000000..a5eb7c08 --- /dev/null +++ b/src/servers/Server_Zone/ZonePosition.cpp @@ -0,0 +1,43 @@ +#include "ZonePosition.h" + +Core::ZonePosition::ZonePosition() + : m_id(0) + , m_targetZoneId(0) + , m_radius(0) +{ +} + +Core::ZonePosition::ZonePosition( uint32_t id, uint32_t targetZoneId, const Core::Common::FFXIVARR_POSITION3& targetPosition, uint32_t radius, float rotation ) +{ + m_id = id; + m_targetZoneId = targetZoneId; + m_targetPos = targetPosition; + m_radius = radius; + m_rotation = rotation; +} + +Core::ZonePosition::~ZonePosition() +{ +} + +uint32_t Core::ZonePosition::getId() const +{ + return m_id; +} + +uint32_t Core::ZonePosition::getTargetZoneId() const +{ + return m_targetZoneId; +} + +const Core::Common::FFXIVARR_POSITION3 & Core::ZonePosition::getTargetPosition() const +{ + return m_targetPos; +} + +float Core::ZonePosition::getTargetRotation() const +{ + return m_rotation; +} + + diff --git a/src/servers/Server_Zone/ZonePosition.h b/src/servers/Server_Zone/ZonePosition.h new file mode 100644 index 00000000..0b4cad7c --- /dev/null +++ b/src/servers/Server_Zone/ZonePosition.h @@ -0,0 +1,33 @@ +#ifndef _ZONELINE_H +#define _ZONELINE_H + +#include + +namespace Core { + +class ZonePosition +{ +protected: + uint32_t m_id; + uint32_t m_targetZoneId; + Common::FFXIVARR_POSITION3 m_targetPos; + float m_rotation; + uint32_t m_radius; + +public: + ZonePosition(); + ZonePosition( uint32_t id, uint32_t targetZoneId, const Common::FFXIVARR_POSITION3& targetPosition, uint32_t radius, float rotation ); + virtual ~ZonePosition(); + + uint32_t getId() const; + + uint32_t getTargetZoneId() const; + + const Common::FFXIVARR_POSITION3& getTargetPosition() const; + + float getTargetRotation() const; + +}; + +} +#endif diff --git a/src/servers/Server_Zone/mainGameServer.cpp b/src/servers/Server_Zone/mainGameServer.cpp new file mode 100644 index 00000000..6a9bcdfc --- /dev/null +++ b/src/servers/Server_Zone/mainGameServer.cpp @@ -0,0 +1,24 @@ +#include + +#include "ServerZone.h" + +Core::ServerZone g_serverZone( "config/settings_zone.xml" ); + +int main( int argc, char* argv[] ) +{ + // i hate to do this, but we need to set this first... + for(auto i = 1; i < argc; ++i ) + { + std::string arg( argv[i] ); + + // trim '-' from start of arg + arg = arg.erase( 0, arg.find_first_not_of( '-' ) ); + if( arg == "sId" && argc > i + 1 ) + { + g_serverZone.setServerId( std::atol( argv[i + 1] ) ); + break; + } + } + g_serverZone.run( argc, argv ); + return 0; +} \ No newline at end of file diff --git a/src/tools/Script/ce_dump_vfs_path_357.lua b/src/tools/Script/ce_dump_vfs_path_357.lua new file mode 100644 index 00000000..3764d4c6 --- /dev/null +++ b/src/tools/Script/ce_dump_vfs_path_357.lua @@ -0,0 +1,231 @@ +-- init global variable +g_dump = io.open(os.date("dump_%Y-%m-%d-%H-%M-%S.txt"), "a") +g_dump:write("ptr, name\n") +g_count = 0 + +-- relative virtual address(rva) to register_whatever function +-- this assume ffxiv sb benchmark +-- YOU MUST EDIT THIS TO RIGHT VALUE TO WORK +-- check http://imgur.com/a/nJCef for disasm +g_addr = {} + +g_override_path = { + -- {"01_roc_r2", "01_gyr_g3"}, + -- {} + -- {"ex1/01_roc_r2/twn/r2t2/level/r2t2", "ex2/01_gyr_g3/twn/g3t2/level/g3t2"}, + -- {"ex1/01_roc_r2/twn/r2t2", "ex2/01_gyr_g3/twn/g3t2"}, + + -- + -- {"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/fld/e3f1/level/e3f1"}, + -- {"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/fld/e3f1"}, + + -- kugane + {"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/twn/e3t1/level/e3t1"}, + {"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/twn/e3t1"}, + + -- last cutscene dungeon ex2/02_est_e3/dun/e3d1 + --{"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/dun/e3d1/level/e3d1"}, + --{"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/dun/e3d1"}, + +} + +-- g_addr.load_master = 0x162210 -- looks like below function is called from this +g_addr.load1 = 0x186BB0 -- mostly loads gfx stuff but does calc in it? +g_addr.load2 = 0x186CE0 -- mostly loads gfx stuff +g_addr.load3 = 0x1868A0 -- mostly loads exd data +g_addr.load4 = 0x186E00 -- from this on found it because there are next to each other +g_addr.load5 = 0x186EE0 -- umm, vfx? +g_addr.kugane_workaround = 0xE61416 + +g_loadseen = {} + +g_xiv = {} +g_xiv.proc_name = "ffxiv_dx11.exe" +g_xiv.path = "C:\\Users\\Mino\\Games\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\" .. g_xiv.proc_name +-- because I don't want to login and out every single try +g_xiv.arg = "DEV.TestSID=3a3f77cc5f7c88136d4a66e36bd27f8535d773d0378b521275468785 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=1 DEV.GMServerHost=localhost" +g_mode = "attach" + +function init() + -- attach debugger + if g_mode == "create" then + print(string.format("Launching ffxiv.exe w/ arg %s", g_xiv.arg)) + createProcess(g_xiv.path, g_xiv.arg, true, true) + elseif g_mode == "attach" then + print("Looking for "..g_xiv.proc_name) + while not openProcess(g_xiv.proc_name) do sleep(1) end + print("Attaching...") + debugProcess() + while not getAddress(g_xiv.proc_name) do sleep(1) end + print("Module loaded") + else + print("Unsupported mode!") + return + end + + for k, v in pairs(g_addr) do + -- k = name + -- v = rva + print(string.format("Attaching %s breakpoint on 0x%X", k, v)) + debug_setBreakpoint(get_va(v)) + end + + print("Now waiting for breakpoints..") +end + +function debugger_onBreakpoint() + if RIP == get_va(g_addr.load1) then + -- dump_message("1>>") + local new_addr = dump_addr(R8) + if new_addr then + R8 = new_addr + end + elseif RIP == get_va(g_addr.load2) then + -- dump_message("2>>") + local new_addr = dump_addr(R8) + if new_addr then + R8 = new_addr + end + elseif RIP == get_va(g_addr.load3) then + local new_addr = dump_addr(RSP + 28) + if new_addr then + writeQword(RSP + 28, new_addr) + end + elseif RIP == get_va(g_addr.load4) then + -- dump_message("4>>") + local new_addr = dump_addr(RCX) + if new_addr then + RCX = new_addr + end + elseif RIP == get_va(g_addr.load5) then + -- dump_message("5>>") + local new_addr = dump_addr(RCX) + if new_addr then + RCX = new_addr + end + elseif RIP == get_va(g_addr.kugane_workaround) then + if RAX == 0 then + -- jump and hope it doesn't cause a UB + print("Workaround hack triggered") + RIP = get_va(0xE61432) + end + else + -- user bp, update gui on ce, but who gives a shit? + -- continue execution anyway + debug_continueFromBreakpoint("co_run") + return 0 + end + + -- something is missing on the text file because it takes ageeeeeeeeees to write to file + -- let just wait it + + + -- continue execution + debug_continueFromBreakpoint("co_run") + --return 0 -- update gui + return 1 +end + +function dump_addr(addr) + local vfspath = readString(addr, 256) + + if not g_loadseen[vfspath] then + g_loadseen[vfspath] = true -- set seen flag + + local message = string.format("%X, %s", addr, vfspath) + dump_line(message) + end + + if vfspath then + return replace_map_string(addr, vfspath) + end + + return nil +end + +function dump_line(message) + dump_message(message .. "\n") +end + +function dump_message(message) + g_dump:write(message) + g_dump:flush() + print(message) +end + +-- mod_base + rva = va +function get_va(rva) + return getAddress(g_xiv.proc_name) + rva +end + +-- rva = va - mod_base +function get_rva(va) + return va - getAddress(g_xiv.proc_name) +end + +-- check mod_base <= va <= mod_base + mod_size +function is_mmod_addr(va) + local mod_base = getAddress(g_xiv.proc_name) + local mod_size = getModuleSize(g_xiv.proc_name) + + if mod_base <= va and va <= mod_base + mod_size then + return true + end + + return false +end + +function replace_map_string(addr, source) + -- try to replace map strnig + for _, v in ipairs(g_override_path) do + local match_pat = v[1] + local replace_pat = v[2] + + if string.find(source, match_pat) then + local path_override = string.gsub(source, match_pat, replace_pat) + dump_line(string.format("OVERRIDE>> %s => %s (w/ %s => %s)", source , path_override, match_pat, replace_pat)) + + -- check str len if + if string.len(source) < string.len(path_override) then + local new_addr = alloc(256) -- alloc 256 bytes + writeCString(new_addr, path_override) + print(string.format("Replaced string is longer than original, allocating it to 0x%X", new_addr)) + return new_addr + else + -- original string len is longer or same + writeCString(addr, path_override) + return nil + end + end + end + + -- can't find it + return nil +end + +-- write a string with null +function writeCString(addr, content) + writeString(addr, content) + writeBytes(addr + string.len(content), 0) -- put null byte, this assume content is only ASCII char +end + +-- no way to free, have fun +function alloc(size) + -- taken from http://forum.cheatengine.org/viewtopic.php?t=560739&sid=73dc5a565393a034ba16d1d468bb1eeb + autoAssemble([[ +alloc(luatemp, ]]..size..[[) +registersymbol(luatemp) +]]) + return getAddress("luatemp") +end + +function split(str) + local result = {} + for token in string.gmatch(str..",", "([^,]+),%s*") do + table.insert(result, all_trim(token)) + end + + return result +end + +init() diff --git a/src/tools/Script/ce_dump_vfs_path_sbbench.lua b/src/tools/Script/ce_dump_vfs_path_sbbench.lua new file mode 100644 index 00000000..fc081884 --- /dev/null +++ b/src/tools/Script/ce_dump_vfs_path_sbbench.lua @@ -0,0 +1,141 @@ +-- init global variable +g_dump = io.open(os.date("dump_%Y-%m-%d-%H-%M-%S.txt"), "a") +g_dump:write("ptr, name\n") +g_count = 0 + +-- relative virtual address(rva) to register_whatever function +-- this assume ffxiv sb benchmark +-- YOU MUST EDIT THIS TO RIGHT VALUE TO WORK +-- check http://imgur.com/a/nJCef for disasm +g_addr = {} +g_addr.load_master = 0x162210 -- looks like below function is called from this +g_addr.load1 = 0x1624B0 -- mostly loads gfx stuff but does calc in it? +g_addr.load2 = 0x162410 -- mostly loads gfx stuff +g_addr.load3 = 0x1626B0 -- mostly loads exd data +g_addr.load4 = 0x162540 -- from this on found it because there are next to each other +g_addr.load5 = 0x162360 -- umm, vfx? + +g_loadseen = {} + +g_xiv = {} +g_xiv.proc_name = "ffxiv_dx11.exe" +g_xiv.path = "C:\\Users\\Mino\\Desktop\\FFXIV-SB\\game\\" .. g_xiv.proc_name +-- because I don't want to login and out every single try +g_xiv.arg = "SYS.Language=1 SYS.Fps=0 SYS.MainAdapter=\"AMD_Radeon_HD_7800_Series(\\\\.\\DISPLAY1)\" SYS.ScreenMode=0 SYS.ScreenWidth=1280 SYS.ScreenHeight=720 SYS.FullScreenWidth=1280 SYS.FullScreenHeight=720 SYS.Gamma=50 SYS.IsSoundAlways=0 SYS.SoundMaster=100 SYS.SoundBgm=100 SYS.SoundSe=100 SYS.SoundVoice=100 SYS.SoundSystem=100 SYS.SoundEnv=100 SYS.IsSndMaster=0 SYS.IsSndBgm=0 SYS.IsSndSe=0 SYS.IsSndVoice=0 SYS.IsSndSystem=0 SYS.IsSndEnv=0 SYS.WaterWet_DX11=1 SYS.OcclusionCulling_DX11=1 SYS.LodType_DX11=1 SYS.ReflectionType_DX11=0 SYS.AntiAliasing_DX11=0 SYS.TranslucentQuality_DX11=0 SYS.GrassQuality_DX11=2 SYS.ShadowLOD_DX11=1 SYS.ShadowVisibilityTypeSelf_DX11=1 SYS.ShadowVisibilityTypeOther_DX11=0 SYS.ShadowTextureSizeType_DX11=1 SYS.ShadowCascadeCountType_DX11=2 SYS.ShadowSoftShadowType_DX11=1 SYS.PhysicsTypeSelf_DX11=2 SYS.PhysicsTypeOther_DX11=2 SYS.TextureFilterQuality_DX11=2 SYS.TextureAnisotropicQuality_DX11=0 SYS.Vignetting_DX11=1 SYS.RadialBlur_DX11=1 SYS.SSAO_DX11=1 SYS.Glare_DX11=2 SYS.DepthOfField_DX11=1 SYS.ParallaxOcclusion_DX11=0 SYS.Tessellation_DX11=0 SYS.GlareRepresentation_DX11=0" +g_mode = "create" + +function init() + -- attach debugger + if g_mode == "create" then + print(string.format("Launching ffxiv.exe w/ arg %s", g_xiv.arg)) + createProcess(g_xiv.path, g_xiv.arg, true, true) + elseif g_mode == "attach" then + print("Looking for ffxiv.exe..") + while not openProcess("ffxiv.exe") do sleep(1) end + print("Attaching...") + debugProcess() + while not getAddress(g_xiv.proc_name) do sleep(1) end + print("Module loaded") + else + print("Unsupported mode!") + return + end + + for k, v in pairs(g_addr) do + -- k = name + -- v = rva + print(string.format("Attaching %s breakpoint on 0x%X", k, v)) + debug_setBreakpoint(get_va(v)) + end + + print("Now waiting for breakpoints..") +end + +function debugger_onBreakpoint() + if RIP == get_va(g_addr.load1) then + -- dump_message("1>>") + dump_addr(R8) + elseif RIP == get_va(g_addr.load2) then + -- dump_message("2>>") + dump_addr(R8) + elseif RIP == get_va(g_addr.load3) then + -- dump_message("3>>") + dump_addr(RCX) + elseif RIP == get_va(g_addr.load4) then + -- dump_message("4>>") + dump_addr(RCX) + elseif RIP == get_va(g_addr.load5) then + -- dump_message("5>>") + dump_addr(RDX) + elseif RIP == get_va(g_addr.load_master) then + -- dump_message("master>>") + dump_addr(RDX) + else + -- user bp, update gui on ce, but who gives a shit? + -- continue execution anyway + debug_continueFromBreakpoint("co_run") + return 0 + end + + -- something is missing on the text file because it takes ageeeeeeeeees to write to file + -- let just wait it + + + -- continue execution + debug_continueFromBreakpoint("co_run") + --return 0 -- update gui + return 1 +end + +function dump_addr(addr) + local vfspath = readString(addr, 256) + if not g_loadseen[vfspath] then + g_loadseen[vfspath] = true -- set seen flag + + local message = string.format("%X, %s", addr, vfspath) + dump_line(message) + end +end + +function dump_line(message) + dump_message(message .. "\n") +end + +function dump_message(message) + g_dump:write(message) + g_dump:flush() + print(message) +end + +-- mod_base + rva = va +function get_va(rva) + return getAddress(g_xiv.proc_name) + rva +end + +-- rva = va - mod_base +function get_rva(va) + return va - getAddress(g_xiv.proc_name) +end + +-- check mod_base <= va <= mod_base + mod_size +function is_mmod_addr(va) + local mod_base = getAddress(g_xiv.proc_name) + local mod_size = getModuleSize(g_xiv.proc_name) + + if mod_base <= va and va <= mod_base + mod_size then + return true + end + + return false +end + +function split(str) + local result = {} + for token in string.gmatch(str..",", "([^,]+),%s*") do + table.insert(result, all_trim(token)) + end + + return result +end + +init() diff --git a/src/tools/pcb_reader/CMakeLists.txt b/src/tools/pcb_reader/CMakeLists.txt new file mode 100644 index 00000000..9bd07e39 --- /dev/null +++ b/src/tools/pcb_reader/CMakeLists.txt @@ -0,0 +1,23 @@ +set( CMAKE_CXX_FLAGS "-std=c++11 -m32") + +include_directories("../../") +include_directories("../") + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.cpp") + +SET(Boost_USE_STATIC_LIBS ON) +set(Boost_INCLUDE_DIR /opt/build_libs/boost_1_60_0) +set(Boost_LIBRARY_DIR /opt/build_libs/boost_1_60_0/stage/lib) +set(SERVER_COMMON_DIR ../../servers/Server_Common) +find_package(Boost COMPONENTS log log_setup thread date_time filesystem system REQUIRED) +include_directories(${Boost_INCLUDE_DIR}) + +link_directories(${Boost_LIBRARY_DIR}) +link_directories(${SERVER_COMMON_DIR}) +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".") +add_executable(pcb_parser2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) + +target_link_libraries (pcb_parser2 server_common.a pthread mysqlclient dl z) +target_link_libraries(pcb_parser2 ${Boost_LIBRARIES} ${Boost_LIBRARIES}) + diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp new file mode 100644 index 00000000..6da22cd5 --- /dev/null +++ b/src/tools/pcb_reader/main.cpp @@ -0,0 +1,258 @@ +#include +#include +#include + +#include "pcb.h" + +#include + +int parseBlockEntry( char* data, std::vector& entries, int gOff ) +{ + int offset = 0; + bool isgroup = true; + while( isgroup ) + { + PCB_BLOCK_ENTRY block_entry; + memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); + isgroup = block_entry.header.type == 0x30 ? true : false; + + //printf( " BLOCKHEADER_%X: type: %i, group_size: %i\n", gOff + offset, block_entry.header.type, block_entry.header.group_size ); + + if( isgroup ) + { + parseBlockEntry( data + offset + 0x30, entries, gOff + offset ); + offset += block_entry.header.group_size; + + } + else + { + printf( "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n", + block_entry.header.num_v16, block_entry.header.num_indices, block_entry.header.num_vertices ); + int doffset = sizeof( block_entry.header ) + offset; + uint16_t block_size = sizeof( block_entry.header ) + + block_entry.header.num_vertices * 3 * 4 + + block_entry.header.num_v16 * 6 + + block_entry.header.num_indices * 6; + + if( block_entry.header.num_vertices != 0 ) + { + block_entry.data.vertices.resize( block_entry.header.num_vertices ); + + int32_t size_vertexbuffer = block_entry.header.num_vertices * 3; + memcpy( &block_entry.data.vertices[0], data + doffset, size_vertexbuffer * 4 ); + doffset += size_vertexbuffer * 4; + } + if( block_entry.header.num_v16 != 0 ) + { + block_entry.data.vertices_i16.resize( block_entry.header.num_v16 ); + int32_t size_unknownbuffer = block_entry.header.num_v16 * 6; + memcpy( &block_entry.data.vertices_i16[0], data + doffset, size_unknownbuffer ); + doffset += block_entry.header.num_v16 * 6; + } + if( block_entry.header.num_indices != 0 ) + { + block_entry.data.indices.resize( block_entry.header.num_indices ); + int32_t size_indexbuffer = block_entry.header.num_indices * 6; + memcpy( &block_entry.data.indices[0], data + doffset, size_indexbuffer ); + doffset += size_indexbuffer; + } + entries.push_back( block_entry ); + + /* printf( "Vertices: \n" ); + for( auto& entry1 : block_entry.data.vertices ) + { + printf( "\t %f, %f, %f \n", + entry1.x, entry1.y, entry1.z ); + } + + float x_base = abs( float( block_entry.header.x1 - block_entry.header.x ) ); + float y_base = abs( float( block_entry.header.y1 - block_entry.header.y ) ); + float z_base = abs( float( block_entry.header.z1 - block_entry.header.z ) ); + + printf( "Vertices I16: \n" ); + for( auto& entry1 : block_entry.data.vertices_i16 ) + { + uint16_t var1 = entry1.x; + uint16_t var2 = entry1.y; + uint16_t var3 = entry1.z; + float x = ( var1 ); + float y = ( var2 ); + float z = ( var3 ); + printf( "\t%f, ", ( x / 0xFFFF ) * x_base + block_entry.header.x ); + printf( "%f, ", ( y / 0xFFFF ) * y_base + block_entry.header.y ); + printf( "%f ", ( z / 0xFFFF ) * z_base + block_entry.header.z ); + printf( "\n" ); + + }*/ + } + } + + return 0; +} + +int main() +{ + char *data; + + uint32_t offset = 0; + //r1f1_b1_dor00.pcb + + //std::string filename( "f1h0_s_rof0003.pcb" ); + std::string filename( "tr0924.pcb" ); + FILE *fp = nullptr; + fp = fopen( filename.c_str(), "rb" ); + if( fp == nullptr ) + { + return 0; + } + + fseek( fp, 0, SEEK_END ); + int32_t size = ftell( fp ); + data = new char[size]; + rewind( fp ); + fread( data, sizeof( char ), size, fp ); + fclose( fp ); + + PCB_FILE pcb_file; + memcpy( &pcb_file.header, data, sizeof( pcb_file.header ) ); + offset += sizeof( pcb_file.header ); + + + + std::vector entries; + + bool isgroup = true; + while( isgroup ) + { + PCB_BLOCK_ENTRY block_entry; + memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); + isgroup = block_entry.header.type == 0x30 ? true : false; + + //printf( "BLOCKHEADER_%X: type: %i, group_size: %i\n", offset, block_entry.header.type, block_entry.header.group_size ); + + if( isgroup ) + { + std::vector data_block( block_entry.header.group_size ); + memcpy( &data_block[0], data + offset, block_entry.header.group_size ); + parseBlockEntry( (char*)&data_block[0] + 0x30, entries, offset ); + offset += block_entry.header.group_size; + } + else + { + parseBlockEntry( data + offset, entries, offset ); + } + + + + } + + for( uint16_t i = 0; i <= pcb_file.header.num_entries; i++ ) + { + PCB_BLOCK_ENTRY block_entry; + memcpy( &block_entry.header, data + offset, sizeof( block_entry.header ) ); + offset += sizeof( block_entry.header ); + + uint16_t block_size = sizeof( block_entry.header ) + + block_entry.header.num_vertices * 3 * 4 + + block_entry.header.num_v16 * 6 + + block_entry.header.num_indices * 6; + + if( block_entry.header.num_vertices != 0 ) + { + block_entry.data.vertices.resize( block_entry.header.num_vertices ); + + int32_t size_vertexbuffer = block_entry.header.num_vertices * 3; + memcpy( &block_entry.data.vertices[0], data + offset, size_vertexbuffer * 4 ); + offset += size_vertexbuffer * 4; + } + if( block_entry.header.num_v16 != 0 ) + { + block_entry.data.vertices_i16.resize( block_entry.header.num_v16 ); + int32_t size_unknownbuffer = block_entry.header.num_v16 * 6; + memcpy( &block_entry.data.vertices_i16[0], data + offset, size_unknownbuffer ); + offset += block_entry.header.num_v16 * 6; + } + if( block_entry.header.num_indices != 0 ) + { + block_entry.data.indices.resize( block_entry.header.num_indices ); + int32_t size_indexbuffer = block_entry.header.num_indices * 6; + memcpy( &block_entry.data.indices[0], data + offset, size_indexbuffer ); + offset += size_indexbuffer; + } + + // blocks always align to 16 bytes + 8 bytes padding till the next block + int rest = ( offset % 16 ); + if( rest > 0 ) + { + rest = 0x10 - rest; + } + offset += rest ; + + + pcb_file.entries.push_back( block_entry ); + } + + FILE* fp_out1 = fopen( std::string( filename + ".plain" ).c_str(), "w" ); + fprintf( fp_out1, ""); + fclose( fp_out1 ); + + FILE* fp_out = fopen( std::string( filename + ".plain" ).c_str(), "w+" ); + + fprintf( fp_out, "HEADER: num_entries: %i, total_indices: %i, unknown_1: %i\n\n", pcb_file.header.num_entries, pcb_file.header.total_indices, pcb_file.header.unknown_1 ); + + int block_cnt = 0; + for( auto& entry : pcb_file.entries ) + { + + fprintf( fp_out, "BLOCKHEADER_%i: type: %i, group_size: %i\n ", + block_cnt, entry.header.type, entry.header.group_size ); + fprintf( fp_out, "\tAABB: x: %f, y: %f, z: %f\n ", + entry.header.x, entry.header.y, entry.header.z ); + fprintf( fp_out, "\t\t x1: %f, y1: %f, z1: %f\n ", + entry.header.x1, entry.header.y1, entry.header.z1 ); + fprintf( fp_out, "\tnum_v16: %i, num_indices: %i, num_vertices: %i\n\n", + entry.header.num_v16, entry.header.num_indices, entry.header.num_vertices ); + + fprintf( fp_out, "Vertices: \n"); + for( auto& entry1 : entry.data.vertices ) + { + fprintf( fp_out, "\t %f, %f, %f \n", + entry1.x, entry1.y, entry1.z ); + } + + float x_base = abs( float( entry.header.x1 - entry.header.x ) ); + float y_base = abs( float( entry.header.y1 - entry.header.y ) ); + float z_base = abs( float( entry.header.z1 - entry.header.z ) ); + + fprintf( fp_out, "Vertices I16: \n" ); + for( auto& entry1 : entry.data.vertices_i16 ) + { + uint16_t var1 = entry1.x; + uint16_t var2 = entry1.y; + uint16_t var3 = entry1.z; + float x = ( var1 ); + float y = ( var2 ); + float z = ( var3 ); + fprintf( fp_out, "\t%f, ", (x / 0xFFFF) * x_base + entry.header.x ); + fprintf( fp_out, "%f, ", (y / 0xFFFF) * y_base + entry.header.y ); + fprintf( fp_out, "%f ", (z / 0xFFFF) * z_base + entry.header.z ); + fprintf( fp_out, "\n"); + + } + + + + + fprintf( fp_out, "Indices: \n" ); + for( auto& entry1 : entry.data.indices ) + { + fprintf( fp_out, "\t %i, %i, %i - %x,%x,%x \n", + entry1.index[0], entry1.index[1], entry1.index[2], entry1.unknown[0], entry1.unknown[1], entry1.unknown[2] ); + } + fprintf( fp_out, "\n" ); + } + + fclose( fp_out ); + + return 0; +} diff --git a/src/tools/pcb_reader/pcb.h b/src/tools/pcb_reader/pcb.h new file mode 100644 index 00000000..a9c65e2e --- /dev/null +++ b/src/tools/pcb_reader/pcb.h @@ -0,0 +1,69 @@ +#include +#include + +struct PCB_HEADER +{ + uint32_t unknown_1; + uint32_t unknown_2; + uint32_t num_entries; // count starts at 0 + uint32_t total_indices; + uint64_t padding; +}; + +struct PCB_BLOCK_HEADER +{ + uint32_t type; // 0 for entry, 0x30 for group + uint32_t group_size; // when group size in bytes for the group block + // bounding box + float x; + float y; + float z; + float x1; + float y1; + float z1; + // number of vertices packed into 16 bit + uint16_t num_v16; + // number of indices + uint16_t num_indices; + // number of normal floar vertices + uint32_t num_vertices; +}; + +struct PCB_VERTEXDATA +{ + float x; + float y; + float z; +}; + +struct PCB_INDEXDATA +{ + uint8_t index[3]; + uint8_t unknown[3]; +}; + +struct PCB_VERTEXDATAI16 +{ + uint16_t x; + uint16_t y; + uint16_t z; +}; + +struct PCB_BLOCK_DATA +{ + std::vector< PCB_VERTEXDATA > vertices; + std::vector< PCB_VERTEXDATAI16 > vertices_i16; + std::vector< PCB_INDEXDATA > indices; +}; + +struct PCB_BLOCK_ENTRY +{ + PCB_BLOCK_HEADER header; + PCB_BLOCK_DATA data; +}; + +struct PCB_FILE +{ + PCB_HEADER header; + std::vector< PCB_BLOCK_ENTRY > entries; +}; diff --git a/src/tools/quest_parser/CMakeLists.txt b/src/tools/quest_parser/CMakeLists.txt new file mode 100644 index 00000000..1a314ae9 --- /dev/null +++ b/src/tools/quest_parser/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 2.6) +cmake_policy(SET CMP0015 NEW) +project(Tool_QuestParser) + +set(SAPPHIRE_BOOST_VER 1.63.0) +set(SAPPHIRE_BOOST_FOLDER_NAME boost_1_63_0) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin/") + +include_directories("../../lib/ChaiScript-6.0.0/include/") + +include_directories("../../sapphire/datReader/") +include_directories("../../sapphire/") +include_directories("../") + +file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") +file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") + +set(SERVER_COMMON_DIR ../../sapphire/Server_Common) +set(Boost_USE_STATIC_LIBS ON) + +if(UNIX) + include_directories("/usr/include/mysql/") + message(STATUS "Setting GCC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32") + + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + else() + if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib) + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + else() + message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!") + endif() + endif() +else() + add_definitions(-D_WIN32_WINNT=0x601) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../lib/MySQL/") + message(STATUS "Setting MSVC flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME}) + message(STATUS "Using boost in /src/lib") + set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME}) + set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0) + else() + find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system) + if(Boost_FOUND) + set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR}) + elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR})) + set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR}) + set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR}) + else() + message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!") + endif() + endif() +endif() + +include_directories(${Boost_INCLUDE_DIR}) + +link_directories(${BOOST_LIBRARYDIR}) +link_directories(${SERVER_COMMON_DIR}) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../sapphire/datReader/) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/MySQL) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/zlib) + +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/") +add_executable(quest_parse ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) + +set_target_properties(quest_parse PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../bin/" +) + +if (UNIX) + target_link_libraries (quest_parse Common xivdat pthread mysqlclient dl z) +else() + target_link_libraries (quest_parse Common xivdat libmysql zlib1) +endif() + +target_link_libraries(quest_parse ${Boost_LIBRARIES} ${Boost_LIBRARIES}) + diff --git a/src/tools/quest_parser/main.cpp b/src/tools/quest_parser/main.cpp new file mode 100644 index 00000000..7c76b2ce --- /dev/null +++ b/src/tools/quest_parser/main.cpp @@ -0,0 +1,337 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +Core::Logger g_log; +Core::Data::ExdData g_exdData; + + +void createScript( boost::shared_ptr< Core::Data::QuestInfo > pQuestData, std::set< std::string > additionalList ) +{ + std::string header( + "// This is an automatically generated chai script template\n" + "// Content needs to be added by hand to make it function\n" + "// In order for this script to be loaded, change its extension to .chai\n" + "// and move it to the correct folder in bin/scripts/chai/quest\n" + "\n" + ); + + std::size_t splitPos = pQuestData->name_intern.find( "_" ); + std::string className = pQuestData->name_intern.substr( 0, splitPos ); + + std::string sceneStr = " //////////////////////////////////////////////////////////////////////\n // Available Scenes in this quest, not necessarly all are used\n"; + std::string seqStr = " // Steps in this quest ( 0 is before accepting, \n // 1 is first, 255 means ready for turning it in\n"; + std::string questVarStr = " // Quest vars / flags used\n"; + for( auto &entry : additionalList ) + { + if( entry.find( "OnScene" ) != std::string::npos ) + { + std::string sceneName = entry.substr( 2 ); + std::string sceneId = entry.substr( 7 ); + + std::size_t numOff = sceneId.find_first_not_of( "0" ); + sceneId = numOff != std::string::npos ? sceneId.substr( numOff ) : "0"; + + sceneStr += " def " + sceneName + "( player )\n"; + sceneStr += " {\n"; + sceneStr += " player.eventPlay( this.id, " + sceneId + ", NONE,\n"; + sceneStr += " fun( player, eventId, param1, param2, param3 )\n"; + sceneStr += " {} );\n"; + sceneStr += " }\n\n"; + } + else if( entry.find( "Flag" ) != std::string::npos || + entry.find( "QuestUI" ) != std::string::npos ) + { + questVarStr += " // " + entry + "\n"; + } + else if( entry.find( "SEQ" ) != std::string::npos ) + { + if( entry.find( "SEQ_FINISH" ) != std::string::npos ) + { + seqStr += " this.SEQ_FINISH = 255;\n"; + } + else if( entry.find( "SEQ_OFFER" ) != std::string::npos ) + { + } + else + { + std::string seqName = entry; + std::string seqId = entry.substr( 4 ); + seqStr += " this." + seqName + " = " + seqId + ";\n"; + } + } + + + } + std::string rewards = " // Quest rewards \n"; + rewards += ( pQuestData->reward_exp_factor != 0 ) ? " this.RewardExpFactor = " + std::to_string( pQuestData->reward_exp_factor ) + ";\n" : ""; + rewards += ( pQuestData->reward_gil != 0 ) ? " this.RewardGil = " + std::to_string( pQuestData->reward_gil ) + ";\n" : ""; + rewards += ( pQuestData->reward_emote != 0 ) ? " this.RewardEmote = " + std::to_string( pQuestData->reward_emote ) + ";\n" : ""; + rewards += ( pQuestData->reward_action != 0 ) ? " this.RewardAction = " + std::to_string( pQuestData->reward_action ) + ";\n" : ""; + rewards += ( pQuestData->reward_action_general1 != 0 ) ? " this.RewardGeneralAction1 = " + std::to_string( pQuestData->reward_action_general1 ) + ";\n" : ""; + rewards += ( pQuestData->reward_action_general2 != 0 ) ? " this.RewardGeneralAction2 = " + std::to_string( pQuestData->reward_action_general2 ) + ";\n" : ""; + rewards += ( pQuestData->reward_gc_seals != 0 ) ? " this.RewardGCSeals = " + std::to_string( pQuestData->reward_gc_seals ) + ";\n" : ""; + rewards += ( pQuestData->reward_other != 0 ) ? " this.RewardOther = " + std::to_string( pQuestData->reward_other ) + ";\n" : ""; + rewards += ( pQuestData->reward_reputation != 0 ) ? " this.RewardReputation = " + std::to_string( pQuestData->reward_reputation ) + ";\n" : ""; + rewards += ( pQuestData->reward_tome_type != 0 ) ? " this.RewardTomeType = " + std::to_string( pQuestData->reward_tome_type ) + ";\n" : ""; + rewards += ( pQuestData->reward_tome_count != 0 ) ? " this.RewardTomeCount = " + std::to_string( pQuestData->reward_tome_count ) + ";\n" : ""; + rewards += ( pQuestData->instanced_content_unlock != 0 ) ? " this.InstancedContentUnlock = " + std::to_string( pQuestData->instanced_content_unlock ) + ";\n" : ""; + + if( pQuestData->reward_item.size() > 0 ) + { + rewards += " this.RewardItem = ["; + for( int ca = 0; ca < pQuestData->reward_item.size(); ca++ ) + { + rewards += std::to_string( pQuestData->reward_item.at( ca ) ); + if( ca != pQuestData->reward_item.size() - 1 ) + { + rewards += ", "; + } + } + rewards += "];\n"; + } + + if( pQuestData->reward_item.size() > 0 ) + { + rewards += " this.RewardItemCount = ["; + for( int ca = 0; ca < pQuestData->reward_item_count.size(); ca++ ) + { + rewards += std::to_string( pQuestData->reward_item_count.at( ca ) ); + if( ca != pQuestData->reward_item_count.size() - 1 ) + { + rewards += ", "; + } + } + rewards += "];\n"; + } + + if( pQuestData->reward_item_optional.size() > 0 ) + { + rewards += " this.RewardItemOptional = ["; + for( int ca = 0; ca < pQuestData->reward_item_optional.size(); ca++ ) + { + rewards += std::to_string( pQuestData->reward_item_optional.at( ca ) ); + if( ca != pQuestData->reward_item_optional.size() - 1 ) + { + rewards += ", "; + } + } + rewards += "];\n"; + } + + if( pQuestData->reward_item_optional_count.size() > 0 ) + { + rewards += " this.RewardItemOptionalCount = ["; + for( int ca = 0; ca < pQuestData->reward_item_optional_count.size(); ca++ ) + { + rewards += std::to_string( pQuestData->reward_item_optional_count.at( ca ) ); + if( ca != pQuestData->reward_item_optional_count.size() - 1 ) + { + rewards += ", "; + } + } + rewards += "];\n"; + } + + bool hasERange = false; + bool hasEmote = false; + bool hasEnemies = false; + std::vector< uint32_t > enemy_ids; + std::vector< std::string > script_entities; + std::string sentities = " // Entities found in the script data of the quest\n"; + + for( int ca = 0; ca < pQuestData->script_entity.size(); ca ++ ) + { + if( ( pQuestData->script_entity.at( ca ).find( "HOWTO" ) != std::string::npos ) || ( pQuestData->script_entity.at( ca ).find( "HOW_TO" ) != std::string::npos ) ) + continue; + + if( (pQuestData->script_entity.at( ca ).find( "EMOTENO" ) != std::string::npos ) || (pQuestData->script_entity.at( ca ).find( "EMOTEOK" ) != std::string::npos ) ) + hasEmote = true; + + if(pQuestData->script_entity.at(ca).find("ENEMY") != std::string::npos) + { + hasEnemies = true; + enemy_ids.push_back(pQuestData->script_value.at(ca)); + } + + script_entities.push_back( pQuestData->script_entity.at( ca ) + " = " + std::to_string( pQuestData->script_value.at( ca ) ) ); + } + std::sort( script_entities.begin(), script_entities.end() ); + + for( auto& entity : script_entities ) + { + sentities += " this." + entity + ";\n"; + } + + std::string additional = "// Quest Script: " + pQuestData->name_intern + "\n"; + additional += "// Quest Name: " + pQuestData->name + "\n"; + additional += "// Quest ID: " + std::to_string( pQuestData->id ) + "\n"; + additional += "// Start NPC: " + std::to_string( pQuestData->enpc_resident_start ) + "\n"; + additional += "// End NPC: " + std::to_string( pQuestData->enpc_resident_end ) + "\n\n"; + + std::string scriptEntry = " //////////////////////////////////////////////////////////////////////\n"; + scriptEntry += " // Event Handlers\n"; + scriptEntry += " def onTalk( eventId, player, actorId )\n"; + scriptEntry += " {\n"; + scriptEntry += " var actor = mapActor( actorId );\n"; + scriptEntry += " }\n\n"; + + if( hasERange ) + { + scriptEntry += " def onWithinRange( eventId, player, eRangeId, x, y, z )\n"; + scriptEntry += " {\n"; + scriptEntry += " }\n\n"; + } + + if( hasEmote ) + { + scriptEntry += " def onEmote( eventId, player, actorId, emoteId )\n"; + scriptEntry += " {\n"; + scriptEntry += " }\n\n"; + } + + for( auto enemy : enemy_ids ) + { + scriptEntry += " def onMobKill_" + std::to_string(enemy) + "( player )\n"; + scriptEntry += " {\n"; + scriptEntry += " }\n\n"; + } + + std::string constructor; + constructor += " def " + className + "Def()\n"; + constructor += " {\n"; + constructor += " // Basic quest information \n"; + constructor += " this.name = \"" + pQuestData->name + "\";\n"; + constructor += " this.id = " + std::to_string( pQuestData->id ) + ";\n\n"; + constructor += questVarStr + "\n"; + constructor += seqStr + "\n"; + constructor += rewards + "\n"; + constructor += sentities + "\n"; + constructor += " }\n"; + + + std::string classString( + "class " + className + "Def \n" + "{\n" + + constructor + + "\n" + + sceneStr + + scriptEntry + + "};\n\n" + ); + + std::string objName = className; + std::string initObj = "GLOBAL " + objName + " = " + className + "Def();\n"; + + std::ofstream outputFile; + outputFile.open( "generated/" + className + ".chai_generated" ); + outputFile << header << additional << classString << initObj; + outputFile.close(); +} + +int main() +{ + + g_log.init(); + + // std::string datLocation( "/opt/sapphire_3_15_0/bin/sqpack" ); + std::string datLocation( "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv" ); + + g_log.info( "Setting up EXD data" ); + if( !g_exdData.init( datLocation ) ) + { + g_log.fatal( "Error setting up EXD data " ); + return 0; + } + + xiv::dat::GameData data( datLocation ); + xiv::exd::ExdData eData( data ); + + auto QuestDat = g_exdData.setupDatAccess( "Quest", xiv::exd::Language::en ); + auto rows = QuestDat.get_rows(); + + for( auto row : rows ) + { + + auto questInfo = g_exdData.getQuestInfo( row.first ); + + if( questInfo->name.empty() || questInfo->name_intern.empty() ) + { + continue; + } + + size_t pos_seperator = questInfo->name_intern.find_first_of( "_" ); + + std::string folder; + + if( pos_seperator != std::string::npos ) + { + folder = questInfo->name_intern.substr( pos_seperator + 1, 3 ); + } + else + { + return 0; + } + + + + const xiv::dat::Cat& test = data.get_category( "game_script" ); + + const std::string questPath = "game_script/quest/" + folder + "/" + questInfo->name_intern + ".luab"; + + const auto &test_file = data.get_file( questPath ); + auto §ion = test_file->access_data_sections().at( 0 ); + int32_t size = *( uint32_t* ) §ion[4]; + + std::set stringList; + + uint32_t offset = 0; + + std::ofstream outputFile1; + outputFile1.open("generated/" + questInfo->name_intern + ".luab", std::ios::binary); + outputFile1.write(§ion[0], section.size()); + outputFile1.close(); + std::string command= std::string("java -jar unluac_2015_06_13.jar ") + "generated/" + questInfo->name_intern + ".luab" + ">> " + "generated/" + questInfo->name_intern + ".lua"; + system(command.c_str()); + for( ; ; ) + { + + std::string entry( §ion[offset] ); + offset += entry.size() + 1; + + if( entry.size() > 3 + && entry.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_" ) == std::string::npos ) + { + if( entry.find( "SEQ" ) != std::string::npos + || entry.find( "QuestUI" ) != std::string::npos + || entry.find( "OnScene" ) != std::string::npos + || entry.find( "Flag" ) != std::string::npos + || entry.find( "ACTOR" ) != std::string::npos + && entry.find( "_ACTOR" ) == std::string::npos ) + if( entry.find( "HOWTO" ) == std::string::npos ) + stringList.insert( entry ); + } + + if( offset >= section.size() ) + break; + } + + + createScript( questInfo, stringList ); +//break; + } + + return 0; +} diff --git a/src/tools/quest_parser/unluac_2015_06_13.jar b/src/tools/quest_parser/unluac_2015_06_13.jar new file mode 100644 index 00000000..a29884fa Binary files /dev/null and b/src/tools/quest_parser/unluac_2015_06_13.jar differ