From ac938c16eb9b9d7731f8efc8440f1564ea9bc96f Mon Sep 17 00:00:00 2001 From: pztrn Date: Tue, 4 Oct 2016 15:42:36 +0500 Subject: [PATCH] Initial commit, what works: * Servers update * Profile creation/edit/deletion * Game launching * Checks server version <> profile version. --- .gitignore | 2 + LICENSE | 674 +++++++++++++++++++++++++++++++ README.md | 58 +++ common/logo.go | 12 + configuration/config_object.go | 53 +++ configuration/exported.go | 15 + context/context_object.go | 82 ++++ context/exported.go | 15 + database/database_object.go | 76 ++++ database/exported.go | 25 ++ database/migrations.go | 77 ++++ datamodels/database.go | 14 + datamodels/profile.go | 23 ++ datamodels/server.go | 33 ++ eventer/eventer_object.go | 54 +++ eventer/exported.go | 15 + launcher/exported.go | 15 + launcher/launcher_object.go | 51 +++ requester/exported.go | 21 + requester/requester_object.go | 236 +++++++++++ ui/about.go | 716 +++++++++++++++++++++++++++++++++ ui/exported.go | 30 ++ ui/mainwindow.go | 554 +++++++++++++++++++++++++ ui/options.go | 226 +++++++++++ ui/options_profile.go | 305 ++++++++++++++ urtrator.go | 29 ++ 26 files changed, 3411 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 common/logo.go create mode 100644 configuration/config_object.go create mode 100644 configuration/exported.go create mode 100644 context/context_object.go create mode 100644 context/exported.go create mode 100644 database/database_object.go create mode 100644 database/exported.go create mode 100644 database/migrations.go create mode 100644 datamodels/database.go create mode 100644 datamodels/profile.go create mode 100644 datamodels/server.go create mode 100644 eventer/eventer_object.go create mode 100644 eventer/exported.go create mode 100644 launcher/exported.go create mode 100644 launcher/launcher_object.go create mode 100644 requester/exported.go create mode 100644 requester/requester_object.go create mode 100644 ui/about.go create mode 100644 ui/exported.go create mode 100644 ui/mainwindow.go create mode 100644 ui/options.go create mode 100644 ui/options_profile.go create mode 100644 urtrator.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd00514 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# We don't need binaries. +urtrator diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 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 General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ffc6634 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# URTrator + +URTrator is a desktop application that should (eventually) replace +Urban Terror's and IRC client interfaces for you, because they're +pretty shitty :). + +Right now it can: + +* Obtaining list of Urban Terror servers from master server and +updating information about them. +* Local caching of whole data (in SQLite3 database). +* Extended Urban Terror launching capabilities (e.g. launching game +in another X session). +* Supporting of multiple profiles with multiple game versions. + +Planning: + +* Updating single server. +* Favorites servers. +* Showing information about servers (like in UrT Connector). +* Friends searching. +* RCON console. +* Game updating (not from official servers yet, sorry). +* Pickup/matchmaking interfaces. +* All kinds of notifications. +* ...maybe more :) + +# Installation + +## Release + +You don't need to install anything, thanks to Go's static linking. +URTrator executable contains everything we need. Just download +approriate binary and launch it! :) The only thing is to make +sure you have GTK2 installed. + +## Development version + +URTrator written in Go and GTK2, so you should have them installed. +Make sure your ``GOPATH`` variable is defined. + +Then execute: + +``` +go get -d github.com/pztrn/urtrator +go install github.com/pztrn/urtrator +``` + +First command will get sources of URTrator and dependencies, second +command will build executable for you and place it in ``$GOROOT/bin``. + +# Important information + +## GTK warnings in console + +Many GTK warnings in console may appear while using URTrator. Unfortunately, +they are out of control, because they are related to Go GTK+2 bindings +and nothing can be done on URTrator side. diff --git a/common/logo.go b/common/logo.go new file mode 100644 index 0000000..64c5168 --- /dev/null +++ b/common/logo.go @@ -0,0 +1,12 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package common + +var Logo = `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` diff --git a/configuration/config_object.go b/configuration/config_object.go new file mode 100644 index 0000000..ed14a23 --- /dev/null +++ b/configuration/config_object.go @@ -0,0 +1,53 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package configuration + +import ( + // stdlib + "fmt" + "os" + "path" + "runtime" +) + +type Config struct { + // Temporary (or runtime) configuration things. + TEMP map[string]string +} + +func (c *Config) initializePathsNix() { + fmt.Println("Initializing configuration paths...") + + // Get storage path. By default we will use ~/.config/urtrator + // directory. + home_path := os.Getenv("HOME") + data_path := path.Join(home_path, ".config", "urtrator") + fmt.Println("Will use data path: " + data_path) + c.TEMP["DATA"] = data_path + + if _, err := os.Stat(data_path); os.IsNotExist(err) { + os.MkdirAll(data_path, 0755) + } +} + +func (c *Config) initializeStorages() { + c.TEMP = make(map[string]string) +} + +func (c *Config) Initialize() { + fmt.Println("Initializing configuration storage...") + c.initializeStorages() + + if runtime.GOOS == "linux" { + c.initializePathsNix() + } else { + panic("We're not ready for other OSes yet!") + } +} diff --git a/configuration/exported.go b/configuration/exported.go new file mode 100644 index 0000000..cf94706 --- /dev/null +++ b/configuration/exported.go @@ -0,0 +1,15 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package configuration + +func New() *Config { + c := Config{} + return &c +} diff --git a/context/context_object.go b/context/context_object.go new file mode 100644 index 0000000..0bc8dde --- /dev/null +++ b/context/context_object.go @@ -0,0 +1,82 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package context + +import ( + // stdlib + "fmt" + + // local + "github.com/pztrn/urtrator/configuration" + "github.com/pztrn/urtrator/database" + "github.com/pztrn/urtrator/eventer" + "github.com/pztrn/urtrator/launcher" + "github.com/pztrn/urtrator/requester" + + // Github + "github.com/mattn/go-gtk/gtk" +) + +type Context struct { + // Configuration. + Cfg *configuration.Config + // Database. + Database *database.Database + // Eventer. + Eventer *eventer.Eventer + // Game launcher. + Launcher *launcher.Launcher + // Requester, which requests server's information. + Requester *requester.Requester +} + +func (ctx *Context) Close() { + fmt.Println("Closing URTrator...") + + ctx.Database.Close() + + // At last, close main window. + gtk.MainQuit() +} + +func (ctx *Context) initializeConfig() { + ctx.Cfg = configuration.New() + ctx.Cfg.Initialize() +} + +func (ctx *Context) initializeDatabase() { + ctx.Database = database.New(ctx.Cfg) + ctx.Database.Initialize() + ctx.Database.Migrate() +} + +func (ctx *Context) initializeEventer() { + ctx.Eventer = eventer.New() + ctx.Eventer.Initialize() +} + +func (ctx *Context) initializeLauncher() { + ctx.Launcher = launcher.New() + ctx.Launcher.Initialize() +} + +func (ctx *Context) initializeRequester() { + ctx.Requester = requester.New() + ctx.Requester.Initialize() +} + +func (ctx *Context) Initialize() { + fmt.Println("Initializing application context...") + ctx.initializeConfig() + ctx.initializeDatabase() + ctx.initializeEventer() + ctx.initializeLauncher() + ctx.initializeRequester() +} diff --git a/context/exported.go b/context/exported.go new file mode 100644 index 0000000..692f7e2 --- /dev/null +++ b/context/exported.go @@ -0,0 +1,15 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package context + +func New() *Context { + c := Context{} + return &c +} diff --git a/database/database_object.go b/database/database_object.go new file mode 100644 index 0000000..f06726c --- /dev/null +++ b/database/database_object.go @@ -0,0 +1,76 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package database + +import ( + // stdlib + //"database/sql" + "fmt" + "path" + "strconv" + + // local + "github.com/pztrn/urtrator/datamodels" + + // Other + "github.com/jmoiron/sqlx" + _ "github.com/mattn/go-sqlite3" +) + +type Database struct { + // Pointer to initialized database connection. + Db *sqlx.DB +} + +func (d *Database) Close() { + fmt.Println("Closing database...") + d.Db.Close() +} + +func (d *Database) Initialize() { + fmt.Println("Initializing database...") + + // Connect to database. + db_path := path.Join(cfg.TEMP["DATA"], "database.sqlite3") + fmt.Println("Database path: " + db_path) + db, err := sqlx.Connect("sqlite3", db_path) + if err != nil { + fmt.Println(err.Error()) + } + d.Db = db +} + +func (d *Database) Migrate() { + // Getting current database version. + dbver := 0 + database := []datamodels.Database{} + d.Db.Select(&database, "SELECT * FROM database") + if len(database) > 0 { + fmt.Println("Current database version: " + database[0].Version) + dbver, _ = strconv.Atoi(database[0].Version) + } else { + fmt.Println("No database found, will create new one") + } + + + migrate_full(d, dbver) +} + +func (d *Database) UpdateServers(data map[string]*datamodels.Server) { + fmt.Println("Updating servers information in database...") + // ToDo: real update :) + d.Db.MustExec("DELETE FROM servers") + tx := d.Db.MustBegin() + for _, srv := range data { + tx.NamedExec("INSERT INTO servers (ip, port, name, ping, players, maxplayers, gamemode, map, version) VALUES (:ip, :port, :name, :ping, :players, :maxplayers, :gamemode, :map, :version)", srv) + } + tx.Commit() + fmt.Println("Done") +} diff --git a/database/exported.go b/database/exported.go new file mode 100644 index 0000000..f0c91e8 --- /dev/null +++ b/database/exported.go @@ -0,0 +1,25 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package database + +import ( + // Local + "github.com/pztrn/urtrator/configuration" +) + +var ( + cfg *configuration.Config +) + +func New(c *configuration.Config) *Database { + cfg = c + d := Database{} + return &d +} diff --git a/database/migrations.go b/database/migrations.go new file mode 100644 index 0000000..5efb30b --- /dev/null +++ b/database/migrations.go @@ -0,0 +1,77 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package database + +import ( + // stdlib + "fmt" +) + +var start_schema = ` +DROP TABLE IF EXISTS database; +CREATE TABLE database ( + version VARCHAR(10) NOT NULL +); + +DROP TABLE IF EXISTS servers; +CREATE TABLE servers ( + ip VARCHAR(128) NOT NULL, + port VARCHAR(5) NOT NULL, + name VARCHAR(128) NOT NULL, + players VARCHAR(2) NOT NULL, + maxplayers VARCHAR(2) NOT NULL, + ping VARCHAR(4), + gamemode VARCHAR(1) NOT NULL, + map VARCHAR(64) NOT NULL, + version VARCHAR(5) NOT NULL +); + +INSERT INTO database (version) VALUES (1); +` + +// Migrate database to latest version. +// ToDo: make it more good :). +func migrate_full(db *Database, version int) { + if version < 1 { + start_to_one(db) + version = 1 + } + if version == 1 { + one_to_two(db) + version = 2 + } + if version == 2 { + two_to_three(db) + version = 3 + } +} + +// Initial database structure. +func start_to_one(db *Database) { + fmt.Println("Upgrading database from 0 to 1...") + db.Db.MustExec(start_schema) +} + +// Favorite server mark. +func one_to_two(db *Database) { + fmt.Println("Upgrading database from 1 to 2...") + db.Db.MustExec("ALTER TABLE servers ADD favorite VARCHAR(1) DEFAULT '0'") + db.Db.MustExec("UPDATE database SET version=2") +} + +// URTRator settings and Urban Terror profiles. +func two_to_three(db *Database) { + fmt.Println("Upgrading database from 2 to 3...") + db.Db.MustExec("DROP TABLE IF EXISTS settings") + db.Db.MustExec("CREATE TABLE settings (show_tray_icon VARCHAR(1) NOT NULL DEFAULT '0', enable_autoupdate VARCHAR(1) NOT NULL DEFAULT '0')") + db.Db.MustExec("DROP TABLE IF EXISTS urt_profiles") + db.Db.MustExec("CREATE TABLE urt_profiles (name VARCHAR(128) NOT NULL, version VARCHAR(5) NOT NULL DEFAULT '4.3', binary VARCHAR(1024) NOT NULL, second_x_session VARCHAR(1) NOT NULL DEFAULT '0', additional_parameters VARCHAR(1024) NOT NULL DEFAULT '')") + db.Db.MustExec("UPDATE database SET version=3") +} diff --git a/datamodels/database.go b/datamodels/database.go new file mode 100644 index 0000000..f8ee1a2 --- /dev/null +++ b/datamodels/database.go @@ -0,0 +1,14 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package datamodels + +type Database struct { + Version string `db:"version"` +} diff --git a/datamodels/profile.go b/datamodels/profile.go new file mode 100644 index 0000000..5aaa255 --- /dev/null +++ b/datamodels/profile.go @@ -0,0 +1,23 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package datamodels + +type Profile struct { + // Profile name. + Name string `db:"name"` + // Game version. + Version string `db:"version"` + // Binary path. + Binary string `db:"binary"` + // Will we use second X session? + Second_x_session string `db:"second_x_session"` + // Additional game parameters we will pass. + Additional_params string `db:"additional_parameters"` +} diff --git a/datamodels/server.go b/datamodels/server.go new file mode 100644 index 0000000..f12f423 --- /dev/null +++ b/datamodels/server.go @@ -0,0 +1,33 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package datamodels + +type Server struct { + // Server's address + Ip string `db:"ip"` + // Server's port + Port string `db:"port"` + // Server's name + Name string `db:"name"` + // Current players count + Players string `db:"players"` + // Maximum players + Maxplayers string `db:"maxplayers"` + // Ping + Ping string `db:"ping"` + // Gametype. See Urban Terror documentation on relationship. + Gamemode string `db:"gamemode"` + // Current map + Map string `db:"map"` + // Server's software version + Version string `db:"version"` + // Is server was favorited? + Favorite string `db:"favorite"` +} diff --git a/eventer/eventer_object.go b/eventer/eventer_object.go new file mode 100644 index 0000000..7542605 --- /dev/null +++ b/eventer/eventer_object.go @@ -0,0 +1,54 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package eventer + +import ( + crand "crypto/rand" + "errors" + "fmt" +) + +type Eventer struct { + // Events + events map[string]map[string]func() +} + +func (e *Eventer) AddEventHandler(event string, handler func()) { + _, ok := e.events[event] + if !ok { + e.events[event] = make(map[string]func()) + } + event_id_raw := make([]byte, 16) + crand.Read(event_id_raw) + event_id := fmt.Sprintf("%x", event_id_raw) + e.events[event][event_id] = handler +} + +func (e *Eventer) Initialize() { + e.initializeStorage() +} + +func (e *Eventer) initializeStorage() { + e.events = make(map[string]map[string]func()) +} + +func (e *Eventer) LaunchEvent(event string) error { + _, ok := e.events[event] + if !ok { + return errors.New("Event " + event + " not found!") + } + fmt.Println("Launching event " + event) + + for _, val := range e.events[event] { + val() + } + + return nil +} diff --git a/eventer/exported.go b/eventer/exported.go new file mode 100644 index 0000000..27cd73a --- /dev/null +++ b/eventer/exported.go @@ -0,0 +1,15 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package eventer + +func New() *Eventer { + e := Eventer{} + return &e +} diff --git a/launcher/exported.go b/launcher/exported.go new file mode 100644 index 0000000..50dd26a --- /dev/null +++ b/launcher/exported.go @@ -0,0 +1,15 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package launcher + +func New() *Launcher { + l := Launcher{} + return &l +} diff --git a/launcher/launcher_object.go b/launcher/launcher_object.go new file mode 100644 index 0000000..0917f6a --- /dev/null +++ b/launcher/launcher_object.go @@ -0,0 +1,51 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package launcher + +import ( + // stdlib + "fmt" + "os/exec" +) + +type Launcher struct {} + +func (l *Launcher) Initialize() { + fmt.Println("Initializing game launcher...") +} + +func (l *Launcher) Launch(binary string, params string, server string, password string, second_x bool, callback func()) { + // ToDo: only one instance of Urban Terror should be launched, so button + // should be disabled. + fmt.Println("Launching Urban Terror...") + + done := make(chan bool, 1) + // Create launch string. + var launch_string string = params + " +connect " + server + if len(password) > 0 { + launch_string += " +password " + password + } + fmt.Println("Final command: " + launch_string) + go func() { + go func() { + cmd := exec.Command(binary, launch_string) + err := cmd.Run() + if err != nil { + fmt.Println("Launch error: " + err.Error()) + } + done <- true + }() + + select { + case <- done: + callback() + } + }() +} diff --git a/requester/exported.go b/requester/exported.go new file mode 100644 index 0000000..1c2a64a --- /dev/null +++ b/requester/exported.go @@ -0,0 +1,21 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package requester + +import ( + // stdlib + "fmt" +) + +func New() *Requester { + fmt.Println("Creating Requester object...") + r := Requester{} + return &r +} diff --git a/requester/requester_object.go b/requester/requester_object.go new file mode 100644 index 0000000..b36faeb --- /dev/null +++ b/requester/requester_object.go @@ -0,0 +1,236 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package requester + +import ( + // stdlib + "bytes" + "fmt" + "net" + "strconv" + "strings" + "sync" + "time" + + // local + "github.com/pztrn/urtrator/datamodels" +) + +type Requester struct { + // Master server address + master_server string + // Master server port + master_server_port string + // Packet prefix. + pp string + // Showstopper for delimiting IP addresses. + // As we are receiving bytes, we will put bytes representation of "\" + // character. + ip_delimiter int +} + +// Requester's initialization. +func (r *Requester) Initialize() { + fmt.Println("Initializing Requester...") + r.master_server = "master.urbanterror.info" + r.master_server_port = "27900" + r.pp = "\377\377\377\377" + r.ip_delimiter = 92 +} + +// Gets all available servers from master server. +func (r *Requester) getServers(callback chan [][]string) { + // IP addresses we will compose to return. + var received_ips [][]string + conn, err1 := net.Dial("udp", r.master_server + ":" + r.master_server_port) + if err1 != nil { + fmt.Println("Error dialing to master server!") + } + defer conn.Close() + + // Set deadline, so we won't wait forever. + ddl := time.Now() + // This should be enough. Maybe, you should'n run URTrator on modem + // connections? :) + ddl = ddl.Add(time.Second * 2) + conn.SetDeadline(ddl) + fmt.Println("Master server connection deadline set: " + ddl.String()) + + // Request. + // 68 - protocol version for 4.3 + // 70 - protocol version for 4.2.x + msg := []byte(r.pp + "getservers 68 full empty") + conn.Write(msg) + + // UDP Buffer. + var received_buf []byte = make([]byte, 4096) + // Received buffer. + var raw_received []byte + // Received IP addresses. + //var received []string + fmt.Println("Receiving servers list...") + for { + _, err := conn.Read(received_buf) + if err != nil { + break + } + raw_received = append(raw_received, received_buf...) + fmt.Println("Received " + strconv.Itoa(len(raw_received)) + " bytes") + } + + // Obtaining list of slices. + var raw_received_slices [][]byte = bytes.Split(raw_received, []byte("\\")) + + // Every ip-port pair contains: + // 1. IP as first 4 bytes + // 2. Port as last 2 bytes. + // Every package is a 7-bytes sequence, which starts with "\" + // (code 92), which we used before to obtain list of slices. + for _, slice := range raw_received_slices { + // We need only 6-bytes slices. All other aren't represent + // server's address. + if len(slice) != 6 { + continue + } + // Generate IP. + ip := strconv.Itoa(int(slice[0])) + "." + strconv.Itoa(int(slice[1])) + "." + strconv.Itoa(int(slice[2])) + "." + strconv.Itoa(int(slice[3])) + // Generate port from last two bytes. + // This is a very shitty thing. Don't do this in real world. + // Maybe bitshifting will help here, but I can't get it to work :( + // Get first byte as integer and multiply it on 256 and summing with + // second byte. + p1 := int(slice[4]) * 256 + port := strconv.Itoa(p1 + int(slice[5])) + + // Create a slice with IP and port. + ip_and_port := []string{ip, port} + // Add it to received_ips. + received_ips = append(received_ips, ip_and_port) + } + + fmt.Println("Parsed " + strconv.Itoa(len(received_ips)) + " addresses") + callback <- received_ips +} + +// Updates information about all available servers from master server and +// parses it to usable format. +func (r *Requester) UpdateAllServers(done_chan chan map[string]*datamodels.Server, error_chan chan bool) { + fmt.Println("Starting all servers updating procedure...") + + callback := make(chan [][]string) + go r.getServers(callback) + + servers := make(map[string]*datamodels.Server) + + select { + case data := <- callback: + // Yay, we got data! :) + fmt.Println("Received " + strconv.Itoa(len(data)) + " servers") + servers = r.updateServerGoroutineDispatcher(data) + break + case <- time.After(time.Second * 10): + // Timeouted? Okay, push error back. + error_chan <- true + } + + done_chan <- servers +} + +func (r *Requester) updateServerGoroutineDispatcher(data [][]string) map[string]*datamodels.Server { + var wait sync.WaitGroup + var lock = sync.RWMutex{} + done_updating := 0 + servers := make(map[string]*datamodels.Server) + + for _, s := range data { + s := datamodels.Server{ + Ip: s[0], + Port: s[1], + } + go func(s *datamodels.Server, servers map[string]*datamodels.Server) { + wait.Add(1) + defer wait.Done() + r.UpdateServer(s) + done_updating = done_updating + 1 + lock.Lock() + servers[s.Ip + ":" + s.Port] = s + lock.Unlock() + }(&s, servers) + } + wait.Wait() + return servers +} + +// Updates information about specific server. +func (r *Requester) UpdateServer(server *datamodels.Server) { + srv := server.Ip + ":" + server.Port + fmt.Println("Updating server: " + srv) + + // Dial to server. + conn, err1 := net.Dial("udp", srv) + if err1 != nil { + fmt.Println("Error dialing to server " + srv + "!") + } + defer conn.Close() + + // Set deadline, so we won't wait forever. + ddl := time.Now() + // This should be enough. Maybe, you should'n run URTrator on modem + // connections? :) + ddl = ddl.Add(time.Second * 2) + conn.SetDeadline(ddl) + + msg := []byte(r.pp + "getinfo") + conn.Write(msg) + + // UDP Buffer. + var received_buf []byte = make([]byte, 4096) + // Received buffer. + var raw_received []byte + for { + _, err := conn.Read(received_buf) + if err != nil { + break + } + raw_received = append(raw_received, received_buf...) + } + + // First line is "infoResponse" string, which we should skip by + // splitting response by "\n". + received_lines := strings.Split(string(raw_received), "\n") + // We have server's data! + if len(received_lines) > 1 { + srv_config := strings.Split(received_lines[1], "\\") + // Parse server configuration into passed server's datamodel. + for i := 0; i < len(srv_config); i = i + 1 { + if srv_config[i] == "modversion" { + server.Version = srv_config[i + 1] + } + if srv_config[i] == "gametype" { + server.Gamemode = srv_config[i + 1] + } + if srv_config[i] == "sv_maxclients" { + server.Maxplayers = srv_config[i + 1] + } + if srv_config[i] == "clients" { + server.Players = srv_config[i + 1] + } + if srv_config[i] == "mapname" { + server.Map = srv_config[i + 1] + } + if srv_config[i] == "hostname" { + server.Name = srv_config[i + 1] + } + } + } + + // ToDo: Calculate ping. 0 for now. + server.Ping = "0" +} diff --git a/ui/about.go b/ui/about.go new file mode 100644 index 0000000..9dd94bd --- /dev/null +++ b/ui/about.go @@ -0,0 +1,716 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package ui + +import ( + "github.com/mattn/go-gtk/gtk" +) + +func ShowAboutDialog() { + ad := gtk.NewAboutDialog() + + ad.SetProgramName("URTrator") + ad.SetComments("Urban Terror servers browser and game launcher") + ad.SetVersion("0.1") + ad.SetWebsite("http://urtrator.pztrn.name") + ad.SetLogo(logo) + + // ToDo: put it in plain text files. + var authors []string + authors = append(authors, "Stanislav N. aka pztrn - project creator, main developer.") + ad.SetAuthors(authors) + + var artists []string + artists = append(artists, "UrTConnector team, for great icons and allowing to use them.") + ad.SetArtists(artists) + + ad.SetCopyright("Stanislav N. aka pztrn") + ad.SetLicense(GPL_LICENSE) + + ad.Run() + ad.Destroy() +} + +var GPL_LICENSE = ` + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 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 General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is 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. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + 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. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + 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 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. Use with the GNU Affero General Public License. + + 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 Affero 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 special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 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 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 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + 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 GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. +` diff --git a/ui/exported.go b/ui/exported.go new file mode 100644 index 0000000..85e995c --- /dev/null +++ b/ui/exported.go @@ -0,0 +1,30 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package ui + +import ( + // local + "github.com/pztrn/urtrator/context" + + // Other + "github.com/mattn/go-gtk/gdkpixbuf" +) + +var ( + ctx *context.Context + + logo *gdkpixbuf.Pixbuf +) + +func NewMainWindow(c *context.Context) *MainWindow { + ctx = c + m := MainWindow{} + return &m +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go new file mode 100644 index 0000000..7e4a47f --- /dev/null +++ b/ui/mainwindow.go @@ -0,0 +1,554 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package ui + +import ( + // stdlib + "encoding/base64" + "errors" + "fmt" + "strconv" + + // Local + "github.com/pztrn/urtrator/common" + "github.com/pztrn/urtrator/datamodels" + + // Other + "github.com/mattn/go-gtk/gdkpixbuf" + "github.com/mattn/go-gtk/glib" + "github.com/mattn/go-gtk/gtk" +) + +type MainWindow struct { + // Gamemodes. + gamemodes map[string]string + + // Widgets. + // The window itself. + window *gtk.Window + // Vertical Box. + vbox *gtk.VBox + // Main menu. + menubar *gtk.MenuBar + // Toolbar + toolbar *gtk.Toolbar + // Horizontal box for main window. + hpane *gtk.HPaned + // Tab widget. + tab_widget *gtk.Notebook + // Tabs list. + tabs map[string]*gtk.Frame + // All servers widget. + all_servers *gtk.TreeView + // Favorite servers widget. + fav_servers *gtk.TreeView + // Statusbar. + statusbar *gtk.Statusbar + // Statusbar context ID. + statusbar_context_id uint + // Profiles combobox. + profiles *gtk.ComboBoxText + // Checkbox for hiding/showing offline servers in 'Servers' tab list. + all_servers_hide_offline *gtk.CheckButton + // Checkbox for hiding/showing offline servers in 'Favorites' tab list. + fav_servers_hide_offline *gtk.CheckButton + // Game launch button. + launch_button *gtk.Button + + // Storages. + // All servers store. + all_servers_store *gtk.ListStore + // All servers sortable store. + all_servers_store_sortable *gtk.TreeSortable + // Favorites + fav_servers_store *gtk.ListStore + + // Dialogs. + options_dialog *OptionsDialog + + // Other + // Old profiles count. + old_profiles_count int +} + +func (m *MainWindow) addToFavorites() { + fmt.Println("Adding server to favorites...") +} + +func (m *MainWindow) Close() { + ctx.Close() +} + +func (m *MainWindow) hideOfflineAllServers() { + fmt.Println("(Un)Hiding offline servers in 'Servers' tab...") + ctx.Eventer.LaunchEvent("loadAllServers") +} + +func (m *MainWindow) hideOfflineFavoriteServers() { + fmt.Println("(Un)Hiding offline servers in 'Favorite' tab...") +} + +func (m *MainWindow) Initialize() { + m.initializeStorages() + + gtk.Init(nil) + m.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL) + m.window.SetTitle("URTrator") + m.window.Connect("destroy", m.Close) + m.vbox = gtk.NewVBox(false, 0) + m.hpane = gtk.NewHPaned() + + // Load program icon from base64. + icon_bytes, _ := base64.StdEncoding.DecodeString(common.Logo) + icon_pixbuf := gdkpixbuf.NewLoader() + icon_pixbuf.Write(icon_bytes) + logo = icon_pixbuf.GetPixbuf() + m.window.SetIcon(logo) + + // Default window size. + // ToDo: size and position restoring. + m.window.SetSizeRequest(1000, 600) + m.window.SetPosition(gtk.WIN_POS_CENTER) + + // Dialogs initialization. + m.options_dialog = &OptionsDialog{} + + // Main menu. + m.InitializeMainMenu() + + // Toolbar. + m.InitializeToolbar() + + // Tabs initialization. + m.InitializeTabs() + + // Sidebar initialization. + m.initializeSidebar() + + m.vbox.PackStart(m.hpane, true, true, 5) + + // Temporary hack. + var w, h int = 0, 0 + m.window.GetSize(&w, &h) + m.hpane.SetPosition(w) + + // Game profiles and launch button. + profile_and_launch_hbox := gtk.NewHBox(false, 0) + m.vbox.PackStart(profile_and_launch_hbox, false, true, 5) + + // Separator + sep := gtk.NewHSeparator() + profile_and_launch_hbox.PackStart(sep, true, true, 5) + + // Profile selection. + profiles_label := gtk.NewLabel("Game profile:") + m.profiles = gtk.NewComboBoxText() + + profile_and_launch_hbox.PackStart(profiles_label, false, true, 5) + profile_and_launch_hbox.PackStart(m.profiles, false, true, 5) + + ctx.Eventer.AddEventHandler("loadProfiles", m.loadProfiles) + + // One more separator. + sepp := gtk.NewVSeparator() + profile_and_launch_hbox.PackStart(sepp, false, true, 5) + + // Game launching button. + m.launch_button = gtk.NewButtonWithLabel("Launch!") + m.launch_button.Clicked(m.launchGame) + profile_and_launch_hbox.PackStart(m.launch_button, false, true, 5) + + // Statusbar. + m.statusbar = gtk.NewStatusbar() + m.vbox.PackStart(m.statusbar, false, true, 0) + + m.statusbar_context_id = m.statusbar.GetContextId("Status Bar") + m.statusbar.Push(m.statusbar_context_id, "URTrator is ready") + + m.window.Add(m.vbox) + m.window.ShowAll() + + // Launch events. + ctx.Eventer.LaunchEvent("loadProfiles") + ctx.Eventer.LaunchEvent("loadAllServers") + + gtk.Main() +} + +func (m *MainWindow) InitializeMainMenu() { + m.menubar = gtk.NewMenuBar() + m.vbox.PackStart(m.menubar, false, false, 0) + + // File menu. + fm := gtk.NewMenuItemWithMnemonic("File") + m.menubar.Append(fm) + file_menu := gtk.NewMenu() + fm.SetSubmenu(file_menu) + + // Options. + options_menu_item := gtk.NewMenuItemWithMnemonic("_Options") + file_menu.Append(options_menu_item) + options_menu_item.Connect("activate", m.options_dialog.ShowOptionsDialog) + + // Exit. + exit_menu_item := gtk.NewMenuItemWithMnemonic("E_xit") + file_menu.Append(exit_menu_item) + exit_menu_item.Connect("activate", m.Close) + + // About menu. + am := gtk.NewMenuItemWithMnemonic("_About") + m.menubar.Append(am) + about_menu := gtk.NewMenu() + am.SetSubmenu(about_menu) + + // About app item. + about_app_item := gtk.NewMenuItemWithMnemonic("About _URTrator...") + about_menu.Append(about_app_item) + about_app_item.Connect("activate", ShowAboutDialog) +} + +func (m *MainWindow) initializeSidebar() { + sidebar_vbox := gtk.NewVBox(false, 0) + + m.hpane.Add2(sidebar_vbox) +} + +// Initializes internal storages. +func (m *MainWindow) initializeStorages() { + // Gamemodes. + m.gamemodes = make(map[string]string) + m.gamemodes = map[string]string{ + "1": "Last Man Standing", + "2": "Free For All", + "3": "Team DM", + "4": "Team Survivor", + "5": "Follow The Leader", + "6": "Cap'n'Hold", + "7": "Capture The Flag", + "8": "Bomb", + "9": "Jump", + "10": "Freeze Tag", + "11": "Gun Game", + "12": "Instagib", + } + + // Frames storage. + m.tabs = make(map[string]*gtk.Frame) + m.tabs["dummy"] = gtk.NewFrame("dummy") + delete(m.tabs, "dummy") + + // Servers tab list view storage. + // Structure: + // Server status icon|Server name|Mode|Map|Players|Ping|Version + m.all_servers_store = gtk.NewListStore(gdkpixbuf.GetType(), glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING) + m.all_servers_store_sortable = gtk.NewTreeSortable(m.all_servers_store) + + // Same as above, but for favorite servers. + m.fav_servers_store = gtk.NewListStore(gdkpixbuf.GetType(), glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_STRING) + + // Profiles count after filling combobox. Defaulting to 0. + m.old_profiles_count = 0 +} + +func (m *MainWindow) InitializeTabs() { + // Create tabs widget. + m.tab_widget = gtk.NewNotebook() + + tab_allsrv_hbox := gtk.NewHBox(false, 0) + swin1 := gtk.NewScrolledWindow(nil, nil) + + m.all_servers = gtk.NewTreeView() + swin1.Add(m.all_servers) + tab_allsrv_hbox.PackStart(swin1, true, true, 5) + m.tab_widget.AppendPage(tab_allsrv_hbox, gtk.NewLabel("Servers")) + + m.all_servers.SetModel(m.all_servers_store) + m.all_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Status", gtk.NewCellRendererPixbuf(), "pixbuf", 0)) + + all_srv_name_column := gtk.NewTreeViewColumnWithAttributes("Name", gtk.NewCellRendererText(), "text", 1) + all_srv_name_column.SetSortColumnId(1) + m.all_servers.AppendColumn(all_srv_name_column) + + all_gamemode_column := gtk.NewTreeViewColumnWithAttributes("Mode", gtk.NewCellRendererText(), "text", 2) + all_gamemode_column.SetSortColumnId(2) + m.all_servers.AppendColumn(all_gamemode_column) + + all_map_column := gtk.NewTreeViewColumnWithAttributes("Map", gtk.NewCellRendererText(), "text", 3) + all_map_column.SetSortColumnId(3) + m.all_servers.AppendColumn(all_map_column) + + // ToDo: custom sorting function. + all_players_column := gtk.NewTreeViewColumnWithAttributes("Players", gtk.NewCellRendererText(), "text", 4) + all_players_column.SetSortColumnId(4) + m.all_servers.AppendColumn(all_players_column) + + all_ping_column := gtk.NewTreeViewColumnWithAttributes("Ping", gtk.NewCellRendererText(), "text", 5) + all_ping_column.SetSortColumnId(5) + m.all_servers.AppendColumn(all_ping_column) + + all_version_column := gtk.NewTreeViewColumnWithAttributes("Version", gtk.NewCellRendererText(), "text", 6) + all_version_column.SetSortColumnId(6) + m.all_servers.AppendColumn(all_version_column) + + all_ip_column := gtk.NewTreeViewColumnWithAttributes("IP", gtk.NewCellRendererText(), "text", 7) + all_ip_column.SetSortColumnId(7) + m.all_servers.AppendColumn(all_ip_column) + // Sorting. + // By default we are sorting by server name. + // ToDo: remembering it to configuration storage. + m.all_servers_store_sortable.SetSortColumnId(1, gtk.SORT_ASCENDING) + + // VBox for some servers list controllers. + tab_all_srv_ctl_vbox := gtk.NewVBox(false, 0) + tab_allsrv_hbox.PackStart(tab_all_srv_ctl_vbox, false, true, 5) + + // Checkbox for hiding offline servers. + m.all_servers_hide_offline = gtk.NewCheckButtonWithLabel("Hide offline servers") + tab_all_srv_ctl_vbox.PackStart(m.all_servers_hide_offline, false, true, 5) + m.all_servers_hide_offline.Clicked(m.hideOfflineAllServers) + + // Final separator. + ctl_sep := gtk.NewVSeparator() + tab_all_srv_ctl_vbox.PackStart(ctl_sep, true, true, 5) + + // Favorites servers + // ToDo: sorting as in all servers list. + tab_fav_srv_hbox := gtk.NewHBox(false, 0) + m.fav_servers = gtk.NewTreeView() + swin2 := gtk.NewScrolledWindow(nil, nil) + swin2.Add(m.fav_servers) + tab_fav_srv_hbox.PackStart(swin2, true, true, 5) + m.tab_widget.AppendPage(tab_fav_srv_hbox, gtk.NewLabel("Favorites")) + m.fav_servers.SetModel(m.fav_servers_store) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Status", gtk.NewCellRendererPixbuf(), "pixbuf", 0)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Name", gtk.NewCellRendererText(), "text", 1)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Mode", gtk.NewCellRendererText(), "text", 2)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Map", gtk.NewCellRendererText(), "text", 3)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Players", gtk.NewCellRendererText(), "text", 4)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Ping", gtk.NewCellRendererText(), "text", 5)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Version", gtk.NewCellRendererText(), "text", 6)) + m.fav_servers.AppendColumn(gtk.NewTreeViewColumnWithAttributes("IP", gtk.NewCellRendererText(), "text", 7)) + m.fav_servers.SetHeadersClickable(true) + + // VBox for some servers list controllers. + tab_fav_srv_ctl_vbox := gtk.NewVBox(false, 0) + tab_fav_srv_hbox.PackStart(tab_fav_srv_ctl_vbox, false, true, 5) + + // Checkbox for hiding offline servers. + m.fav_servers_hide_offline = gtk.NewCheckButtonWithLabel("Hide offline servers") + tab_fav_srv_ctl_vbox.PackStart(m.fav_servers_hide_offline, false, true, 5) + m.fav_servers_hide_offline.Clicked(m.hideOfflineFavoriteServers) + + // Final separator. + ctl_fav_sep := gtk.NewVSeparator() + tab_fav_srv_ctl_vbox.PackStart(ctl_fav_sep, true, true, 5) + + // Add tab_widget widget to window. + m.hpane.Add1(m.tab_widget) + + ctx.Eventer.AddEventHandler("loadAllServers", m.loadServers) +} + +func (m *MainWindow) InitializeToolbar() { + m.toolbar = gtk.NewToolbar() + m.vbox.PackStart(m.toolbar, false, false, 5) + + // Update servers button. + button_update_all_servers := gtk.NewToolButtonFromStock(gtk.STOCK_REFRESH) + button_update_all_servers.SetLabel("Update all servers") + button_update_all_servers.OnClicked(m.UpdateServers) + m.toolbar.Insert(button_update_all_servers, 0) + + // Separator. + separator := gtk.NewSeparatorToolItem() + m.toolbar.Insert(separator, 1) + + // Add server to favorites button. + fav_button := gtk.NewToolButtonFromStock(gtk.STOCK_ADD) + fav_button.SetLabel("Add to favorites") + fav_button.OnClicked(m.addToFavorites) + m.toolbar.Insert(fav_button, 2) +} + +func (m *MainWindow) launchGame() error { + fmt.Println("Launching Urban Terror...") + + //var launch_params string = "" + + // Getting server's name from list. + // ToDo: detect on what tab we are and use approriate list. + sel := m.all_servers.GetSelection() + model := m.all_servers.GetModel() + iter := new(gtk.TreeIter) + _ = sel.GetSelected(iter) + + // Getting server name. + var srv_name string + srv_name_gval := glib.ValueFromNative(srv_name) + model.GetValue(iter, 1, srv_name_gval) + server_name := srv_name_gval.GetString() + + // Getting server address. + var srv_addr string + srv_address_gval := glib.ValueFromNative(srv_addr) + model.GetValue(iter, 7, srv_address_gval) + srv_address := srv_address_gval.GetString() + + // Getting server's game version. + var srv_game_ver_raw string + srv_game_ver_gval := glib.ValueFromNative(srv_game_ver_raw) + model.GetValue(iter, 6, srv_game_ver_gval) + srv_game_ver := srv_game_ver_gval.GetString() + + + // Check for proper server name. If length == 0: server is offline, + // we should show notification to user. + if len(server_name) == 0 { + var will_continue bool = false + mbox_string := "Selected server is offline.\n\nWould you still want to launch Urban Terror?\nIt will just launch a game, without connecting to\nany server." + m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, mbox_string) + m.Connect("response", func(resp *glib.CallbackContext) { + if resp.Args(0) == 4294967287 { + will_continue = false + } else { + will_continue = true + } + }) + m.Response(func() { + m.Destroy() + }) + m.Run() + if !will_continue { + return errors.New("User declined to connect to offline server") + } + } + + // Getting selected profile's name. + profile_name := m.profiles.GetActiveText() + // Checking profile name length. If 0 - then stop executing :) + if len(profile_name) == 0 { + mbox_string := "Invalid game profile selected.\n\nPlease, select profile and retry." + m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + return errors.New("User didn't select valid profile.") + } + + fmt.Println("Connecting to " + server_name + " (" + srv_address + ") using profile " + profile_name + "...") + + // Getting profile data from database. + // ToDo: cache profiles data in runtime. + profile := []datamodels.Profile{} + err := ctx.Database.Db.Select(&profile, ctx.Database.Db.Rebind("SELECT * FROM urt_profiles WHERE name=?"), profile_name) + if err != nil { + fmt.Println(err.Error()) + } + + // Check if profile version is valid for selected game server. + if profile[0].Version != srv_game_ver { + mbox_string := "Invalid game profile selected.\n\nSelected profile have different game version than server.\nPlease, select valid profile and retry." + m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + return errors.New("User didn't select valid profile, mismatch with server's version.") + } + + // Hey, we're ok here! :) Launch Urban Terror! + m.statusbar.Push(m.statusbar_context_id, "Launching Urban Terror...") + m.launch_button.SetSensitive(false) + // ToDo: handling server passwords. + ctx.Launcher.Launch(profile[0].Binary, profile[0].Additional_params, srv_address, "", m.unlockInterface) + + return nil +} + +func (m *MainWindow) loadProfiles() { + fmt.Println("Loading profiles into combobox on MainWindow") + for i := 0; i < m.old_profiles_count; i++ { + // ComboBox indexes are shifting on element deletion, so we should + // detele very first element every time. + m.profiles.Remove(0) + } + + profiles := []datamodels.Profile{} + err := ctx.Database.Db.Select(&profiles, "SELECT * FROM urt_profiles") + if err != nil { + fmt.Println(err.Error()) + } + for p := range profiles { + m.profiles.AppendText(profiles[p].Name) + } + + m.old_profiles_count = len(profiles) + fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles") +} + +func (m *MainWindow) loadServers() { + fmt.Println("Loading servers lists into widgets...") + servers := []datamodels.Server{} + err := ctx.Database.Db.Select(&servers, "SELECT * FROM servers") + if err != nil { + fmt.Println(err.Error()) + } + // ToDo: do it without clearing. + m.all_servers_store.Clear() + for _, srv := range servers { + if m.all_servers_hide_offline.GetActive() && srv.Name == "" && srv.Players == "" { + continue + } + var iter gtk.TreeIter + m.all_servers_store.Append(&iter) + if srv.Name == "" && srv.Players == "" { + m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf) + } else { + m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf) + } + m.all_servers_store.Set(&iter, 1, srv.Name) + m.all_servers_store.Set(&iter, 2, m.gamemodes[srv.Gamemode]) + m.all_servers_store.Set(&iter, 3, srv.Map) + m.all_servers_store.Set(&iter, 4, srv.Players + "/" + srv.Maxplayers) + m.all_servers_store.Set(&iter, 5, srv.Ping) + m.all_servers_store.Set(&iter, 6, srv.Version) + m.all_servers_store.Set(&iter, 7, srv.Ip + ":" + srv.Port) + } +} + +func (m *MainWindow) unlockInterface() { + m.launch_button.SetSensitive(true) + m.statusbar.Push(m.statusbar_context_id, "URTrator is ready.") +} + +func (m *MainWindow) UpdateServers() { + m.statusbar.Push(m.statusbar_context_id, "Updating servers...") + current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage())) + fmt.Println("Updating servers on tab '" + current_tab + "'...") + done_chan := make(chan map[string]*datamodels.Server, 1) + error_chan := make(chan bool, 1) + if current_tab == "Servers" { + go ctx.Requester.UpdateAllServers(done_chan, error_chan) + } else if current_tab == "Favorites" { + fmt.Println("Favorites update stub") + } + + select { + case data := <- done_chan: + fmt.Println("Information about servers successfully gathered") + ctx.Database.UpdateServers(data) + ctx.Eventer.LaunchEvent("loadAllServers") + case <- error_chan: + fmt.Println("Error occured") + } + + m.statusbar.Push(m.statusbar_context_id, "Servers updated.") +} diff --git a/ui/options.go b/ui/options.go new file mode 100644 index 0000000..beeac97 --- /dev/null +++ b/ui/options.go @@ -0,0 +1,226 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package ui + +import ( + // stdlib + "fmt" + + // Local + "github.com/pztrn/urtrator/datamodels" + + // Other + "github.com/mattn/go-gtk/gtk" + "github.com/mattn/go-gtk/glib" +) + +type OptionsDialog struct { + // Window. + window *gtk.Window + // Options main VBox. + vbox *gtk.VBox + // Tabs widget. + tab_widget *gtk.Notebook + + // Widgets. + // General tab. + // Show tray icon checkbutton. + show_tray_icon *gtk.CheckButton + // Enable autoupdate checkbutton. + autoupdate *gtk.CheckButton + // Urban Terror tab. + // Profiles list. + profiles_list *gtk.TreeView + + // Data stores. + // Urban Terror profiles list. + profiles_list_store *gtk.ListStore +} + +func (o *OptionsDialog) addProfile() { + fmt.Println("Adding profile...") + + op := OptionsProfile{} + op.Initialize(false, o.loadProfiles) + ctx.Eventer.LaunchEvent("loadProfiles") +} + +func (o *OptionsDialog) closeOptionsDialogByCancel() { + o.window.Destroy() +} + +func (o *OptionsDialog) closeOptionsDialogWithDiscard() { +} + +func (o *OptionsDialog) closeOptionsDialogWithSaving() { + fmt.Println("Saving changes to options...") + + ctx.Eventer.LaunchEvent("loadProfiles") + + o.window.Destroy() +} + +func (o *OptionsDialog) deleteProfile() { + // Oh... dat... GTK... + sel := o.profiles_list.GetSelection() + model := o.profiles_list.GetModel() + iter := new(gtk.TreeIter) + _ = sel.GetSelected(iter) + var p string + gval := glib.ValueFromNative(p) + model.GetValue(iter, 0, gval) + profile_name := gval.GetString() + + if len(profile_name) > 0 { + fmt.Println("Deleting profile '" + profile_name + "'") + + profile := datamodels.Profile{} + profile.Name = profile_name + ctx.Database.Db.NamedExec("DELETE FROM urt_profiles WHERE name=:name", &profile) + ctx.Eventer.LaunchEvent("loadProfiles") + } +} + +func (o *OptionsDialog) editProfile() { + // Oh... dat... GTK... + sel := o.profiles_list.GetSelection() + model := o.profiles_list.GetModel() + iter := new(gtk.TreeIter) + _ = sel.GetSelected(iter) + var p string + gval := glib.ValueFromNative(p) + model.GetValue(iter, 0, gval) + profile_name := gval.GetString() + + if len(profile_name) > 0 { + op := OptionsProfile{} + op.InitializeUpdate(profile_name, o.loadProfiles) + ctx.Eventer.LaunchEvent("loadProfiles") + } +} + +func (o *OptionsDialog) initializeGeneralTab() { + general_vbox := gtk.NewVBox(false, 0) + + // Tray icon checkbox. + o.show_tray_icon = gtk.NewCheckButtonWithLabel("Show tray icon?") + general_vbox.PackStart(o.show_tray_icon, false, true, 5) + + // Autoupdate checkbox. + o.autoupdate = gtk.NewCheckButtonWithLabel("Automatically update URTrator?") + general_vbox.PackStart(o.autoupdate, false, true, 5) + + o.tab_widget.AppendPage(general_vbox, gtk.NewLabel("General")) +} + +func (o *OptionsDialog) initializeStorages() { + // Structure: + // Name|Version|Second X session + o.profiles_list_store = gtk.NewListStore(glib.G_TYPE_STRING, glib.G_TYPE_STRING, glib.G_TYPE_BOOL) +} + +func (o *OptionsDialog) initializeTabs() { + o.initializeStorages() + o.tab_widget = gtk.NewNotebook() + + o.initializeGeneralTab() + o.initializeUrtTab() + + // Buttons for saving and discarding changes. + buttons_hbox := gtk.NewHBox(false, 0) + sep := gtk.NewHSeparator() + + cancel_button := gtk.NewButtonWithLabel("Cancel") + cancel_button.Clicked(o.closeOptionsDialogByCancel) + + ok_button := gtk.NewButtonWithLabel("OK") + ok_button.Clicked(o.closeOptionsDialogWithSaving) + + buttons_hbox.PackStart(sep, true, true, 5) + buttons_hbox.PackStart(cancel_button, false, true, 5) + buttons_hbox.PackStart(ok_button, false, true, 5) + + o.vbox.PackStart(o.tab_widget, true, true, 5) + o.vbox.PackStart(buttons_hbox, false, true, 5) +} + +func (o *OptionsDialog) initializeUrtTab() { + urt_hbox := gtk.NewHBox(false, 0) + + // Profiles list. + o.profiles_list = gtk.NewTreeView() + urt_hbox.Add(o.profiles_list) + o.profiles_list.SetModel(o.profiles_list_store) + o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Profile name", gtk.NewCellRendererText(), "text", 0)) + o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Urban Terror version", gtk.NewCellRendererText(), "text", 1)) + crt := gtk.NewCellRendererToggle() + o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Second X session", crt, "bool", 2)) + + // Profiles list buttons. + urt_profiles_buttons_vbox := gtk.NewVBox(false, 0) + + button_add := gtk.NewButtonWithLabel("Add") + button_add.Clicked(o.addProfile) + urt_profiles_buttons_vbox.PackStart(button_add, false, true, 5) + + button_edit := gtk.NewButtonWithLabel("Edit") + button_edit.Clicked(o.editProfile) + urt_profiles_buttons_vbox.PackStart(button_edit, false, true, 5) + + // Spacer for profiles list buttons. + sep := gtk.NewVSeparator() + urt_profiles_buttons_vbox.PackStart(sep, true, true, 5) + + button_delete := gtk.NewButtonWithLabel("Delete") + button_delete.Clicked(o.deleteProfile) + urt_profiles_buttons_vbox.PackStart(button_delete, false, true, 5) + + urt_hbox.Add(urt_profiles_buttons_vbox) + + o.tab_widget.AppendPage(urt_hbox, gtk.NewLabel("Urban Terror")) + + // Load Profiles. + o.loadProfiles() +} + +func (o *OptionsDialog) loadProfiles() { + fmt.Println("Loading profiles...") + o.profiles_list_store.Clear() + // Get profiles from database. + profiles := []datamodels.Profile{} + err := ctx.Database.Db.Select(&profiles, "SELECT * FROM urt_profiles") + if err != nil { + fmt.Println(err.Error()) + } + + for p := range profiles { + var iter gtk.TreeIter + o.profiles_list_store.Append(&iter) + o.profiles_list_store.Set(&iter, 0, profiles[p].Name) + o.profiles_list_store.Set(&iter, 1, profiles[p].Version) + } +} + +func (o *OptionsDialog) ShowOptionsDialog() { + o.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL) + o.window.SetTitle("URTrator - Options") + o.window.Connect("destroy", o.closeOptionsDialogWithDiscard) + o.window.SetModal(true) + o.window.SetSizeRequest(550, 400) + o.window.SetPosition(gtk.WIN_POS_CENTER) + o.window.SetIcon(logo) + + o.vbox = gtk.NewVBox(false, 0) + + o.initializeTabs() + + o.window.Add(o.vbox) + o.window.ShowAll() +} diff --git a/ui/options_profile.go b/ui/options_profile.go new file mode 100644 index 0000000..3d7267a --- /dev/null +++ b/ui/options_profile.go @@ -0,0 +1,305 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package ui + +import ( + // stdlib + "fmt" + "os" + "runtime" + "strings" + + // Local + "github.com/pztrn/urtrator/datamodels" + + // Other + "github.com/mattn/go-gtk/gtk" + //"github.com/mattn/go-gtk/glib" +) + +type OptionsProfile struct { + // Window. + window *gtk.Window + // Main Vertical Box. + vbox *gtk.VBox + // Profile name. + profile_name *gtk.Entry + // Binary path. + binary_path *gtk.Entry + // Urban Terror versions combobox + urt_version_combo *gtk.ComboBoxText + // Another X session? + another_x_session *gtk.CheckButton + // Additional parameters for game launching. + additional_parameters *gtk.Entry + + // File chooser dialog for selecting binary. + f *gtk.FileChooserDialog + + // Flags. + // This is profile update? + update bool + + // Callbacks. + // This will be triggered after we change profile. + loadProfiles func() + + // Others. + // Old profile, needed for proper update. + old_profile *datamodels.Profile +} + +func (op *OptionsProfile) browseForBinary() { + op.f = gtk.NewFileChooserDialog("URTrator - Select Urban Terro binary", op.window, gtk.FILE_CHOOSER_ACTION_OPEN, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) + op.f.Response(op.browseForBinaryHelper) + op.f.Run() +} + +func (op *OptionsProfile) browseForBinaryHelper() { + filename := op.f.GetFilename() + op.binary_path.SetText(filename) + op.f.Destroy() + + // Check for valid filename. + // ToDo: add more OSes. + if runtime.GOOS == "linux" { + // Filename should end with approriate arch. + if runtime.GOARCH == "amd64" { + if len(filename) > 0 && strings.Split(filename, ".")[1] != "x86_64" && strings.Split(filename, ".")[0] != "Quake3-UrT" { + fmt.Println("Invalid binary selected!") + mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.x86_64." + m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + op.binary_path.SetText("") + } + } + } +} + +func (op *OptionsProfile) closeByCancel() { + op.window.Destroy() +} + +func (op *OptionsProfile) closeWithDiscard() { +} + +func (op *OptionsProfile) Initialize(update bool, lp func()) { + if update { + op.update = true + } + + op.loadProfiles = lp + + op.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL) + if update { + op.window.SetTitle("URTrator - Update Urban Terror profile") + } else { + op.window.SetTitle("URTrator - Add Urban Terror profile") + } + op.window.Connect("destroy", op.closeWithDiscard) + op.window.SetModal(true) + op.window.SetSizeRequest(550, 400) + op.window.SetPosition(gtk.WIN_POS_CENTER) + op.window.SetIcon(logo) + + op.vbox = gtk.NewVBox(false, 0) + + + // Profile name. + pn_hbox := gtk.NewHBox(false, 0) + pn_label := gtk.NewLabel("Profile name:") + sep1 := gtk.NewHSeparator() + op.profile_name = gtk.NewEntry() + pn_hbox.PackStart(pn_label, false, true, 5) + pn_hbox.PackStart(sep1, true, true, 5) + pn_hbox.PackStart(op.profile_name, true, true, 5) + op.vbox.PackStart(pn_hbox, false, true, 5) + + // Urban Terror version. + urt_version_hbox := gtk.NewHBox(false, 0) + urt_version_label := gtk.NewLabel("Urban Terror version:") + urt_version_sep := gtk.NewHSeparator() + op.urt_version_combo = gtk.NewComboBoxText() + op.urt_version_combo.AppendText("4.2.023") + op.urt_version_combo.AppendText("4.3.000") + op.urt_version_combo.SetActive(1) + urt_version_hbox.PackStart(urt_version_label, false, true, 5) + urt_version_hbox.PackStart(urt_version_sep, true, true, 5) + urt_version_hbox.PackStart(op.urt_version_combo, true, true, 5) + op.vbox.PackStart(urt_version_hbox, false, true, 5) + + // Urban Terror binary path. + binpath_hbox := gtk.NewHBox(false, 0) + binpath_label := gtk.NewLabel("Urban Terror binary:") + sep2 := gtk.NewHSeparator() + op.binary_path = gtk.NewEntry() + button_select_binary := gtk.NewButtonWithLabel("Browse") + button_select_binary.Clicked(op.browseForBinary) + binpath_hbox.PackStart(binpath_label, false, true, 5) + binpath_hbox.PackStart(sep2, true, true, 5) + binpath_hbox.PackStart(op.binary_path, true, true, 5) + binpath_hbox.PackStart(button_select_binary, false, true, 5) + op.vbox.PackStart(binpath_hbox, false, true, 5) + + // Should we use additional X session? + op.another_x_session = gtk.NewCheckButtonWithLabel("Start Urban Terror in another X session?") + op.vbox.PackStart(op.another_x_session, false, true, 5) + + // Additional game parameters. + params_hbox := gtk.NewHBox(false, 0) + params_label := gtk.NewLabel("Additional parameters:") + params_sep := gtk.NewHSeparator() + op.additional_parameters = gtk.NewEntry() + params_hbox.PackStart(params_label, false, true, 5) + params_hbox.PackStart(params_sep, true, true, 5) + params_hbox.PackStart(op.additional_parameters, true, true, 5) + op.vbox.PackStart(params_hbox, false, true, 5) + + // Vertical separator. + vert_sep := gtk.NewVSeparator() + op.vbox.PackStart(vert_sep, true, true, 5) + + // The buttons. + buttons_box := gtk.NewHBox(false, 0) + buttons_sep := gtk.NewHSeparator() + + cancel_button := gtk.NewButtonWithLabel("Cancel") + cancel_button.Clicked(op.closeByCancel) + buttons_box.PackStart(cancel_button, false, true, 5) + + buttons_box.PackStart(buttons_sep, true, true, 5) + + add_button := gtk.NewButton() + if op.update { + add_button.SetLabel("Update") + } else { + add_button.SetLabel("Add") + } + add_button.Clicked(op.saveProfile) + buttons_box.PackStart(add_button, false, true, 5) + + op.vbox.PackStart(buttons_box, false, true, 5) + + op.window.Add(op.vbox) + op.window.ShowAll() +} + +func (op *OptionsProfile) InitializeUpdate(profile_name string, lp func()) { + fmt.Println("Updating profile '" + profile_name + "'") + op.Initialize(true, lp) + + // Get profile data. + profile := []datamodels.Profile{} + err := ctx.Database.Db.Select(&profile, ctx.Database.Db.Rebind("SELECT * FROM urt_profiles WHERE name=?"), profile_name) + if err != nil { + fmt.Println(err.Error()) + } + + op.profile_name.SetText(profile[0].Name) + op.binary_path.SetText(profile[0].Binary) + op.additional_parameters.SetText(profile[0].Additional_params) + if profile[0].Second_x_session == "1" { + op.another_x_session.SetActive(true) + } + + if profile[0].Version == "4.3.000" { + op.urt_version_combo.SetActive(1) + } else { + op.urt_version_combo.SetActive(0) + } + + op.old_profile = &profile[0] + +} + +func (op *OptionsProfile) saveProfile() { + fmt.Println("Saving profile...") + + // Validating fields. + // Profile name must not be empty. + if len(op.profile_name.GetText()) < 1 { + mbox_string := "Empty profile name!\nProfile must be named somehow." + m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + } + // Binary path must also be filled. + if len(op.binary_path.GetText()) < 1 { + mbox_string := "Empty path to binary!\nThis profile will be unusable if you\nwill not provide path to binary!" + m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + } + // ...and must be executable! :) + filestat, err := os.Stat(op.binary_path.GetText()) + if err != nil { + mbox_string := "Invalid path to binary!\n\nError was:\n" + err.Error() + "\n\nCheck binary path and try again." + m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + } else { + // ToDo: executable flag checking. + fmt.Println(filestat.Mode()) + + // If we here - we can try to save game profile :). + profile := datamodels.Profile{ + Name: op.profile_name.GetText(), + Version: op.urt_version_combo.GetActiveText(), + Binary: op.binary_path.GetText(), + Additional_params: op.additional_parameters.GetText(), + } + + if op.another_x_session.GetActive() { + profile.Second_x_session = "1" + } else { + profile.Second_x_session = "0" + } + + // Check if we already have profile with such name. + profiles := []datamodels.Profile{} + err1 := ctx.Database.Db.Select(&profiles, "SELECT * FROM urt_profiles") + if err1 != nil { + fmt.Println(err1.Error()) + } + + var found bool = false + for p := range profiles { + if profiles[p].Name == profile.Name { + found = true + } + } + + if found { + mbox_string := "Game profile with same name already exist.\nRename profile for saving." + m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) + m.Response(func() { + m.Destroy() + }) + m.Run() + } else { + if op.update { + ctx.Database.Db.NamedExec("UPDATE urt_profiles SET name=:name, version=:version, binary=:binary, second_x_session=:second_x_session, additional_parameters=:additional_parameters WHERE name='" + op.old_profile.Name + "'", &profile) + } else { + ctx.Database.Db.NamedExec("INSERT INTO urt_profiles (name, version, binary, second_x_session, additional_parameters) VALUES (:name, :version, :binary, :second_x_session, :additional_parameters)", &profile) + } + } + } + op.loadProfiles() + op.window.Destroy() +} diff --git a/urtrator.go b/urtrator.go new file mode 100644 index 0000000..61a79dc --- /dev/null +++ b/urtrator.go @@ -0,0 +1,29 @@ +// URTator - Urban Terror server browser and game launcher, written in +// Go. +// +// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n) +// All rights reserved. +// +// Licensed under Terms and Conditions of GNU General Public License +// version 3 or any higher. +// ToDo: put full text of license here. +package main + +import ( + // local + "github.com/pztrn/urtrator/context" + "github.com/pztrn/urtrator/ui" + + // stdlib + "fmt" +) + +func main() { + fmt.Println("This is URTrator, version 0.1") + + ctx := context.New() + ctx.Initialize() + + ui := ui.NewMainWindow(ctx) + ui.Initialize() +}